@lazyingart/agintiflow 0.20.208 → 0.20.210
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.
- package/docs/supervision-campaign-ledger.md +27 -0
- package/package.json +13 -3
- package/scripts/check-js-syntax.js +38 -0
- package/scripts/smoke-canvas-artifacts.js +19 -0
- package/scripts/smoke-cli-chat.js +10 -0
- package/scripts/smoke-coding-tools.js +59 -0
- package/scripts/smoke-deep-research.js +7 -0
- package/scripts/smoke-dynamic-step-budget.js +219 -0
- package/scripts/smoke-integration-production-mount.js +3 -5
- package/scripts/smoke-integration-retained-event-ledger-bundle.js +1 -1
- package/scripts/smoke-integration-retained-native-execution-evidence.js +2948 -0
- package/scripts/smoke-integration-retained-native-session-repository-state.js +40 -1
- package/scripts/smoke-integration-retained-repository-kernel.js +1 -0
- package/scripts/smoke-integration-retained-runtime-repository-phase-b.js +1296 -0
- package/scripts/smoke-integration-retained-runtime-repository-surface.js +1230 -0
- package/scripts/smoke-integration-retained-session-state-store.js +1 -0
- package/scripts/smoke-integration-runtime-authority.js +5040 -0
- package/scripts/smoke-integration-runtime-repository-contract.js +3 -1
- package/scripts/smoke-integration-storage-authority.js +64 -2
- package/scripts/smoke-local-failure-recovery.js +497 -0
- package/scripts/smoke-model-roles.js +70 -0
- package/scripts/smoke-perception-research.js +28 -4
- package/scripts/smoke-progressive-tool-selection.js +315 -3
- package/scripts/smoke-scs-evidence-visibility.js +22 -0
- package/scripts/smoke-session-runtime.js +5 -0
- package/scripts/smoke-skills.js +4 -0
- package/scripts/smoke-supervision-ledger.js +20 -1
- package/scripts/smoke-truthful-completion.js +212 -8
- package/scripts/smoke-web-api.js +4 -2
- package/scripts/smoke-web-autostart.js +15 -1
- package/scripts/supervision-ledger.js +45 -11
- package/src/agent-runner.js +2163 -178
- package/src/artifact-tunnel.js +48 -7
- package/src/config.js +7 -2
- package/src/deep-research.js +13 -1
- package/src/integration-durable-common.js +1 -0
- package/src/integration-native-executor.js +114 -12
- package/src/integration-retained-native-execution-evidence.js +869 -0
- package/src/integration-retained-native-session-repository-state.js +431 -20
- package/src/integration-retained-runtime-repository-surface.js +3753 -0
- package/src/integration-retained-session-state-store.js +350 -4
- package/src/integration-retained-text-workspace.js +899 -0
- package/src/integration-retained-vision-workspace.js +1676 -0
- package/src/integration-runtime-authority.js +384 -20
- package/src/integration-runtime-process-owner-bootstrap.js +63 -0
- package/src/integration-runtime-repository.js +34 -1
- package/src/integration-session-persistence.js +259 -8
- package/src/integration-session-service.js +4 -4
- package/src/integration-storage-authority.js +265 -10
- package/src/model-client.js +100 -21
- package/src/perception-tools.js +30 -9
- package/src/progressive-tool-selection.js +129 -8
- package/src/research-routing.js +10 -6
- package/src/scs-controller.js +17 -1
- package/src/scs-evidence.js +330 -13
- package/src/session-runtime.js +2 -1
- package/src/session-store.js +124 -2
- package/src/skill-library.js +8 -3
- package/src/step-budget-controller.js +39 -6
- package/src/workspace-tools.js +28 -6
- package/web.js +3 -1
|
@@ -38,6 +38,11 @@ Create, start, and finish a concrete run with `test`, `start`, `event`, and
|
|
|
38
38
|
idempotent or append-only SQLite operation suitable for a persistent tmux
|
|
39
39
|
campaign.
|
|
40
40
|
|
|
41
|
+
Test registration validates that any named capability and scenario belong to
|
|
42
|
+
the same campaign. Finishing a test updates the test, capability, and scenario
|
|
43
|
+
status in one SQLite transaction, so a typo cannot silently leave the campaign
|
|
44
|
+
matrix stale or split across contradictory states.
|
|
45
|
+
|
|
41
46
|
Inspect current coverage:
|
|
42
47
|
|
|
43
48
|
```bash
|
|
@@ -48,3 +53,25 @@ node scripts/supervision-ledger.js status \
|
|
|
48
53
|
Do not mark a test passed from the agent's prose. Verify session events, files,
|
|
49
54
|
checks, external state, and side-effect boundaries first, then store those paths
|
|
50
55
|
in `evidence_json` and `events`.
|
|
56
|
+
|
|
57
|
+
## Current Campaign Findings
|
|
58
|
+
|
|
59
|
+
### Data analysis retained-state recovery
|
|
60
|
+
|
|
61
|
+
`data-analysis-local-011` passed after reusable runtime fixes. AgInTi completed
|
|
62
|
+
the mixed sensor-export project, repaired its own unclosed CSV handles, created
|
|
63
|
+
commits `2f79553` and `3317a59`, and resumed the same durable session after a
|
|
64
|
+
control-loop pause. Independent validation ran both the unit suite and the
|
|
65
|
+
canonical generator with `ResourceWarning` promoted to an error; all tests,
|
|
66
|
+
audit counts, condition means, plot bytes, and clean-git checks passed.
|
|
67
|
+
|
|
68
|
+
The incident established two general contracts:
|
|
69
|
+
|
|
70
|
+
- A canonical command wrapped only by an explicit exit-status probe, such as
|
|
71
|
+
`python analysis.py; echo "EXIT=$?"`, is valid evidence when the captured
|
|
72
|
+
status is zero. Arbitrary semicolon chains remain invalid because a later
|
|
73
|
+
command can hide an earlier failure.
|
|
74
|
+
- User-facing artifacts keep canonical workspace paths but receive a readable
|
|
75
|
+
canvas/download filename derived from the task title and source purpose.
|
|
76
|
+
Internal collision identifiers are short suffixes, never the leading or only
|
|
77
|
+
visible filename information.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.210",
|
|
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",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"scripts/setup-agent-toolchain-docker.sh",
|
|
62
62
|
"scripts/real-deepseek-capabilities.js",
|
|
63
63
|
"scripts/postinstall-webapp.js",
|
|
64
|
+
"scripts/check-js-syntax.js",
|
|
64
65
|
"scripts/seed-supervised-homework.js",
|
|
65
66
|
"scripts/supervision-ledger.js",
|
|
66
67
|
"scripts/smoke-supervision-ledger.js",
|
|
@@ -73,7 +74,11 @@
|
|
|
73
74
|
"scripts/smoke-integration-retained-repository-kernel.js",
|
|
74
75
|
"scripts/smoke-integration-retained-session-state-store.js",
|
|
75
76
|
"scripts/smoke-integration-retained-native-session-repository-state.js",
|
|
77
|
+
"scripts/smoke-integration-retained-native-execution-evidence.js",
|
|
78
|
+
"scripts/smoke-integration-retained-runtime-repository-surface.js",
|
|
79
|
+
"scripts/smoke-integration-retained-runtime-repository-phase-b.js",
|
|
76
80
|
"scripts/smoke-integration-runtime-repository-contract.js",
|
|
81
|
+
"scripts/smoke-integration-runtime-authority.js",
|
|
77
82
|
"scripts/smoke-auxiliary-tools.js",
|
|
78
83
|
"scripts/smoke-agentlink.js",
|
|
79
84
|
"scripts/smoke-auth.js",
|
|
@@ -91,6 +96,7 @@
|
|
|
91
96
|
"scripts/smoke-long-jobs.js",
|
|
92
97
|
"scripts/smoke-localllm-auto-max.js",
|
|
93
98
|
"scripts/smoke-localllm-code-routing.js",
|
|
99
|
+
"scripts/smoke-local-failure-recovery.js",
|
|
94
100
|
"scripts/smoke-local-resource-policy.js",
|
|
95
101
|
"scripts/smoke-localllm-model-tiers.js",
|
|
96
102
|
"scripts/smoke-localllm-provider.js",
|
|
@@ -139,7 +145,7 @@
|
|
|
139
145
|
"scripts": {
|
|
140
146
|
"start": "node run.js",
|
|
141
147
|
"web": "node web.js",
|
|
142
|
-
"check": "node
|
|
148
|
+
"check": "node scripts/check-js-syntax.js",
|
|
143
149
|
"setup:toolchain-docker": "scripts/setup-agent-toolchain-docker.sh",
|
|
144
150
|
"smoke:coding-tools": "node scripts/smoke-coding-tools.js",
|
|
145
151
|
"smoke:dynamic-step-budget": "node scripts/smoke-dynamic-step-budget.js",
|
|
@@ -203,11 +209,15 @@
|
|
|
203
209
|
"smoke:integration-retained-repository-kernel": "node --experimental-test-module-mocks scripts/smoke-integration-retained-repository-kernel.js",
|
|
204
210
|
"smoke:integration-retained-session-state-store": "node --experimental-test-module-mocks scripts/smoke-integration-retained-session-state-store.js",
|
|
205
211
|
"smoke:integration-retained-native-session-repository-state": "node --experimental-test-module-mocks scripts/smoke-integration-retained-native-session-repository-state.js",
|
|
212
|
+
"smoke:integration-retained-native-execution-evidence": "node scripts/smoke-integration-retained-native-execution-evidence.js",
|
|
213
|
+
"smoke:integration-retained-runtime-repository-surface": "node scripts/smoke-integration-retained-runtime-repository-surface.js",
|
|
214
|
+
"smoke:integration-retained-runtime-repository-phase-b": "node scripts/smoke-integration-retained-runtime-repository-phase-b.js",
|
|
206
215
|
"smoke:integration-runtime-repository-contract": "node scripts/smoke-integration-runtime-repository-contract.js",
|
|
216
|
+
"smoke:integration-runtime-authority": "node scripts/smoke-integration-runtime-authority.js",
|
|
207
217
|
"storage:migrate": "node bin/aginti-cli.js storage migrate",
|
|
208
218
|
"publish:env": "node scripts/npm-publish-from-env.js publish --access public",
|
|
209
219
|
"publish:env:whoami": "node scripts/npm-publish-from-env.js whoami",
|
|
210
|
-
"test": "npm run check && npm run smoke:integration-production-mount && npm run smoke:integration-storage-authority && npm run smoke:integration-retained-durable-common && npm run smoke:integration-retained-file-lock && npm run smoke:integration-retained-event-ledger && npm run smoke:integration-retained-event-ledger-bundle && npm run smoke:integration-retained-repository-kernel && npm run smoke:integration-retained-session-state-store && npm run smoke:integration-retained-native-session-repository-state && npm run smoke:integration-runtime-repository-contract && npm run smoke:localllm-provider && npm run smoke:localllm-model-tiers && npm run smoke:localllm-code-routing && npm run smoke:local-failure-recovery && npm run smoke:localllm-auto-max && npm run smoke:local-resource-policy && npm run smoke:context-budget-recovery && npm run smoke:session-runtime && npm run smoke:runtime-core && npm run smoke:progressive-tools && npm run smoke:truthful-completion && npm run smoke:writing-specialist-routing && npm run eval:local-first-agent && npm run eval:provider-attribution && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:math-rendering && 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:execution-policy && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:deep-research && npm run smoke:public-research && npm run smoke:safe-chat && npm run smoke:auth && npm run smoke:agentlink && 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:supervision-ledger && npm run smoke:tmux-tools && npm run smoke:long-jobs && npm run smoke:run-stdin && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
220
|
+
"test": "npm run check && npm run smoke:integration-production-mount && npm run smoke:integration-storage-authority && npm run smoke:integration-retained-durable-common && npm run smoke:integration-retained-file-lock && npm run smoke:integration-retained-event-ledger && npm run smoke:integration-retained-event-ledger-bundle && npm run smoke:integration-retained-repository-kernel && npm run smoke:integration-retained-session-state-store && npm run smoke:integration-retained-native-session-repository-state && npm run smoke:integration-retained-native-execution-evidence && npm run smoke:integration-retained-runtime-repository-surface && npm run smoke:integration-retained-runtime-repository-phase-b && npm run smoke:integration-runtime-repository-contract && npm run smoke:integration-runtime-authority && npm run smoke:localllm-provider && npm run smoke:localllm-model-tiers && npm run smoke:localllm-code-routing && npm run smoke:local-failure-recovery && npm run smoke:localllm-auto-max && npm run smoke:local-resource-policy && npm run smoke:context-budget-recovery && npm run smoke:session-runtime && npm run smoke:runtime-core && npm run smoke:progressive-tools && npm run smoke:truthful-completion && npm run smoke:writing-specialist-routing && npm run eval:local-first-agent && npm run eval:provider-attribution && npm run smoke:runtime-compat && npm run smoke:autoupdate && npm run smoke:web-api && npm run smoke:math-rendering && 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:execution-policy && npm run smoke:aaps-adapter && npm run smoke:auxiliary-tools && npm run smoke:perception-research && npm run smoke:deep-research && npm run smoke:public-research && npm run smoke:safe-chat && npm run smoke:auth && npm run smoke:agentlink && 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:supervision-ledger && npm run smoke:tmux-tools && npm run smoke:long-jobs && npm run smoke:run-stdin && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
211
221
|
"pack:dry-run": "npm pack --dry-run",
|
|
212
222
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
213
223
|
},
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
|
+
const roots = ["run.js", "web.js", "bin", "src", "public", "scripts"];
|
|
9
|
+
const extensions = new Set([".cjs", ".js", ".mjs"]);
|
|
10
|
+
|
|
11
|
+
async function collect(entryPath, files) {
|
|
12
|
+
const stat = await fs.lstat(entryPath);
|
|
13
|
+
if (stat.isSymbolicLink()) return;
|
|
14
|
+
if (stat.isDirectory()) {
|
|
15
|
+
const entries = await fs.readdir(entryPath, { withFileTypes: true });
|
|
16
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17
|
+
await collect(path.join(entryPath, entry.name), files);
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (stat.isFile() && extensions.has(path.extname(entryPath).toLowerCase())) files.push(entryPath);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const files = [];
|
|
25
|
+
for (const root of roots) await collect(path.join(projectRoot, root), files);
|
|
26
|
+
|
|
27
|
+
for (const filePath of files) {
|
|
28
|
+
const checked = spawnSync(process.execPath, ["--check", filePath], {
|
|
29
|
+
cwd: projectRoot,
|
|
30
|
+
encoding: "utf8",
|
|
31
|
+
});
|
|
32
|
+
if (checked.status !== 0) {
|
|
33
|
+
process.stderr.write(checked.stderr || checked.stdout || `Syntax check failed: ${filePath}\n`);
|
|
34
|
+
process.exit(checked.status || 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(`JavaScript syntax check passed (${files.length} files).`);
|
|
@@ -40,6 +40,24 @@ async function main() {
|
|
|
40
40
|
const persisted = await persistCanvasPayloadFile(normalized.payload, { config, store });
|
|
41
41
|
if (!persisted.ok) throw new Error(persisted.reason || "canvas artifact persistence failed");
|
|
42
42
|
if (!persisted.payload.artifactPersisted) throw new Error("canvas file was not persisted into session artifacts");
|
|
43
|
+
if (persisted.payload.downloadName !== "durable-report.md") {
|
|
44
|
+
throw new Error(`canvas artifact lost its meaningful download name: ${persisted.payload.downloadName}`);
|
|
45
|
+
}
|
|
46
|
+
if (!/^durable-report--[A-Za-z0-9_-]{8}\.md$/.test(path.basename(persisted.payload.sessionFilePath))) {
|
|
47
|
+
throw new Error(`canvas persistence put an opaque id before the filename: ${persisted.payload.sessionFilePath}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const genericNamed = normalizeCanvasPayload(
|
|
51
|
+
{
|
|
52
|
+
title: "Fluorescence Experiment Analysis",
|
|
53
|
+
kind: "pdf",
|
|
54
|
+
path: "report.pdf",
|
|
55
|
+
},
|
|
56
|
+
config
|
|
57
|
+
);
|
|
58
|
+
if (!genericNamed.ok || genericNamed.payload.downloadName !== "Fluorescence-Experiment-Analysis.pdf") {
|
|
59
|
+
throw new Error(`generic artifact did not receive a task-meaningful filename: ${genericNamed.payload?.downloadName}`);
|
|
60
|
+
}
|
|
43
61
|
|
|
44
62
|
await fs.rm(sourcePath);
|
|
45
63
|
|
|
@@ -59,6 +77,7 @@ async function main() {
|
|
|
59
77
|
const content = await readArtifactContent(items[0], { store, config });
|
|
60
78
|
if (!content.ok) throw new Error(content.error || "persisted artifact could not be read");
|
|
61
79
|
if (!String(content.text || "").includes("Durable report")) throw new Error("persisted artifact content mismatch");
|
|
80
|
+
if (content.filename !== "durable-report.md") throw new Error("artifact read metadata lost the download filename");
|
|
62
81
|
|
|
63
82
|
const largeImagePath = path.join(workspace, "large-preview.png");
|
|
64
83
|
const pngHeader = Buffer.from("89504e470d0a1a0a", "hex");
|
|
@@ -1190,6 +1190,7 @@ try {
|
|
|
1190
1190
|
|
|
1191
1191
|
const resumeStatePath = path.join(agintiflowHome, "sessions", resumedSessionId, "state.json");
|
|
1192
1192
|
const runtimeBeforeAmbientResume = JSON.parse(await fs.readFile(resumeStatePath, "utf8"));
|
|
1193
|
+
const completedArtifactBeforeAmbientResume = await fs.stat(path.join(tempRoot, "notes/interactive.md"));
|
|
1193
1194
|
const ambientSecret = "must-not-persist-cli-resume-secret";
|
|
1194
1195
|
const ordinaryResume = await runCli(
|
|
1195
1196
|
["resume", resumedSessionId, "--", "Continue this saved mock session without changing its runtime."],
|
|
@@ -1204,6 +1205,7 @@ try {
|
|
|
1204
1205
|
}
|
|
1205
1206
|
);
|
|
1206
1207
|
const runtimeAfterAmbientResume = JSON.parse(await fs.readFile(resumeStatePath, "utf8"));
|
|
1208
|
+
const completedArtifactAfterAmbientResume = await fs.stat(path.join(tempRoot, "notes/interactive.md"));
|
|
1207
1209
|
if (
|
|
1208
1210
|
runtimeAfterAmbientResume.meta.runtimeConfig.provider !== "mock" ||
|
|
1209
1211
|
runtimeAfterAmbientResume.meta.runtimeConfig.model !== "mock-agent" ||
|
|
@@ -1213,6 +1215,14 @@ try {
|
|
|
1213
1215
|
) {
|
|
1214
1216
|
throw new Error("ordinary one-shot resume drifted to ambient provider/model/key settings");
|
|
1215
1217
|
}
|
|
1218
|
+
if (
|
|
1219
|
+
completedArtifactAfterAmbientResume.mtimeMs !== completedArtifactBeforeAmbientResume.mtimeMs ||
|
|
1220
|
+
runtimeAfterAmbientResume.meta.goalContract.status !== "completed" ||
|
|
1221
|
+
runtimeAfterAmbientResume.meta.goalContract.lifecycle.at(-1)?.reason !== "completed_task_noop" ||
|
|
1222
|
+
!ordinaryResume.stdout.includes("did not repeat any tool or external side effect")
|
|
1223
|
+
) {
|
|
1224
|
+
throw new Error("generic resume of a completed task repeated work instead of preserving the verified result");
|
|
1225
|
+
}
|
|
1216
1226
|
|
|
1217
1227
|
await runCli(
|
|
1218
1228
|
["resume", resumedSessionId, "--no-web-search", "--no-auxiliary-tools", "--", "Continue with explicitly reduced tools."],
|
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import {
|
|
7
7
|
buildModelTimeoutRetryMessages,
|
|
8
|
+
genericArtifactFilenameBlock,
|
|
8
9
|
modelTimeoutRetryRoute,
|
|
9
10
|
repairModelMessageHistory,
|
|
10
11
|
shouldResetStaticDiscoveryPhase,
|
|
@@ -83,6 +84,40 @@ async function runMock(goal, sessionId, { resume = false } = {}) {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
try {
|
|
87
|
+
const genericArtifactBlock = await genericArtifactFilenameBlock(
|
|
88
|
+
"write_file",
|
|
89
|
+
{ path: "report.md", content: "summary" },
|
|
90
|
+
{ commandCwd: workspace, taskProfile: "data", goal: "Analyze the fluorescence experiment exports." },
|
|
91
|
+
{ goal: "Analyze the fluorescence experiment exports.", messages: [], meta: {} }
|
|
92
|
+
);
|
|
93
|
+
assert(
|
|
94
|
+
genericArtifactBlock?.category === "artifact-filename" &&
|
|
95
|
+
/fluorescence|experiment/i.test(genericArtifactBlock.permissionAdvice?.instruction || ""),
|
|
96
|
+
"new generic artifact filename was not redirected to a meaningful topic-derived name"
|
|
97
|
+
);
|
|
98
|
+
assert(
|
|
99
|
+
(await genericArtifactFilenameBlock(
|
|
100
|
+
"write_file",
|
|
101
|
+
{ path: "outputs/summary.json", content: "{}" },
|
|
102
|
+
{ commandCwd: workspace, taskProfile: "data", goal: "Analyze the experiment." },
|
|
103
|
+
{
|
|
104
|
+
goal: "Analyze the experiment.",
|
|
105
|
+
messages: [],
|
|
106
|
+
meta: { projectVerification: { requiredOutputs: ["outputs/summary.json"] } },
|
|
107
|
+
}
|
|
108
|
+
)) === null,
|
|
109
|
+
"an exact project-declared artifact filename was incorrectly rejected as generic"
|
|
110
|
+
);
|
|
111
|
+
assert(
|
|
112
|
+
(await genericArtifactFilenameBlock(
|
|
113
|
+
"write_file",
|
|
114
|
+
{ path: "reports/fluorescence-dose-response-analysis.md", content: "summary" },
|
|
115
|
+
{ commandCwd: workspace, taskProfile: "data" },
|
|
116
|
+
{ goal: "Analyze the experiment.", messages: [], meta: {} }
|
|
117
|
+
)) === null,
|
|
118
|
+
"a descriptive artifact filename was incorrectly blocked"
|
|
119
|
+
);
|
|
120
|
+
|
|
86
121
|
const staleDeepSeekState = {
|
|
87
122
|
messages: [
|
|
88
123
|
{ role: "system", content: "system" },
|
|
@@ -1385,6 +1420,30 @@ try {
|
|
|
1385
1420
|
const unifiedText = await fs.readFile(path.join(workspace, "unified-target.txt"), "utf8");
|
|
1386
1421
|
assert(unified.ok && unifiedText === "alpha\nnew\nomega\n", "unified apply_patch did not update expected file");
|
|
1387
1422
|
|
|
1423
|
+
await fs.writeFile(path.join(workspace, "hybrid-patch-target.txt"), "alpha\nold\nomega\n", "utf8");
|
|
1424
|
+
const hybridPatch = await executeWorkspaceTool(
|
|
1425
|
+
"apply_patch",
|
|
1426
|
+
{
|
|
1427
|
+
patch: [
|
|
1428
|
+
"*** Begin Patch ***",
|
|
1429
|
+
"--- a/hybrid-patch-target.txt",
|
|
1430
|
+
"+++ b/hybrid-patch-target.txt",
|
|
1431
|
+
"@@ -1,3 +1,3 @@",
|
|
1432
|
+
" alpha",
|
|
1433
|
+
"-old",
|
|
1434
|
+
"+new",
|
|
1435
|
+
" omega",
|
|
1436
|
+
"*** End Patch ***",
|
|
1437
|
+
].join("\n"),
|
|
1438
|
+
},
|
|
1439
|
+
{ commandCwd: workspace, allowFileTools: true }
|
|
1440
|
+
);
|
|
1441
|
+
const hybridPatchText = await fs.readFile(path.join(workspace, "hybrid-patch-target.txt"), "utf8");
|
|
1442
|
+
assert(
|
|
1443
|
+
hybridPatch.ok && hybridPatchText === "alpha\nnew\nomega\n",
|
|
1444
|
+
"hybrid wrapped unified apply_patch did not update expected file"
|
|
1445
|
+
);
|
|
1446
|
+
|
|
1388
1447
|
await fs.writeFile(path.join(workspace, "repair-report.md"), "old report\n", "utf8");
|
|
1389
1448
|
const ordinaryAddExistingError = await executeWorkspaceTool(
|
|
1390
1449
|
"apply_patch",
|
|
@@ -52,6 +52,13 @@ async function main() {
|
|
|
52
52
|
toolChoiceForProvider({ provider: "deepseek" }, []) === "auto",
|
|
53
53
|
"provider-neutral research routing added an unsupported named tool_choice"
|
|
54
54
|
);
|
|
55
|
+
assert(
|
|
56
|
+
toolChoiceForProvider(
|
|
57
|
+
{ provider: "deepseek" },
|
|
58
|
+
[{ role: "user", content: "Emit exactly one enabled tool call that performs the next concrete action." }]
|
|
59
|
+
) === "auto",
|
|
60
|
+
"DeepSeek thinking mode received unsupported required tool selection during recovery"
|
|
61
|
+
);
|
|
55
62
|
assert(
|
|
56
63
|
JSON.stringify(providerStructuredOutputAttempts("deepseek")) === JSON.stringify(["json_object", "prompt"]),
|
|
57
64
|
"DeepSeek structured extraction still probes an unsupported JSON Schema mode"
|
|
@@ -18,21 +18,34 @@ import {
|
|
|
18
18
|
artifactValidationScopeBlock,
|
|
19
19
|
canonicalizeVerifiedArtifactCompletion,
|
|
20
20
|
completedDeepResearchReuse,
|
|
21
|
+
completionEvidenceNeedsCommand,
|
|
22
|
+
enqueueFailedTestRepairInstruction,
|
|
21
23
|
nextStepRuntimeConfig,
|
|
24
|
+
projectAcceptanceFromMarkdown,
|
|
25
|
+
recordCanonicalGeneratedOutputProgress,
|
|
26
|
+
recordProjectVerificationOutcome,
|
|
22
27
|
recordExactOutputProgress,
|
|
23
28
|
recordStaticDiscoveryProgress,
|
|
29
|
+
resetStaticDiscoveryAfterContextLoss,
|
|
24
30
|
rememberCompletedDeepResearch,
|
|
25
31
|
repeatedNoProgressToolBlock,
|
|
26
32
|
repeatedSuccessfulMutationBlock,
|
|
27
33
|
repeatedStaticToolBlock,
|
|
34
|
+
reopenedArtifactRepairPending,
|
|
28
35
|
shouldResetStaticDiscoveryPhase,
|
|
29
36
|
} from "../src/agent-runner.js";
|
|
37
|
+
import {
|
|
38
|
+
augmentScsTaskContractWithProjectVerification,
|
|
39
|
+
buildScsEvidenceLedger,
|
|
40
|
+
evaluateScsEvidence,
|
|
41
|
+
} from "../src/scs-evidence.js";
|
|
30
42
|
import {
|
|
31
43
|
createStepBudgetState,
|
|
32
44
|
decideStepBudgetExtension,
|
|
33
45
|
isStaticDiscoveryToolCall,
|
|
34
46
|
staticToolCallSignature,
|
|
35
47
|
normalizeDynamicStepsMode,
|
|
48
|
+
shouldEvaluateResumeBoundary,
|
|
36
49
|
summarizeRepeatedStaticDiscovery,
|
|
37
50
|
} from "../src/step-budget-controller.js";
|
|
38
51
|
import { SessionStore } from "../src/session-store.js";
|
|
@@ -59,6 +72,18 @@ function toolMessage(payload) {
|
|
|
59
72
|
|
|
60
73
|
try {
|
|
61
74
|
assert(normalizeDynamicStepsMode("off") === "off", "dynamic mode off did not normalize");
|
|
75
|
+
assert(
|
|
76
|
+
completionEvidenceNeedsCommand({ missingProjectCommands: ["python analysis.py"] }),
|
|
77
|
+
"a pending canonical command did not reopen command execution"
|
|
78
|
+
);
|
|
79
|
+
assert(
|
|
80
|
+
completionEvidenceNeedsCommand({ missingGitActions: ["commit"] }),
|
|
81
|
+
"a pending git action did not reopen command execution"
|
|
82
|
+
);
|
|
83
|
+
assert(
|
|
84
|
+
!completionEvidenceNeedsCommand({ missing: [], missingProjectCommands: [], missingGitActions: [] }),
|
|
85
|
+
"satisfied completion evidence kept command execution open"
|
|
86
|
+
);
|
|
62
87
|
assert(normalizeDynamicStepsMode("always") === "on", "dynamic mode always did not normalize to on");
|
|
63
88
|
assert(normalizeDynamicStepsMode("smart") === "auto", "dynamic mode smart did not normalize to auto");
|
|
64
89
|
const inheritedBudget = createStepBudgetState(
|
|
@@ -111,6 +136,27 @@ try {
|
|
|
111
136
|
assert(uniqueDiscovery.staticTotal === 2, "duplicate reads consumed the unique convergence budget");
|
|
112
137
|
assert(uniqueDiscovery.staticCallTotal === 3, "raw static call telemetry did not retain duplicate calls");
|
|
113
138
|
assert(uniqueDiscovery.staticCounts["read_file:/reference/A.md"] === 2, "per-signature loop accounting was lost");
|
|
139
|
+
const compactedDiscoveryState = {
|
|
140
|
+
meta: {
|
|
141
|
+
toolLoop: {
|
|
142
|
+
recent: [],
|
|
143
|
+
warned: ["file-read:/reference/A.md", "run_command:keep"],
|
|
144
|
+
staticCounts: { "file-read:/reference/A.md": 1 },
|
|
145
|
+
staticOrder: ["file-read:/reference/A.md"],
|
|
146
|
+
staticTotal: 1,
|
|
147
|
+
staticCallTotal: 1,
|
|
148
|
+
convergenceAnnounced: { staticTotal: 1 },
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
resetStaticDiscoveryAfterContextLoss(compactedDiscoveryState, "smoke-compaction");
|
|
153
|
+
assert(compactedDiscoveryState.meta.toolLoop.staticTotal === 0, "context recovery kept a stale static convergence total");
|
|
154
|
+
assert(compactedDiscoveryState.meta.toolLoop.staticOrder.length === 0, "context recovery kept stale read signatures active");
|
|
155
|
+
assert(compactedDiscoveryState.meta.toolLoop.staticHistory.length === 1, "context recovery did not archive discovery telemetry");
|
|
156
|
+
assert(
|
|
157
|
+
JSON.stringify(compactedDiscoveryState.meta.toolLoop.warned) === JSON.stringify(["run_command:keep"]),
|
|
158
|
+
"context recovery did not clear only stale static-read warnings"
|
|
159
|
+
);
|
|
114
160
|
const exactReadSignature = staticToolCallSignature("read_file", { path: "/reference/A.md" }, {
|
|
115
161
|
commandCwd: workspace,
|
|
116
162
|
});
|
|
@@ -379,6 +425,35 @@ try {
|
|
|
379
425
|
deleteOutputBlock?.category === "artifact-validation-delete-output",
|
|
380
426
|
"artifact validation allowed delete-and-recreate repair of an exact output"
|
|
381
427
|
);
|
|
428
|
+
const reopenedSourceReadState = {
|
|
429
|
+
commandCwd: workspace,
|
|
430
|
+
meta: {
|
|
431
|
+
artifactProgress: {
|
|
432
|
+
exactOutputPaths: ["outputs/report.md"],
|
|
433
|
+
needsRepair: true,
|
|
434
|
+
needsSourceRead: true,
|
|
435
|
+
reopenedSourcePaths: ["analysis.py"],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
assert(
|
|
440
|
+
artifactValidationScopeBlock(
|
|
441
|
+
reopenedSourceReadState,
|
|
442
|
+
"read_file",
|
|
443
|
+
{ path: "analysis.py" },
|
|
444
|
+
{ commandCwd: workspace, artifactValidationPhase: true }
|
|
445
|
+
) === null,
|
|
446
|
+
"a correction request could not inspect its exact named source file"
|
|
447
|
+
);
|
|
448
|
+
assert(
|
|
449
|
+
artifactValidationScopeBlock(
|
|
450
|
+
reopenedSourceReadState,
|
|
451
|
+
"read_file",
|
|
452
|
+
{ path: "unrelated.py" },
|
|
453
|
+
{ commandCwd: workspace, artifactValidationPhase: true }
|
|
454
|
+
)?.category === "artifact-validation-scope",
|
|
455
|
+
"the correction source allowance leaked to unrelated files"
|
|
456
|
+
);
|
|
382
457
|
const artifactProgress = recordExactOutputProgress(
|
|
383
458
|
artifactState,
|
|
384
459
|
{
|
|
@@ -395,6 +470,28 @@ try {
|
|
|
395
470
|
nextStepRuntimeConfig({ provider: "localllm" }, artifactState).artifactValidationPhase === true,
|
|
396
471
|
"next step did not enter artifact validation mode"
|
|
397
472
|
);
|
|
473
|
+
const reopenedRepairState = {
|
|
474
|
+
meta: {
|
|
475
|
+
goalContract: { revision: 9 },
|
|
476
|
+
artifactProgress: { reopenedGoalRevision: 9, reopenedMutationRevision: 4 },
|
|
477
|
+
projectVerification: { mutationRevision: 4 },
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
assert(
|
|
481
|
+
reopenedArtifactRepairPending(reopenedRepairState),
|
|
482
|
+
"a fresh same-task correction was cleared before any source mutation"
|
|
483
|
+
);
|
|
484
|
+
reopenedRepairState.meta.projectVerification.mutationRevision = 5;
|
|
485
|
+
assert(
|
|
486
|
+
!reopenedArtifactRepairPending(reopenedRepairState),
|
|
487
|
+
"a source mutation did not satisfy the revision-scoped repair obligation"
|
|
488
|
+
);
|
|
489
|
+
reopenedRepairState.meta.projectVerification.mutationRevision = 4;
|
|
490
|
+
reopenedRepairState.meta.goalContract.revision = 10;
|
|
491
|
+
assert(
|
|
492
|
+
!reopenedArtifactRepairPending(reopenedRepairState),
|
|
493
|
+
"an old correction obligation leaked into a different goal revision"
|
|
494
|
+
);
|
|
398
495
|
artifactState.meta.artifactProgress.needsRepair = true;
|
|
399
496
|
artifactState.meta.artifactProgress.outputEmbedded = true;
|
|
400
497
|
artifactState.meta.artifactProgress.usedValidationTools = ["read_file"];
|
|
@@ -513,6 +610,27 @@ try {
|
|
|
513
610
|
) === "Completed and verified MEDIA_ROUTINE_READINESS.md.",
|
|
514
611
|
"verified completion rewrote a result that already named the exact contract output"
|
|
515
612
|
);
|
|
613
|
+
const portablePathState = {
|
|
614
|
+
commandCwd: workspace,
|
|
615
|
+
meta: {
|
|
616
|
+
artifactProgress: {
|
|
617
|
+
complete: true,
|
|
618
|
+
exactOutputPaths: [path.join(workspace, "reports", "fluorescence-dose-response-analysis.pdf")],
|
|
619
|
+
preflight: { defectCount: 0 },
|
|
620
|
+
preflightFingerprint: "passed",
|
|
621
|
+
defectCount: 0,
|
|
622
|
+
needsRepair: false,
|
|
623
|
+
needsCommand: false,
|
|
624
|
+
needsSourceRead: false,
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
const portableCompletion = canonicalizeVerifiedArtifactCompletion(portablePathState, "");
|
|
629
|
+
assert(
|
|
630
|
+
portableCompletion.includes("reports/fluorescence-dose-response-analysis.pdf") &&
|
|
631
|
+
!portableCompletion.includes(workspace),
|
|
632
|
+
"verified completion leaked an absolute private workspace path"
|
|
633
|
+
);
|
|
516
634
|
artifactState.meta.artifactProgress.needsRepair = true;
|
|
517
635
|
artifactState.meta.artifactProgress.defectCount = 2;
|
|
518
636
|
assert(
|
|
@@ -656,6 +774,48 @@ try {
|
|
|
656
774
|
},
|
|
657
775
|
{ meta: {}, stepsCompleted: 0 }
|
|
658
776
|
);
|
|
777
|
+
const migratedDefaultBudget = createStepBudgetState(
|
|
778
|
+
{
|
|
779
|
+
provider: "localllm",
|
|
780
|
+
maxSteps: 30,
|
|
781
|
+
dynamicSteps: "auto",
|
|
782
|
+
dynamicStepExtensionLimit: 1,
|
|
783
|
+
dynamicStepExtensionLimitExplicit: false,
|
|
784
|
+
scsActive: false,
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
meta: {
|
|
788
|
+
stepBudget: {
|
|
789
|
+
initialMaxSteps: 30,
|
|
790
|
+
currentMaxSteps: 40,
|
|
791
|
+
hardCap: 60,
|
|
792
|
+
extensionLimit: 1,
|
|
793
|
+
extensionsUsed: 1,
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
stepsCompleted: 40,
|
|
797
|
+
}
|
|
798
|
+
);
|
|
799
|
+
assert(
|
|
800
|
+
migratedDefaultBudget.extensionLimit === 3 && migratedDefaultBudget.extensionsUsed === 1,
|
|
801
|
+
"resumed non-explicit step budget did not adopt the current bounded default"
|
|
802
|
+
);
|
|
803
|
+
assert(
|
|
804
|
+
shouldEvaluateResumeBoundary(
|
|
805
|
+
{ resume: "existing-session" },
|
|
806
|
+
{ stepsCompleted: 40 },
|
|
807
|
+
migratedDefaultBudget
|
|
808
|
+
),
|
|
809
|
+
"a resumed session at its consumed boundary did not request bounded capacity before the loop"
|
|
810
|
+
);
|
|
811
|
+
assert(
|
|
812
|
+
!shouldEvaluateResumeBoundary(
|
|
813
|
+
{ resume: "existing-session" },
|
|
814
|
+
{ stepsCompleted: migratedDefaultBudget.hardCap },
|
|
815
|
+
{ ...migratedDefaultBudget, currentMaxSteps: migratedDefaultBudget.hardCap }
|
|
816
|
+
),
|
|
817
|
+
"a resumed session attempted to exceed its dynamic hard cap"
|
|
818
|
+
);
|
|
659
819
|
const progressDecision = decideStepBudgetExtension({
|
|
660
820
|
config: { scsActive: false },
|
|
661
821
|
budget: normalBudget,
|
|
@@ -699,6 +859,65 @@ try {
|
|
|
699
859
|
});
|
|
700
860
|
assert(!blockedDecision.approved && /permission|approval|blocked/i.test(blockedDecision.reason), "budget gate did not deny blocker loops");
|
|
701
861
|
|
|
862
|
+
const repairBudget = createStepBudgetState(
|
|
863
|
+
{
|
|
864
|
+
provider: "localllm",
|
|
865
|
+
maxSteps: 30,
|
|
866
|
+
dynamicSteps: "on",
|
|
867
|
+
dynamicStepExtensionLimit: 2,
|
|
868
|
+
scsActive: false,
|
|
869
|
+
},
|
|
870
|
+
{ meta: {}, stepsCompleted: 0 }
|
|
871
|
+
);
|
|
872
|
+
const repairDecision = decideStepBudgetExtension({
|
|
873
|
+
config: { scsActive: false, commandCwd: "/tmp/workspace" },
|
|
874
|
+
budget: repairBudget,
|
|
875
|
+
step: 29,
|
|
876
|
+
state: {
|
|
877
|
+
messages: [
|
|
878
|
+
toolMessage({
|
|
879
|
+
toolName: "run_command",
|
|
880
|
+
ok: false,
|
|
881
|
+
exitCode: 1,
|
|
882
|
+
args: { command: "python -m unittest discover -s tests" },
|
|
883
|
+
stderr: "AssertionError: expected calibrated values",
|
|
884
|
+
}),
|
|
885
|
+
toolMessage({
|
|
886
|
+
toolName: "apply_patch",
|
|
887
|
+
ok: false,
|
|
888
|
+
reason: "Patch search text was not found in analysis.py.",
|
|
889
|
+
}),
|
|
890
|
+
toolMessage({
|
|
891
|
+
toolName: "read_file",
|
|
892
|
+
ok: true,
|
|
893
|
+
args: { path: "analysis.py" },
|
|
894
|
+
path: "analysis.py",
|
|
895
|
+
}),
|
|
896
|
+
toolMessage({
|
|
897
|
+
toolName: "read_file",
|
|
898
|
+
ok: false,
|
|
899
|
+
blocked: true,
|
|
900
|
+
category: "repeated-read-only-call",
|
|
901
|
+
reason: "The same static discovery call already ran once.",
|
|
902
|
+
}),
|
|
903
|
+
],
|
|
904
|
+
},
|
|
905
|
+
events: [
|
|
906
|
+
{ type: "file.changed", data: { path: "analysis.py" } },
|
|
907
|
+
...Array.from({ length: 30 }, (_, index) => ({
|
|
908
|
+
type: index % 2 === 0 ? "snapshot.captured" : "model.requested",
|
|
909
|
+
data: {},
|
|
910
|
+
})),
|
|
911
|
+
{ type: "tool.completed", data: { toolName: "run_command", exitCode: 1 } },
|
|
912
|
+
{ type: "tool.failed", data: { toolName: "apply_patch" } },
|
|
913
|
+
{ type: "tool.blocked", data: { toolName: "read_file" } },
|
|
914
|
+
],
|
|
915
|
+
});
|
|
916
|
+
assert(
|
|
917
|
+
repairDecision.approved && repairDecision.extraSteps > 0,
|
|
918
|
+
`budget gate denied an active repair after concrete file progress: ${repairDecision.reason}`
|
|
919
|
+
);
|
|
920
|
+
|
|
702
921
|
const repeatedDiscoveryMessages = [
|
|
703
922
|
"ls -la ../Musia",
|
|
704
923
|
"ls -la ../LALACHAN",
|
|
@@ -543,6 +543,7 @@ async function verifyPackagedBinAndScriptClosure() {
|
|
|
543
543
|
assert.equal(packageJson.bin?.["aginti-integration"], "bin/aginti-integration.js");
|
|
544
544
|
assert.equal(packageJson.files.includes("bin/"), true);
|
|
545
545
|
for (const requiredFile of [
|
|
546
|
+
"scripts/check-js-syntax.js",
|
|
546
547
|
"scripts/eval-provider-attribution.js",
|
|
547
548
|
"scripts/smoke-context-budget-recovery.js",
|
|
548
549
|
"scripts/smoke-integration-production-mount.js",
|
|
@@ -553,11 +554,7 @@ async function verifyPackagedBinAndScriptClosure() {
|
|
|
553
554
|
packageJson.scripts?.["smoke:integration-production-mount"],
|
|
554
555
|
"node scripts/smoke-integration-production-mount.js"
|
|
555
556
|
);
|
|
556
|
-
assert.equal(packageJson.scripts.check
|
|
557
|
-
assert.equal(
|
|
558
|
-
packageJson.scripts.check.includes("node --check scripts/smoke-integration-production-mount.js"),
|
|
559
|
-
true
|
|
560
|
-
);
|
|
557
|
+
assert.equal(packageJson.scripts.check, "node scripts/check-js-syntax.js");
|
|
561
558
|
for (const relativePath of referencedRunnablePaths(packageJson)) {
|
|
562
559
|
const stat = await fs.stat(path.join(REPOSITORY_ROOT, relativePath));
|
|
563
560
|
assert.equal(stat.isFile(), true, `packaged script reference is missing: ${relativePath}`);
|
|
@@ -1131,5 +1128,6 @@ async function main() {
|
|
|
1131
1128
|
|
|
1132
1129
|
main().catch((error) => {
|
|
1133
1130
|
process.stderr.write(`integration production mount smoke: failed (${String(error?.code || error?.name || "ERROR")})\n`);
|
|
1131
|
+
process.stderr.write(`${String(error?.stack || error?.message || error)}\n`);
|
|
1134
1132
|
process.exitCode = 1;
|
|
1135
1133
|
});
|
|
@@ -601,7 +601,7 @@ async function runCore() {
|
|
|
601
601
|
cancellationAttestation: runtimeFixture.cancellationAttestation,
|
|
602
602
|
hardenedSandboxAttestation: runtimeFixture.hardenedSandboxAttestation,
|
|
603
603
|
});
|
|
604
|
-
const realRuntimeProof = realRuntimeAuthority.getIntegrationRuntimeProof();
|
|
604
|
+
const realRuntimeProof = await realRuntimeAuthority.getIntegrationRuntimeProof();
|
|
605
605
|
assert.equal(realRuntimeProof.repositoryProofDigest, runtimeFixture.repositoryAttestation.digest);
|
|
606
606
|
assert.equal(
|
|
607
607
|
realRuntimeProof.eventAppendProofDigest,
|