forked from molecule-ai/molecule-core
Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1) with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo. Brand: Starfire → Molecule AI. Slug: starfire / agent-molecule → molecule. Env vars: STARFIRE_* → MOLECULE_*. Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform. Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent. DB: agentmolecule → molecule. History truncated; see public repo for prior commits and contributor attribution. Verified green: go test -race ./... (platform), pytest (workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
52 lines
2.1 KiB
Python
52 lines
2.1 KiB
Python
"""Helpers for the workspace's one-shot initial_prompt.
|
|
|
|
Kept as a standalone module (no heavy imports like uvicorn) so the marker
|
|
logic is unit-testable without standing up the full workspace runtime.
|
|
|
|
Background: the workspace runtime supports an `initial_prompt` that runs once
|
|
on first boot (clone the repo, set git hooks, read CLAUDE.md, commit_memory).
|
|
A marker file `.initial_prompt_done` prevents the prompt from re-running on
|
|
subsequent boots.
|
|
|
|
Prior behaviour wrote the marker AFTER the prompt completed successfully. If
|
|
the prompt crashed mid-execution (e.g. ProcessError from a stale Claude
|
|
session), the marker was never written; every subsequent container boot
|
|
replayed the same failing prompt, cascading into "every message crashes until
|
|
an operator intervenes." See GitHub issue #71.
|
|
|
|
Fix (2026-04-12): write the marker BEFORE firing the prompt. If the prompt
|
|
fails, operators re-send it manually via chat — cheap and available — instead
|
|
of trapping the workspace in a crash loop.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
|
|
def resolve_initial_prompt_marker(config_path: str) -> str:
|
|
"""Return the path where the `.initial_prompt_done` marker should live.
|
|
|
|
Prefers ``<config_path>/.initial_prompt_done`` when the directory is
|
|
writable; falls back to ``/workspace/.initial_prompt_done`` for containers
|
|
where ``/configs`` is read-only.
|
|
"""
|
|
if os.access(config_path, os.W_OK):
|
|
return os.path.join(config_path, ".initial_prompt_done")
|
|
return "/workspace/.initial_prompt_done"
|
|
|
|
|
|
def mark_initial_prompt_attempted(marker_path: str) -> bool:
|
|
"""Write the marker best-effort. Return True on success, False on I/O error.
|
|
|
|
Called BEFORE the initial-prompt self-message is sent. If the attempt
|
|
later fails, the marker is still present — so the next container boot
|
|
does NOT replay the same failing prompt. Operators retry manually via
|
|
the chat interface instead of relying on auto-replay.
|
|
"""
|
|
try:
|
|
with open(marker_path, "w") as f:
|
|
f.write("attempted")
|
|
return True
|
|
except OSError:
|
|
return False
|