@lazyingart/agintiflow 0.20.178 → 0.20.182

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,7 @@ Each pipeline should create these project-local files or equivalents:
11
11
  - Task manifest: stable chunk IDs, source locations, dependencies, prompt/schema version, and output paths.
12
12
  - Schema and validator: the exact artifact shape plus semantic checks that define a promotable output.
13
13
  - Runners: writer, reviewer, repairer, monitor, merge, compile/export, and status commands.
14
+ - Freshness markers: run IDs, heartbeats, log/status timestamps, and tmux markers that separate a current run from old scrollback.
14
15
  - Completion report: counts, first missing ID, failed/quarantined items, latest previews, final artifact paths, and resume commands.
15
16
 
16
17
  The target repository owns its schemas, prompts, chunk policy, and rendering code. AgInTiFlow owns the behavior: inspect, create missing scripts, run observable sessions, preserve valid work, validate, repair, compile, and report evidence.
@@ -27,10 +28,12 @@ The repairer runs independently of the writer. It can wake from status files, ha
27
28
 
28
29
  The monitor is gentle. It waits through healthy progress and provider limits, restarts only on hard evidence of stall or crash, and records each decision.
29
30
 
31
+ When the monitor restarts a tmux worker, it should require a fresh run marker or updated heartbeat before judging the new run. Capturing a pane immediately after restart can include old failures; those lines are history unless they occur after the current marker or current log timestamp.
32
+
30
33
  ## Concurrency
31
34
 
32
35
  Parallelism is optional. When used, each worker needs deterministic shard ownership, separate logs, atomic writes, and no direct compile responsibility. Merge, promotion, compilation, publishing, and commits should be serialized unless the project already has a safe coordinator.
33
36
 
34
37
  ## Completion
35
38
 
36
- A run is complete only when the final artifact was built from the current manifest and the status report shows full coverage or intentional quarantine. A successful tmux pane, a page count, or a single preview file is not enough.
39
+ A run is complete only when the final artifact was built from the current manifest and the status report shows full coverage or intentional quarantine. A successful tmux pane, stale scrollback, a page count, or a single preview file is not enough.
@@ -138,6 +138,22 @@ aginti "start a tmux session named demo, run ls in it, keep it open, and tell me
138
138
 
139
139
  The agent should use `tmux_start_session`, `tmux_send_keys`, and `tmux_capture_pane`, not Docker `run_command`. In Docker sandbox mode, tmux commands must stay inside the project. In host mode, tmux command text is still governed by host shell policy; if a tmux command is blocked, present the suggested rerun path instead of trying tmux as a workaround. For trusted whole-host tmux work, use the host recipe above.
140
140
 
141
+ Long download or long command:
142
+
143
+ ```bash
144
+ aginti "download this large zip with wget -c, verify the final byte count, and do not burn model steps polling it"
145
+ ```
146
+
147
+ The agent should use `start_long_job` once instead of repeatedly calling `wait`, `run_command`, or `tmux_capture_pane`. The tool creates `.aginti/long-jobs/<job-id>/status.json`, stdout/stderr logs, a supervisor log, and an optional status card. For resumable downloads, the agent should first determine `Content-Length` when practical, then call `start_long_job` with:
148
+
149
+ - `command`: a resumable transfer such as `wget -c URL -O file.zip`
150
+ - `expectedOutputPath`: the downloaded file
151
+ - `expectedSizeBytes`: the expected byte count
152
+ - `verifyCommand`: a deterministic check such as `unzip -t file.zip` or `sha256sum -c`
153
+ - `restartOnFailure`: `true` when the transfer command is safe to resume
154
+
155
+ After `start_long_job` succeeds, the model loop should finish with the job id and status path. Later status checks should use `long_job_status` or direct shell inspection of the status JSON. This keeps multi-hour I/O under a shell supervisor instead of consuming model tokens and step budget.
156
+
141
157
  ## Future Persistent Container Mode
142
158
 
143
159
  A useful next runtime mode is a service container:
@@ -14,10 +14,12 @@ Concurrency is a tool, not a product stance. AgInTiFlow should choose sequential
14
14
 
15
15
  Review and repair are separate from writing. A reviewer should detect missing source units, repeated filler, malformed structured data, source drift, and known quality failures, then produce candidate repairs or failed-only requests. A repairer should be able to run independently of the writer, wake from status files, run bounded passes, and exit without blocking healthy progress.
16
16
 
17
+ Tmux evidence must be fresh. Tmux panes preserve old scrollback, so after a restart the agent should emit a unique run marker into the pane and durable log/status file, then verify output after that marker. If no marker exists, it must compare log mtimes, process PID/elapsed time, and status timestamps before claiming the current run is healthy. Old failures in scrollback are useful history, not proof that the restarted worker is still failing.
18
+
17
19
  ## Boundaries
18
20
 
