processing: inspectable per-run stop seam for lifecycle fire
Unify halt and fire cancellation by verified run identity with durable first-owner receipts and shared deadlines. Integrate the instance control with the current host, library and tester-socket paths. Focused nine-file evidence is 62 passing tests; broad suites, mutants and review remain pending.
This commit is contained in:
1 parent
41b0cf7551
commit
f7bf7dfec0
79 files changed
+3286
-91
No files matched your search
+2
-1
@@ -53,6 +53,7 @@ case "$NAME" in *[!A-Za-z0-9._-]*|'') echo "agent: invalid agent name" >&2; exit
|
||||
load_config
|
||||
load_release
|
||||
bootstrap_runtime_dir
|
||||
processing_guard
|
||||
ensure_release_aligned
|
||||
|
||||
# Named auth account (M19, corrected per owner in #48): per-launch
|
||||
@@ -205,4 +206,4 @@ export MOSAIC_WORKSPACE="/var/lib/mosaic/workspaces/$WORKSPACE"
|
||||
echo "agent: launching TUI agent '$NAME' (session: $SESSION, adapter: $MOSAIC_ADAPTER, model: $MOSAIC_MODEL)"
|
||||
echo "agent: contracts + $([ -n "$MISSION" ] && echo 'mission' || echo 'no mission') loaded; exit the TUI with /quit"
|
||||
# No -T: the TTY is the point. Ctrl+C twice or /quit exits.
|
||||
exec docker compose run --rm mosaic-agent
|
||||
exec node packages/processing-control/src/container-cli.mjs --tty
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
POC_ROOT_MARKER=".mosaic-poc-root"
|
||||
|
||||
# Trusted first initialization plus a fresh processing check, before credentials/build/network work.
|
||||
processing_guard() {
|
||||
node packages/processing-control/src/container-cli.mjs --check
|
||||
}
|
||||
|
||||
# Load and validate the Mosaic configuration (config.json), exporting
|
||||
# MOSAIC_DATA_ROOT, MOSAIC_PROVIDER, and MOSAIC_MODEL.
|
||||
#
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ load_config
|
||||
load_release
|
||||
|
||||
bootstrap_runtime_dir
|
||||
processing_guard
|
||||
ensure_release_aligned
|
||||
|
||||
# -T: no pseudo-TTY, so stdout is clean model output.
|
||||
@@ -21,4 +22,4 @@ ensure_release_aligned
|
||||
# an interactive terminal would otherwise block it forever. The request
|
||||
# text comes from the compose command, not stdin, so nothing is lost.
|
||||
# Errors, if any, go to stderr for diagnostics.
|
||||
docker compose run --rm -T mosaic-agent < /dev/null
|
||||
node packages/processing-control/src/container-cli.mjs < /dev/null
|
||||
+1
-1
@@ -41,6 +41,6 @@ if [ "${1:-}" = library ] && [ "${2:-}" = survey ]; then
|
||||
exec node "$REPO/packages/library/src/survey.mjs" "$@"
|
||||
fi
|
||||
case "${1:-}" in
|
||||
usage|project|limits|inbox|decide|tasks|agents|trail|talk|stop|launches|bus|plugins|credentials|hosts|name|names|follow|threads|thread) exec node "$REPO/packages/cli/src/cli.mjs" "$@" ;;
|
||||
halt|resume|processing|usage|project|limits|inbox|decide|tasks|agents|trail|talk|stop|launches|bus|plugins|credentials|hosts|name|names|follow|threads|thread) exec node "$REPO/packages/cli/src/cli.mjs" "$@" ;;
|
||||
esac
|
||||
exec node "$REPO/packages/seat/src/cli.mjs" "$@"
|
||||
+15
-6
@@ -36,6 +36,9 @@ import { spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { BusinessError, validateRoleDocument } from "../packages/business/src/index.mjs";
|
||||
import { RunsError, isRunId, listRunRecords, readRunRecord } from "../packages/runs/src/index.mjs";
|
||||
import { initializeControl, assertProcessing } from "../packages/processing-control/src/state.mjs";
|
||||
import { runContainer } from "../packages/processing-control/src/containers.mjs";
|
||||
import { unregisterRuntime } from "../packages/processing-control/src/runtimes.mjs";
|
||||
|
||||
const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const RUNS_DIRNAME = "runs";
|
||||
@@ -298,6 +301,8 @@ function runTask(taskFile, options = {}) {
|
||||
maxBuffer: 1024 * 1024,
|
||||
}).stdout,
|
||||
);
|
||||
try { initializeControl(resolved.dataRoot); assertProcessing(resolved.dataRoot); }
|
||||
catch (e) { fail(3, e.code?.startsWith('processing-') ? e.code : 'processing-state-invalid'); }
|
||||
|
||||
const task = validateTask(readJsonFile(taskFile, "task file"), path.resolve(taskFile));
|
||||
const runId = `r-${utcStamp()}-${randomBytes(3).toString("hex")}`;
|
||||
@@ -392,10 +397,10 @@ function runTask(taskFile, options = {}) {
|
||||
spawnEnv.MOSAIC_SESSION_FORK = `/var/lib/mosaic/${relative.split(path.sep).join("/")}`;
|
||||
}
|
||||
|
||||
const proc = spawnSync(
|
||||
"docker",
|
||||
["compose", "run", "--rm", "-T", "mosaic-agent", task.prompt],
|
||||
{
|
||||
let launched;
|
||||
try {
|
||||
launched = runContainer(resolved.dataRoot, { kind: 'task', business: 'instance', run: runId }, {
|
||||
args: [task.prompt],
|
||||
cwd: PROJECT_ROOT,
|
||||
env: spawnEnv,
|
||||
input: "", // stdin detached: print mode must never wait on a terminal (see issue #5)
|
||||
@@ -403,8 +408,9 @@ function runTask(taskFile, options = {}) {
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
timeout: task.timeoutSeconds * 1000,
|
||||
killSignal: "SIGKILL",
|
||||
},
|
||||
);
|
||||
});
|
||||
} catch (e) { fail(3, e.code?.startsWith('processing-') ? e.code : 'processing-container-failed'); }
|
||||
const { proc, managed, haltGeneration } = launched;
|
||||
const finishedAt = new Date();
|
||||
|
||||
fs.writeFileSync(stderrFile, proc.stderr ?? "", { flag: "wx" });
|
||||
@@ -427,6 +433,7 @@ function runTask(taskFile, options = {}) {
|
||||
status = "failed";
|
||||
reason = "expect-mismatch";
|
||||
}
|
||||
if (haltGeneration) { status = 'failed'; reason = 'kill-switch'; }
|
||||
|
||||
const result = {
|
||||
runVersion: 1,
|
||||
@@ -435,6 +442,7 @@ function runTask(taskFile, options = {}) {
|
||||
missionId: task.missionId,
|
||||
status,
|
||||
reason,
|
||||
...(haltGeneration ? { haltGeneration } : {}),
|
||||
request: task.prompt,
|
||||
response,
|
||||
expectedExact: expected,
|
||||
@@ -453,6 +461,7 @@ function runTask(taskFile, options = {}) {
|
||||
};
|
||||
|
||||
writeOnce(path.join(runDir, "result.json"), `${JSON.stringify(result, null, 2)}\n`);
|
||||
if (!haltGeneration && !proc.error && proc.status === 0) unregisterRuntime(resolved.dataRoot, managed.id);
|
||||
|
||||
process.stdout.write(`run: ${runId}\nstatus: ${status}${reason ? ` (${reason})` : ""}\nresponse: ${response}\n`);
|
||||
process.exit(status === "succeeded" ? 0 : 1);
|
||||
|
||||
@@ -13,6 +13,7 @@ source scripts/common.sh
|
||||
load_config
|
||||
load_release # compose requires MOSAIC_IMAGE_TAG; task runs are release-scoped too
|
||||
bootstrap_runtime_dir
|
||||
processing_guard
|
||||
|
||||
# Workers never auto-align releases (no builds or model gates mid-automation);
|
||||
# drift is reported so the operator or a human-facing launcher can align.
|
||||
|
||||
@@ -32,6 +32,7 @@ COPY_PATHS=(
|
||||
packages/bus/src packages/bus/schema.sql packages/business/src packages/cli/src packages/project
|
||||
packages/conversation/src packages/discord/src packages/discord/bin packages/discord/extension packages/harness/src
|
||||
packages/seat/src packages/tasks/src packages/tasks/fields.json packages/tasks/requirements.json
|
||||
packages/processing-control
|
||||
packages/webui/src packages/webui/README.md packages/cli/README.md
|
||||
packages/queue/src packages/library/src packages/library/bin
|
||||
# Whole directory: every in-repo plug-in module ships with the copy (row 131).
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ IMAGE="$MOSAIC_IMAGE_TAG"
|
||||
# Ensure the configured data root exists (host-owned) before the mount,
|
||||
# otherwise Docker would auto-create a root-owned directory.
|
||||
bootstrap_runtime_dir
|
||||
processing_guard
|
||||
ensure_release_aligned
|
||||
|
||||
# 1. Build the image only if it is not already present.
|
||||
@@ -42,7 +43,7 @@ fi
|
||||
# 2. Run the agent request (stdout only = model response).
|
||||
# stdin detached: see hello.sh — print mode would block on a terminal.
|
||||
set +e
|
||||
RESPONSE="$(docker compose run --rm -T mosaic-agent </dev/null 2>/tmp/mosaic-poc-stderr.$$)"
|
||||
RESPONSE="$(node packages/processing-control/src/container-cli.mjs </dev/null 2>/tmp/mosaic-poc-stderr.$$)"
|
||||
RC=$?
|
||||
set -e
|
||||
STDERR_FILE="/tmp/mosaic-poc-stderr.$$"
|
||||
|
||||
Reference in new issue
Block a user