19
21
  AgInTiFlow should not embed project-specific schemas in its core. A book writer, data pipeline, or build system owns its own validators and artifact layout. AgInTiFlow provides the reusable behavior: diagnose, preserve valid work, patch the local workflow, verify, build checkpoint artifacts, restart only affected sessions, and report exact resume commands.
20
22
 
21
23
  ## Verification
22
24
 
23
- After a repair, the agent should run syntax checks for changed scripts, perform a dry-run or bounded batch when safe, inspect counters and first-missing IDs, and keep unrelated tmux sessions running. If the same symptom repeats, the agent should improve the project workflow or a reusable AgInTiFlow skill instead of repeatedly sending manual nudges.
25
+ After a repair, the agent should run syntax checks for changed scripts, perform a dry-run or bounded batch when safe, inspect counters, first-missing IDs, fresh run markers, and timestamps, and keep unrelated tmux sessions running. If the same symptom repeats, the agent should improve the project workflow or a reusable AgInTiFlow skill instead of repeatedly sending manual nudges.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.178",
3
+ "version": "0.20.182",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -63,6 +63,7 @@
63
63
  "scripts/smoke-capabilities.js",
64
64
  "scripts/smoke-auto-update.js",
65
65
  "scripts/smoke-inbox.js",
66
+ "scripts/smoke-long-jobs.js",
66
67
  "scripts/smoke-mcp.js",
67
68
  "scripts/fixtures/mcp-stdio-smoke-server.mjs",
68
69
  "scripts/smoke-model-roles.js",
@@ -91,7 +92,7 @@
91
92
  "scripts": {
92
93
  "start": "node run.js",
93
94
  "web": "node web.js",
94
- "check": "node --check run.js && node --check web.js && node --check bin/aginti-cli.js && node --check src/*.js && node --check src/mcp/*.js && node --check public/app.js && node --check scripts/seed-supervised-homework.js && node --check scripts/smoke-mcp.js && node --check scripts/smoke-web-ui.js && node --check scripts/fixtures/mcp-stdio-smoke-server.mjs",
95
+ "check": "node --check run.js && node --check web.js && node --check bin/aginti-cli.js && node --check src/*.js && node --check src/mcp/*.js && node --check public/app.js && node --check scripts/seed-supervised-homework.js && node --check scripts/smoke-mcp.js && node --check scripts/smoke-web-ui.js && node --check scripts/smoke-scs-evidence-visibility.js && node --check scripts/fixtures/mcp-stdio-smoke-server.mjs",
95
96
  "setup:toolchain-docker": "scripts/setup-agent-toolchain-docker.sh",
96
97
  "smoke:coding-tools": "node scripts/smoke-coding-tools.js",
97
98
  "smoke:dynamic-step-budget": "node scripts/smoke-dynamic-step-budget.js",
@@ -102,9 +103,11 @@
102
103
  "smoke:cli-chat": "node scripts/smoke-cli-chat.js",
103
104
  "smoke:docker-command": "node scripts/smoke-docker-command.js",
104
105
  "smoke:skills": "node scripts/smoke-skills.js",
106
+ "smoke:scs-evidence": "node scripts/smoke-scs-evidence-visibility.js",
105
107
  "smoke:skillmesh": "node scripts/smoke-skillmesh.js",
106
108
  "smoke:toolchain-docker": "node scripts/smoke-toolchain-docker.js",
107
109
  "smoke:inbox": "node scripts/smoke-inbox.js",
110
+ "smoke:long-jobs": "node scripts/smoke-long-jobs.js",
108
111
  "smoke:mcp": "node scripts/smoke-mcp.js",
109
112
  "smoke:model-roles": "node scripts/smoke-model-roles.js",
110
113
  "smoke:platform": "node scripts/smoke-platform.js",
@@ -124,7 +127,7 @@
124
127
  "storage:migrate": "node bin/aginti-cli.js storage migrate",
125
128
  "publish:env": "node scripts/npm-publish-from-env.js publish --access public",
126
129
  "publish:env:whoami": "node scripts/npm-publish-from-env.js whoami",
127
- "test": "npm run check && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:web-ui && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:docker-command && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:auth && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:mcp && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:permission-modes && npm run smoke:skills && npm run smoke:skillmesh && npm run smoke:tmux-tools && npm run smoke:cli-chat && npm run smoke:inbox",
130
+ "test": "npm run check && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:web-ui && npm run smoke:web-autostart && npm run smoke:webapp-command && npm run smoke:web-port-fallback && npm run smoke:docker-command && npm run smoke:coding-tools && npm run smoke:dynamic-step-budget && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:auth && npm run smoke:canvas-artifacts && npm run smoke:capabilities && npm run smoke:mcp && npm run smoke:model-roles && npm run smoke:platform && npm run smoke:permission-modes && npm run smoke:skills && npm run smoke:skillmesh && npm run smoke:tmux-tools && npm run smoke:long-jobs && npm run smoke:cli-chat && npm run smoke:inbox",
128
131
  "pack:dry-run": "npm pack --dry-run",
129
132
  "smoke:capabilities": "node scripts/smoke-capabilities.js"
130
133
  },
@@ -0,0 +1,66 @@
1
+ # AgInTiFlow Generalization Audit
2
+
3
+ Date: 2026-05-20
4
+
5
+ ## Scope
6
+
7
+ This audit checks whether the core AgInTiFlow runtime learned a general problem-solving capability, or whether it accidentally hard-coded a project-specific browser workflow.
8
+
9
+ Reviewed areas:
10
+
11
+ - SCS controller and evidence gates
12
+ - Engineering guidance and dynamic step budget
13
+ - Built-in skills and project-local skill loading
14
+ - Smoke tests that protect browser workflows, upload contracts, and SCS validation
15
+
16
+ ## Findings
17
+
18
+ AgInTiFlow core should stay task-independent. It may know that browser work often needs page-state reconciliation, scoped selectors, attachment verification, model/duration verification, submit evidence, and external blocker handling. It should not know a private platform name, a private repository name, a private character set, a fixed prompt, or fixed asset filenames.
19
+
20
+ The useful general capability is:
21
+
22
+ - derive a task contract from the user's request
23
+ - preserve exact input paths and forbidden actions
24
+ - reject plans that invent unrequested uploads or skipped UI actions
25
+ - require concrete browser, visual, filesystem, or command evidence before finish
26
+ - route failed finish claims back to a validator/replan loop
27
+ - load narrow project-local skills from `.aginti/skills/<skill-id>/SKILL.md`
28
+
29
+ ## Remediation
30
+
31
+ Project-specific browser/video terms were removed from core heuristics and smoke fixtures. Generic Chinese and English browser workflow terms remain because they apply across websites.
32
+
33
+ Kept in core:
34
+
35
+ - generic browser composer guidance
36
+ - generic upload/asset-library/reference-media verification
37
+ - generic model, mode, duration, and submit-state verification
38
+ - generic external blocker handling such as login, captcha, credits, permission, server error, or user confirmation
39
+ - project-local skill loading and skill-creation workflow
40
+
41
+ Moved out of core:
42
+
43
+ - private platform names
44
+ - private repository names
45
+ - private asset filenames
46
+ - private character names
47
+ - task-specific model branding examples
48
+
49
+ ## Design Rule
50
+
51
+ If a future task requires special UI labels, account behavior, reference media, or platform-specific workarounds, create or update a project-local skill. Only promote a rule into AgInTiFlow core when the rule applies to a broad class of tasks such as browser form submission, book generation, LaTeX compilation, translation, testing, or deployment.
52
+
53
+ ## Verification
54
+
55
+ Commands run:
56
+
57
+ ```bash
58
+ npm run check
59
+ npm run smoke:model-roles
60
+ node scripts/smoke-skills.js
61
+ node scripts/smoke-dynamic-step-budget.js
62
+ node scripts/smoke-coding-tools.js
63
+ rg -n --hidden --glob '!node_modules/**' --glob '!.git/**' --glob '!dist/**' --glob '!coverage/**' 'xiaoyunque|小云雀|xyq|jianying|LALACHAN|啦啦|拉拉|阿芽|飒飒|Seedance|沉浸式短片|Trio\.png|display\.png|R1\.jpg|patchwork-leather|duanpian' .
64
+ ```
65
+
66
+ Result: checks passed, and the repository search found no remaining private workflow terms in AgInTiFlow runtime code, tests, or general references after excluding this audit's own search expression.
@@ -40,30 +40,30 @@ AgInTiFlow relevance:
40
40
  - AgInTiFlow must not own or silently mutate AAPS-selected project, workflow,
41
41
  program, block, or working file.
42
42
 
43
- ### LALACHAN Browser And AgInTi Supervision Source Session
43
+ ### Browser Automation And AgInTi Supervision Source Session
44
44
 
45
45
  - Session: `019dc795-e538-75b2-8a03-bc103b32985d`
46
- - Status context shown by Codex: `~/ProjectsLFS/LALACHAN`
47
- - Primary repo involved: `/home/lachlan/ProjectsLFS/LALACHAN`
46
+ - Status context shown by Codex: a project-local media/browser workflow repo
47
+ - Primary repo involved: project-local, outside AgInTiFlow core
48
48
  - Related AgInTiFlow repo: `/home/lachlan/ProjectsLFS/Agent/AgInTiFlow`
49
49
  - Role: failure-source and product-requirement session.
50
50
 
51
- This session started from LALACHAN/Xiaoyunque browser video workflows and exposed
52
- a general AgInTiFlow weakness: the executor could claim progress or completion
51
+ This session started from a project-local browser video workflow and exposed a
52
+ general AgInTiFlow weakness: the executor could claim progress or completion
53
53
  without enough evidence that the requested browser state, upload state, selected
54
54
  mode, selected model, prompt, reference media, or generated artifact actually
55
55
  existed.
56
56
 
57
57
  Important lesson from this session:
58
58
 
59
- - Do not hard-code Xiaoyunque-specific rules into AgInTiFlow core.
59
+ - Do not hard-code project-specific browser workflow rules into AgInTiFlow core.
60
60
  - Keep domain details in project-local skills such as `.aginti/skills/<id>/SKILL.md`.
61
61
  - Improve the core agent harness instead: task contract, monitor, evidence ledger,
62
62
  validator gate, replan loop, and real blocker reporting.
63
63
 
64
64
  Project-local skill produced from this direction:
65
65
 
66
- - `/home/lachlan/ProjectsLFS/LALACHAN/.aginti/skills/xiaoyunque-video-browser/SKILL.md`
66
+ - `.aginti/skills/<project-browser-workflow>/SKILL.md` in the task repository
67
67
 
68
68
  ### ZhJpBook And AgInTiFlow Implementation Session
69
69
 
@@ -76,7 +76,7 @@ Project-local skill produced from this direction:
76
76
  This session used a long bilingual book pipeline to stress AgInTiFlow's ability
77
77
  to run durable, evidence-based work: source conversion, chunking, DeepSeek JSON
78
78
  writing, validation, monitoring, PDF compilation, artifact handling, and recovery.
79
- The same core principle from the LALACHAN session was then implemented in
79
+ The same core principle from the browser workflow session was then implemented in
80
80
  AgInTiFlow.
81
81
 
82
82
  Relevant AgInTiFlow commits:
@@ -127,7 +127,7 @@ These sessions should be read together:
127
127
  - Agent Meta-AAPS supplies the cross-repo bridge: AAPS borrows useful
128
128
  AgInTiFlow interaction patterns while keeping AAPS as the state and semantics
129
129
  owner.
130
- - LALACHAN supplies the concrete failure mode: browser automation and media tasks
130
+ - The browser workflow session supplies the concrete failure mode: browser automation and media tasks
131
131
  can look successful while the visible external state is wrong or unverified.
132
132
  - ZhJpBook supplies the long-running pipeline pressure: the agent must keep
133
133
  moving through scripts, monitors, validators, and artifacts without accepting
@@ -448,7 +448,7 @@ try {
448
448
  assert(hostLocalhostJsonProbePolicy.allowed, "host localhost JSON probe should not require full-host destructive access");
449
449
  assert(hostLocalhostJsonProbePolicy.category === "network-fetch", "host localhost JSON probe should stay classified as network-fetch");
450
450
  const absolutePythonHelperPolicy = evaluateCommandPolicy(
451
- "/home/lachlan/miniconda3/bin/python scripts/xyq_cdp_browser.py list-pages",
451
+ "/home/lachlan/miniconda3/bin/python scripts/browser_cdp_helper.py list-pages",
452
452
  hostWorkspacePolicy
453
453
  );
454
454
  assert(absolutePythonHelperPolicy.allowed, "host absolute Python helper script should be allowed without full-host destructive access");
@@ -7,6 +7,7 @@ import { runAgent } from "../src/agent-runner.js";
7
7
  import { resolveRuntimeConfig } from "../src/config.js";
8
8
  import {
9
9
  browserSubmitFinishIssue,
10
+ isRecoverableShellToolResult,
10
11
  isSuspiciousBroadBrowserToolResult,
11
12
  shouldActivateScs,
12
13
  shouldReviewToolResult,
@@ -85,6 +86,29 @@ try {
85
86
  shouldReviewToolResult(broadBrowserClick, { meta: {} }),
86
87
  "SCS should review suspicious broad browser click results"
87
88
  );
89
+ const blockedSecretProbe = {
90
+ toolName: "run_command",
91
+ blocked: true,
92
+ args: { command: "env | grep API_KEY" },
93
+ reason: "Command is blocked because it references secrets or credential files.",
94
+ };
95
+ assert(isRecoverableShellToolResult(blockedSecretProbe), "SCS should classify blocked secret probes as recoverable shell results");
96
+ assert(
97
+ !shouldReviewToolResult(blockedSecretProbe, { meta: {} }),
98
+ "SCS should not derail the phase for a safely blocked credential probe"
99
+ );
100
+ const malformedReadOnlyCheck = {
101
+ toolName: "run_command",
102
+ ok: false,
103
+ exitCode: 2,
104
+ args: { command: "for f in *.pdf; do python3 -c 'print(\"oops\")'" },
105
+ stderr: "/bin/bash: -c: line 9: syntax error: unexpected end of file",
106
+ };
107
+ assert(isRecoverableShellToolResult(malformedReadOnlyCheck), "SCS should classify shell quoting mistakes as recoverable");
108
+ assert(
109
+ !shouldReviewToolResult(malformedReadOnlyCheck, { meta: {} }),
110
+ "SCS should let the normal agent loop repair simple shell quoting mistakes"
111
+ );
88
112
  assert(
89
113
  !isSuspiciousBroadBrowserToolResult({
90
114
  toolName: "run_command",
@@ -96,19 +120,19 @@ try {
96
120
  );
97
121
  assert(
98
122
  recommendedMaxStepsForTask({
99
- goal: "Use Chrome CDP to upload five images, select an asset-library video, choose non-VIP model, and submit the Xiaoyunque browser composer.",
123
+ goal: "Use Chrome CDP to upload five images, select an asset-library video, choose the requested non-premium model tier, and submit the browser composer.",
100
124
  }) >= 48,
101
125
  "browser submit workflows need a larger default step budget"
102
126
  );
103
127
  assert(
104
128
  browserSubmitFinishIssue(
105
- "小云雀网页上传五张图,从资产库选择参考视频,然后提交生成",
106
- "资产库旧三人视频 未执行;提交 未执行;步骤不足。"
129
+ "网页创作器上传五张图,从素材库选择参考视频,然后提交生成",
130
+ "素材库参考视频 未执行;提交 未执行;步骤不足。"
107
131
  ),
108
132
  "SCS finish gate should reject unfinished browser submit reports"
109
133
  );
110
134
  assert(
111
- !browserSubmitFinishIssue("小云雀网页提交生成", "停止:积分不足,需要用户处理 credits not enough。"),
135
+ !browserSubmitFinishIssue("网页创作器提交生成", "停止:积分不足,需要用户处理 credits not enough。"),
112
136
  "SCS finish gate should allow real external browser blockers"
113
137
  );
114
138
 
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ import assert from "node:assert/strict";
3
+ import fs from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { checkToolUse } from "../src/guardrails.js";
7
+ import { longJobStatus, startLongJob } from "../src/long-job-tools.js";
8
+ import { tmuxAvailable } from "../src/tmux-tools.js";
9
+
10
+ const workspace = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-long-job-"));
11
+ const config = {
12
+ allowShellTool: true,
13
+ allowDestructive: true,
14
+ allowPasswords: true,
15
+ commandCwd: workspace,
16
+ sandboxMode: "host",
17
+ useDockerSandbox: false,
18
+ };
19
+ const dockerConfig = {
20
+ ...config,
21
+ allowDestructive: false,
22
+ allowPasswords: false,
23
+ useDockerSandbox: true,
24
+ sandboxMode: "docker-workspace",
25
+ packageInstallPolicy: "allow",
26
+ };
27
+
28
+ function sleep(ms) {
29
+ return new Promise((resolve) => setTimeout(resolve, ms));
30
+ }
31
+
32
+ try {
33
+ if (!(await tmuxAvailable())) {
34
+ console.log(JSON.stringify({ ok: true, skipped: true, reason: "tmux is not installed" }, null, 2));
35
+ process.exit(0);
36
+ }
37
+
38
+ const noShell = checkToolUse({
39
+ toolName: "start_long_job",
40
+ args: { command: "printf ok > out.txt" },
41
+ config: { ...config, allowShellTool: false },
42
+ });
43
+ assert.equal(noShell.allowed, false, "start_long_job should require shell capability");
44
+
45
+ const outsidePath = path.join(os.tmpdir(), "aginti-long-job-outside.txt");
46
+ const outsideGuard = checkToolUse({
47
+ toolName: "start_long_job",
48
+ args: { name: "outside", command: `cat ${outsidePath}` },
49
+ config: dockerConfig,
50
+ });
51
+ assert.equal(outsideGuard.allowed, false, "Docker-mode long jobs should block outside host absolute paths");
52
+
53
+ const start = await startLongJob(
54
+ {
55
+ name: "smoke-download-style",
56
+ command: "printf 'abc123' > download.bin",
57
+ expectedOutputPath: "download.bin",
58
+ expectedSizeBytes: 6,
59
+ verifyCommand: "grep -q abc download.bin",
60
+ pollIntervalSeconds: 5,
61
+ note: "Smoke test for durable long command handoff.",
62
+ },
63
+ config
64
+ );
65
+ assert.equal(start.ok, true, start.error || start.reason);
66
+ assert.equal(start.background, true, "long job should return as a background handoff");
67
+ assert.match(start.statusPath, /^\.aginti\/long-jobs\//, "status path should be project-local");
68
+
69
+ let status = null;
70
+ for (let i = 0; i < 30; i += 1) {
71
+ await sleep(250);
72
+ status = await longJobStatus({ jobId: start.jobId }, config);
73
+ assert.equal(status.ok, true, status.reason);
74
+ if (["completed", "failed"].includes(status.state)) break;
75
+ }
76
+ assert.equal(status?.state, "completed", `long job did not complete: ${JSON.stringify(status)}`);
77
+ assert.equal(status.outputBytes, 6, "expected-size progress was not recorded");
78
+ assert.equal(status.verifyExitCode, 0, "verify command did not pass");
79
+
80
+ const output = await fs.readFile(path.join(workspace, "download.bin"), "utf8");
81
+ assert.equal(output, "abc123", "job output content mismatch");
82
+ await fs.access(path.join(workspace, start.statusPath));
83
+ await fs.access(path.join(workspace, start.stdoutPath));
84
+ await fs.access(path.join(workspace, start.stderrPath));
85
+ await fs.access(path.join(workspace, start.supervisorLogPath));
86
+
87
+ console.log(
88
+ JSON.stringify(
89
+ {
90
+ ok: true,
91
+ workspace,
92
+ jobId: start.jobId,
93
+ statusPath: start.statusPath,
94
+ state: status.state,
95
+ outputBytes: status.outputBytes,
96
+ verifyExitCode: status.verifyExitCode,
97
+ },
98
+ null,
99
+ 2
100
+ )
101
+ );
102
+ } finally {
103
+ await fs.rm(workspace, { recursive: true, force: true }).catch(() => {});
104
+ }
@@ -11,7 +11,13 @@ import {
11
11
  } from "../src/model-routing.js";
12
12
  import { normalizeTextToolCallResponse, parseTextToolCalls, usesTextToolProtocol } from "../src/model-client.js";
13
13
  import { modelRoleChoices, selectorVisibleWindow } from "../src/interactive-cli.js";
14
- import { buildScsEvidencePack, buildSupervisorInstruction, reviewScsFinish, shouldRequestScsReplan } from "../src/scs-controller.js";
14
+ import {
15
+ buildScsEvidencePack,
16
+ buildSupervisorInstruction,
17
+ deterministicPlanActionContradiction,
18
+ reviewScsFinish,
19
+ shouldRequestScsReplan,
20
+ } from "../src/scs-controller.js";
15
21
  import { buildScsEvidenceLedger, deriveScsTaskContract, evaluateScsEvidence } from "../src/scs-evidence.js";
16
22
 
17
23
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
@@ -249,9 +255,28 @@ assert(scsEvidence.includes("evidenceLedger"), "SCS evidence pack should include
249
255
  assert(scsEvidence.includes("evaluation"), "SCS evidence pack should include deterministic contract evaluation");
250
256
 
251
257
  const uploadContract = deriveScsTaskContract({
252
- goal: "Upload five images in the browser composer and verify visible thumbnails. Do not submit.",
258
+ goal:
259
+ "Upload five images in the browser composer and verify visible thumbnails: /tmp/reference-a.png /tmp/reference-b.png /tmp/reference-c.png. Do not submit.",
253
260
  taskProfile: "website",
254
261
  });
262
+ assert(
263
+ uploadContract.exactInputPaths.includes("/tmp/reference-a.png") &&
264
+ uploadContract.exactInputPaths.includes("/tmp/reference-b.png") &&
265
+ uploadContract.exactInputPaths.includes("/tmp/reference-c.png"),
266
+ "SCS contract should preserve exact input/reference paths for browser upload tasks"
267
+ );
268
+ const uploadImagePlanContradiction = deterministicPlanActionContradiction(
269
+ "Find the latest .mp4 in the project, upload the video file, then click submit.",
270
+ uploadContract
271
+ );
272
+ assert(
273
+ /video-file upload/i.test(uploadImagePlanContradiction || ""),
274
+ "SCS deterministic plan gate should reject invented video-file uploads for image-upload tasks"
275
+ );
276
+ assert(
277
+ !deterministicPlanActionContradiction("Upload the five images, verify thumbnails, and do not submit.", uploadContract),
278
+ "SCS deterministic plan gate should allow matching image-upload plans"
279
+ );
255
280
  assert(uploadContract.requiresExternalEvidence, "browser upload contract should require external evidence");
256
281
  assert(
257
282
  uploadContract.requiredEvidence.some((item) => item.category === "browser"),
@@ -272,7 +297,7 @@ const weakUploadLedger = buildScsEvidenceLedger({
272
297
  toolName: "run_command",
273
298
  ok: true,
274
299
  exitCode: 0,
275
- args: { command: "scripts/cdp-helper set-file-input PAGE_ID display.png R1.jpg" },
300
+ args: { command: "scripts/cdp-helper set-file-input PAGE_ID reference-a.png reference-b.jpg" },
276
301
  stdout: '{"ok":true,"nodeCount":1}',
277
302
  }),
278
303
  },
@@ -292,7 +317,7 @@ const strongUploadLedger = buildScsEvidenceLedger({
292
317
  toolName: "run_command",
293
318
  ok: true,
294
319
  exitCode: 0,
295
- args: { command: "scripts/cdp-helper upload-images-verify PAGE_ID display.png R1.jpg --screenshot outputs/upload.png" },
320
+ args: { command: "scripts/cdp-helper upload-images-verify PAGE_ID reference-a.png reference-b.jpg --screenshot outputs/upload.png" },
296
321
  stdout: '{"ok":true,"visibleEvidenceCount":5,"screenshot":"outputs/upload.png"}',
297
322
  }),
298
323
  },
@@ -365,6 +390,42 @@ const jsonObjectContract = deriveScsTaskContract({
365
390
  const virtualFileContract = deriveScsTaskContract({
366
391
  goal: "Create file: /workspace/virtual-output.txt with virtual Docker path support.",
367
392
  });
393
+ const outputListContract = deriveScsTaskContract({
394
+ goal: [
395
+ "Create:",
396
+ "- `work/demo/generate_items.py`",
397
+ "- `work/demo/review_items.py`",
398
+ "",
399
+ "Validate with `work/demo/validate_items.py` before promoting output.",
400
+ "Do not treat `work/demo/existing_validator.py` as an output artifact.",
401
+ "",
402
+ "Output structure:",
403
+ "- `build/demo/primary/color/book.pdf`",
404
+ "- `build/demo/secondary/color/book.pdf`",
405
+ "",
406
+ "Each generated item file goes to:",
407
+ "`data/demo/items/{item_id}.json`",
408
+ ].join("\n"),
409
+ taskProfile: "code",
410
+ });
411
+ const manyOutputContract = deriveScsTaskContract({
412
+ goal: [
413
+ "Required outputs:",
414
+ "- `out/a01.json`",
415
+ "- `out/a02.json`",
416
+ "- `out/a03.json`",
417
+ "- `out/a04.json`",
418
+ "- `out/a05.json`",
419
+ "- `out/a06.json`",
420
+ "- `out/a07.json`",
421
+ "- `out/a08.json`",
422
+ "- `out/a09.json`",
423
+ "- `out/a10.json`",
424
+ "- `out/a11.json`",
425
+ "- `out/a12.json`",
426
+ ].join("\n"),
427
+ taskProfile: "code",
428
+ });
368
429
  const generatedReviewContract = deriveScsTaskContract({
369
430
  goal: [
370
431
  "Review focus: changed files only",
@@ -398,6 +459,23 @@ assert(
398
459
  !virtualFileContract.requiredEvidence.some((item) => item.category === "artifact"),
399
460
  "virtual output filename should require file evidence without treating output in the filename as an artifact"
400
461
  );
462
+ assert(
463
+ outputListContract.exactOutputPaths.includes("work/demo/generate_items.py") &&
464
+ outputListContract.exactOutputPaths.includes("work/demo/review_items.py") &&
465
+ outputListContract.exactOutputPaths.includes("build/demo/primary/color/book.pdf") &&
466
+ outputListContract.exactOutputPaths.includes("build/demo/secondary/color/book.pdf"),
467
+ "SCS should infer exact outputs from Create/Output structure list sections"
468
+ );
469
+ assert(
470
+ !outputListContract.exactOutputPaths.includes("work/demo/validate_items.py") &&
471
+ !outputListContract.exactOutputPaths.includes("work/demo/existing_validator.py") &&
472
+ !outputListContract.exactOutputPaths.some((item) => item.includes("{item_id}")),
473
+ "SCS should not treat validator/tool paths or templated paths as exact output artifacts"
474
+ );
475
+ assert(
476
+ manyOutputContract.exactOutputPaths.length === 12 && manyOutputContract.exactOutputPaths.includes("out/a12.json"),
477
+ "SCS should preserve more than eight exact output paths for multi-artifact tasks"
478
+ );
401
479
  assert(
402
480
  generatedReviewContract.requiredEvidence.some((item) => item.category === "command") &&
403
481
  !generatedReviewContract.requiredEvidence.some((item) => item.category === "file") &&
@@ -114,6 +114,11 @@ async function main() {
114
114
  assert(dangerAbsoluteMkdir.trustedDangerOverride === true, "danger absolute host path should be explicit override");
115
115
  const dangerPublish = evaluateCommandPolicy("npm publish", danger);
116
116
  assert(dangerPublish.allowed === false, "danger should still block hard publish/token guardrails");
117
+ const workspaceCdSecretProbe = evaluateCommandPolicy(
118
+ `cd ${workspace} && env | grep -i 'deepseek\\\\|API_KEY' | head -5; echo "EXIT:$?"`,
119
+ danger
120
+ );
121
+ assert(workspaceCdSecretProbe.allowed === false, "workspace-root cd must not bypass secret command guardrails");
117
122
  const outsidePath = path.join(tempRoot, "outside-danger.txt");
118
123
  const dangerOutside = await executeWorkspaceTool("write_file", { path: outsidePath, content: "danger outside ok" }, danger);
119
124
  assert(dangerOutside.ok === true, "danger should allow outside workspace file write");
@@ -66,7 +66,7 @@ assert(
66
66
  ),
67
67
  "browser automation prompt did not select browser-automation"
68
68
  );
69
- assert(selectedIds("小云雀 网页 上传 五张图 从资产库选择视频 提交").includes("browser-automation"), "Chinese XYQ prompt did not select browser-automation");
69
+ assert(selectedIds("网页表单 上传 五张图 从素材库选择视频 提交").includes("browser-automation"), "Chinese browser prompt did not select browser-automation");
70
70
  assert(selectedIds("write a LaTeX paper and compile a PDF").includes("latex-manuscript"), "latex prompt did not select latex-manuscript");
71
71
  assert(selectedIds("edit a Microsoft Word docx and preserve the original").includes("word-documents"), "docx prompt did not select word-documents");
72
72
  assert(selectedIds("generate a logo image with grsai nanobanana").includes("image-generation"), "image prompt did not select image-generation");
@@ -83,6 +83,13 @@ try {
83
83
  const destructive = checkTmuxToolUse("tmux_send_keys", { target: start.target, text: "rm -rf /" }, config);
84
84
  assert.equal(destructive.allowed, false, "tmux guardrail did not block destructive text");
85
85
 
86
+ const workspaceRelativeCleanup = checkToolUse({
87
+ toolName: "tmux_start_session",
88
+ args: { name: `${session}-cleanup`, cwd: ".", command: "rm -f logs/task.status && echo cleaned" },
89
+ config,
90
+ });
91
+ assert.equal(workspaceRelativeCleanup.allowed, true, "Host danger tmux should allow workspace-relative cleanup commands");
92
+
86
93
  const outsidePath = path.join(os.tmpdir(), "agintiflow-outside-workspace-canary.txt");
87
94
  const workspacePath = path.join(workspace, "inside-workspace.txt");
88
95
  const dockerTmuxOutsideStart = checkToolUse({
@@ -249,6 +256,7 @@ try {
249
256
  "list-sessions",
250
257
  "secret-guardrail",
251
258
  "destructive-guardrail",
259
+ "host-danger-workspace-cleanup-allowed",
252
260
  "docker-tmux-start-outside-path-guardrail",
253
261
  "docker-tmux-send-outside-path-guardrail",
254
262
  "docker-tmux-start-project-path-allowed",
@@ -38,7 +38,8 @@ Before launching long work, create or identify a project-local contract:
38
38
  3. Task manifest: stable chunk IDs, source location, dependency order, prompt/schema version, and output paths.
39
39
  4. Artifact schema: JSON Schema or other validator-owned shape for each generated unit.
40
40
  5. Runners: resumable writer, reviewer, repairer, monitor, merge, compile/export, and status commands.
41
- 6. Completion evidence: counters, first missing item, failed IDs, current previews, final artifact paths, and resume commands.
41
+ 6. Freshness evidence: runner IDs, heartbeats, log timestamps, status file mtimes, and tmux markers that separate current output from old scrollback.
42
+ 7. Completion evidence: counters, first missing item, failed IDs, current previews, final artifact paths, and resume commands.
42
43
 
43
44
  Project-specific schemas, prompts, layouts, and compilers belong in the target repository. AgInTiFlow provides the orchestration pattern and should generate or patch local scripts when they are missing.
44
45
 
@@ -50,6 +51,7 @@ Project-specific schemas, prompts, layouts, and compilers belong in the target r
50
51
  - Use isolated structured-data calls for repetitive JSON units. Keep prompts focused on the chunk, schema, source references, and validation errors.
51
52
  - Add deterministic canonicalizers before model retry when failures are representational, such as punctuation normalization, stable token splitting, missing metadata backfill, schema version migration, or renderer-specific wrapping. Do not spend provider calls on repairs a local script can prove.
52
53
  - Run writers in tmux or another observable background process. Each worker must have disjoint claims, atomic output writes, and shard-local logs.
54
+ - When restarting a tmux worker, print a unique run marker and write the same marker to its log or status file. A pane capture that only shows old scrollback is not proof of current progress.
53
55
  - Keep review and repair asynchronous but safe. Reviewers may produce candidate fixes while writers continue; only validators or merge scripts promote candidates.
54
56
  - Compile or export checkpoint previews after successful merge batches and always at final completion.
55
57
  - Commit reusable scripts, manifests, validators, templates, and stable checkpoints when the project expects git tracking.
@@ -68,4 +70,4 @@ A robust pipeline has an independent repair path that is not blocked by the main
68
70
 
69
71
  ## Done Criteria
70
72
 
71
- Do not call the task complete until the final artifact was built from the current manifest and the status report shows complete or intentionally quarantined coverage. A partial PDF, stale page count, or successful worker log is not enough.
73
+ Do not call the task complete until the final artifact was built from the current manifest and the status report shows complete or intentionally quarantined coverage. A partial PDF, stale page count, old tmux scrollback, or successful worker log is not enough.