@lazyingart/agintiflow 0.20.207 → 0.20.209
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 +31 -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 +137 -0
- package/scripts/smoke-context-budget-recovery.js +4 -0
- package/scripts/smoke-deep-research.js +7 -0
- package/scripts/smoke-dynamic-step-budget.js +392 -0
- package/scripts/smoke-integration-production-mount.js +3 -5
- package/scripts/smoke-integration-retained-durable-common.js +693 -0
- package/scripts/smoke-integration-retained-event-ledger-bundle.js +771 -0
- package/scripts/smoke-integration-retained-event-ledger.js +991 -0
- package/scripts/smoke-integration-retained-file-lock.js +707 -0
- package/scripts/smoke-integration-retained-native-execution-evidence.js +2948 -0
- package/scripts/smoke-integration-retained-native-session-repository-state.js +1761 -0
- package/scripts/smoke-integration-retained-repository-kernel.js +1345 -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 +2055 -0
- package/scripts/smoke-integration-runtime-authority.js +5040 -0
- package/scripts/smoke-integration-runtime-repository-contract.js +655 -0
- package/scripts/smoke-integration-storage-authority.js +884 -0
- 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 +410 -5
- 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 +434 -12
- package/scripts/smoke-web-autostart.js +15 -1
- package/scripts/supervision-ledger.js +45 -11
- package/src/agent-runner.js +2595 -176
- package/src/artifact-tunnel.js +48 -7
- package/src/config.js +7 -2
- package/src/deep-research.js +13 -1
- package/src/integration-authority-error.js +15 -0
- package/src/integration-durable-common.js +12 -18
- package/src/integration-event-ledger-store.js +1667 -0
- package/src/integration-native-executor.js +119 -114
- package/src/integration-native-runtime-roots.js +263 -0
- package/src/integration-retained-native-execution-evidence.js +869 -0
- package/src/integration-retained-native-session-repository-state.js +3203 -0
- package/src/integration-retained-runtime-repository-surface.js +3753 -0
- package/src/integration-retained-session-state-store.js +2141 -0
- package/src/integration-retained-text-workspace.js +899 -0
- package/src/integration-retained-vision-workspace.js +1676 -0
- package/src/integration-run-registry.js +81 -5
- package/src/integration-runtime-authority.js +917 -174
- package/src/integration-runtime-process-owner-bootstrap.js +63 -0
- package/src/integration-runtime-repository-contract.js +395 -0
- package/src/integration-runtime-repository.js +1536 -0
- package/src/integration-session-persistence.js +259 -8
- package/src/integration-session-service.js +4 -4
- package/src/integration-storage-authority.js +2870 -0
- package/src/model-client.js +109 -28
- package/src/perception-tools.js +30 -9
- package/src/progressive-tool-selection.js +136 -8
- package/src/scs-controller.js +23 -4
- package/src/scs-evidence.js +442 -17
- 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 +43 -7
- package/src/tool-contract.js +59 -0
- 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.209",
|
|
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,10 +61,24 @@
|
|
|
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",
|
|
67
68
|
"scripts/smoke-integration-production-mount.js",
|
|
69
|
+
"scripts/smoke-integration-storage-authority.js",
|
|
70
|
+
"scripts/smoke-integration-retained-durable-common.js",
|
|
71
|
+
"scripts/smoke-integration-retained-file-lock.js",
|
|
72
|
+
"scripts/smoke-integration-retained-event-ledger.js",
|
|
73
|
+
"scripts/smoke-integration-retained-event-ledger-bundle.js",
|
|
74
|
+
"scripts/smoke-integration-retained-repository-kernel.js",
|
|
75
|
+
"scripts/smoke-integration-retained-session-state-store.js",
|
|
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",
|
|
80
|
+
"scripts/smoke-integration-runtime-repository-contract.js",
|
|
81
|
+
"scripts/smoke-integration-runtime-authority.js",
|
|
68
82
|
"scripts/smoke-auxiliary-tools.js",
|
|
69
83
|
"scripts/smoke-agentlink.js",
|
|
70
84
|
"scripts/smoke-auth.js",
|
|
@@ -82,6 +96,7 @@
|
|
|
82
96
|
"scripts/smoke-long-jobs.js",
|
|
83
97
|
"scripts/smoke-localllm-auto-max.js",
|
|
84
98
|
"scripts/smoke-localllm-code-routing.js",
|
|
99
|
+
"scripts/smoke-local-failure-recovery.js",
|
|
85
100
|
"scripts/smoke-local-resource-policy.js",
|
|
86
101
|
"scripts/smoke-localllm-model-tiers.js",
|
|
87
102
|
"scripts/smoke-localllm-provider.js",
|
|
@@ -130,7 +145,7 @@
|
|
|
130
145
|
"scripts": {
|
|
131
146
|
"start": "node run.js",
|
|
132
147
|
"web": "node web.js",
|
|
133
|
-
"check": "node
|
|
148
|
+
"check": "node scripts/check-js-syntax.js",
|
|
134
149
|
"setup:toolchain-docker": "scripts/setup-agent-toolchain-docker.sh",
|
|
135
150
|
"smoke:coding-tools": "node scripts/smoke-coding-tools.js",
|
|
136
151
|
"smoke:dynamic-step-budget": "node scripts/smoke-dynamic-step-budget.js",
|
|
@@ -186,10 +201,23 @@
|
|
|
186
201
|
"supervision:ledger": "node scripts/supervision-ledger.js",
|
|
187
202
|
"smoke:supervision-ledger": "node scripts/smoke-supervision-ledger.js",
|
|
188
203
|
"smoke:integration-production-mount": "node scripts/smoke-integration-production-mount.js",
|
|
204
|
+
"smoke:integration-storage-authority": "node scripts/smoke-integration-storage-authority.js",
|
|
205
|
+
"smoke:integration-retained-durable-common": "node scripts/smoke-integration-retained-durable-common.js",
|
|
206
|
+
"smoke:integration-retained-file-lock": "node scripts/smoke-integration-retained-file-lock.js",
|
|
207
|
+
"smoke:integration-retained-event-ledger": "node scripts/smoke-integration-retained-event-ledger.js",
|
|
208
|
+
"smoke:integration-retained-event-ledger-bundle": "node --experimental-test-module-mocks scripts/smoke-integration-retained-event-ledger-bundle.js",
|
|
209
|
+
"smoke:integration-retained-repository-kernel": "node --experimental-test-module-mocks scripts/smoke-integration-retained-repository-kernel.js",
|
|
210
|
+
"smoke:integration-retained-session-state-store": "node --experimental-test-module-mocks scripts/smoke-integration-retained-session-state-store.js",
|
|
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",
|
|
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",
|
|
189
217
|
"storage:migrate": "node bin/aginti-cli.js storage migrate",
|
|
190
218
|
"publish:env": "node scripts/npm-publish-from-env.js publish --access public",
|
|
191
219
|
"publish:env:whoami": "node scripts/npm-publish-from-env.js whoami",
|
|
192
|
-
"test": "npm run check && npm run smoke:integration-production-mount && 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",
|
|
193
221
|
"pack:dry-run": "npm pack --dry-run",
|
|
194
222
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
195
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,8 +5,10 @@ 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,
|
|
11
|
+
shouldResetStaticDiscoveryPhase,
|
|
10
12
|
runAgent,
|
|
11
13
|
sanitizeToolResult,
|
|
12
14
|
toolResultForModel,
|
|
@@ -18,6 +20,7 @@ import { formatBehaviorContractForPrompt } from "../src/behavior-contract.js";
|
|
|
18
20
|
import { resolveRuntimeConfig } from "../src/config.js";
|
|
19
21
|
import { readCodebaseMap } from "../src/codebase-map.js";
|
|
20
22
|
import { evaluateCommandPolicy } from "../src/command-policy.js";
|
|
23
|
+
import { shouldReviewToolResult } from "../src/scs-controller.js";
|
|
21
24
|
import {
|
|
22
25
|
engineeringGuidanceForTask,
|
|
23
26
|
recommendedMaxStepsForTask,
|
|
@@ -81,6 +84,40 @@ async function runMock(goal, sessionId, { resume = false } = {}) {
|
|
|
81
84
|
}
|
|
82
85
|
|
|
83
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
|
+
|
|
84
121
|
const staleDeepSeekState = {
|
|
85
122
|
messages: [
|
|
86
123
|
{ role: "system", content: "system" },
|
|
@@ -164,6 +201,74 @@ try {
|
|
|
164
201
|
interruptedDeepSeekState.messages.at(-1)?.content === "Continue with this new request: /review",
|
|
165
202
|
"interrupted repair dropped the new user request"
|
|
166
203
|
);
|
|
204
|
+
const deepSeekCompactionState = {
|
|
205
|
+
goal: "Continue a data repair from retained evidence.",
|
|
206
|
+
plan: "Use the verified source evidence, repair once, then test.",
|
|
207
|
+
stepsCompleted: 4,
|
|
208
|
+
meta: {},
|
|
209
|
+
messages: [
|
|
210
|
+
{ role: "system", content: "system" },
|
|
211
|
+
{ role: "user", content: "inspect the exact source" },
|
|
212
|
+
{
|
|
213
|
+
role: "assistant",
|
|
214
|
+
content: "",
|
|
215
|
+
reasoning_content: "The source must be read before editing.",
|
|
216
|
+
tool_calls: [
|
|
217
|
+
{ id: "deep-read", type: "function", function: { name: "read_file", arguments: '{"path":"analysis.py"}' } },
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
{ role: "tool", tool_call_id: "deep-read", content: '{"ok":true,"path":"analysis.py","content":"verified source"}' },
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
const deepSeekCompacted = buildModelTimeoutRetryMessages(
|
|
224
|
+
deepSeekCompactionState,
|
|
225
|
+
{ provider: "deepseek", model: "deepseek-v4-pro", contextWindowTokens: 32768 },
|
|
226
|
+
{ url: "", title: "No browser page open" },
|
|
227
|
+
5,
|
|
228
|
+
new Error("synthetic timeout")
|
|
229
|
+
);
|
|
230
|
+
assert(
|
|
231
|
+
!deepSeekCompacted.some((message) => message.role === "assistant" && Array.isArray(message.tool_calls)),
|
|
232
|
+
"DeepSeek compaction synthesized assistant tool calls without original reasoning_content"
|
|
233
|
+
);
|
|
234
|
+
assert(
|
|
235
|
+
deepSeekCompacted.some(
|
|
236
|
+
(message) => message.role === "user" && /Retained runtime tool evidence/.test(message.content) && /analysis\.py/.test(message.content) && /verified source/.test(message.content)
|
|
237
|
+
),
|
|
238
|
+
"DeepSeek compaction dropped bounded source evidence while removing synthetic tool-call messages"
|
|
239
|
+
);
|
|
240
|
+
assert(
|
|
241
|
+
!shouldReviewToolResult(
|
|
242
|
+
{ ok: true, toolName: "read_file", path: "analysis.py" },
|
|
243
|
+
{ meta: { toolLoop: { warned: ["old-read"], recent: [{ toolName: "read_file", ok: false }] } } }
|
|
244
|
+
),
|
|
245
|
+
"SCS scheduled a redundant review for a successful read because an older read failed"
|
|
246
|
+
);
|
|
247
|
+
assert(
|
|
248
|
+
shouldReviewToolResult(
|
|
249
|
+
{ ok: false, blocked: true, toolName: "read_file", reason: "exact read blocked" },
|
|
250
|
+
{ meta: { toolLoop: { warned: [], recent: [] } } }
|
|
251
|
+
),
|
|
252
|
+
"SCS stopped reviewing an exact blocked tool result"
|
|
253
|
+
);
|
|
254
|
+
assert(
|
|
255
|
+
!shouldResetStaticDiscoveryPhase({
|
|
256
|
+
ok: true,
|
|
257
|
+
toolName: "run_command",
|
|
258
|
+
args: { command: 'echo "SOURCE"; cat analysis.py; echo "DIFF"; git diff -- analysis.py' },
|
|
259
|
+
commandPolicy: { writesWorkspace: false },
|
|
260
|
+
}),
|
|
261
|
+
"composite read-only shell discovery incorrectly reset the bounded discovery phase"
|
|
262
|
+
);
|
|
263
|
+
assert(
|
|
264
|
+
shouldResetStaticDiscoveryPhase({
|
|
265
|
+
ok: true,
|
|
266
|
+
toolName: "run_command",
|
|
267
|
+
args: { command: "python analysis.py" },
|
|
268
|
+
commandPolicy: { writesWorkspace: true },
|
|
269
|
+
}),
|
|
270
|
+
"a successful workspace-writing command did not reset the discovery phase"
|
|
271
|
+
);
|
|
167
272
|
const workspaceToolConfig = {
|
|
168
273
|
commandCwd: workspace,
|
|
169
274
|
allowFileTools: true,
|
|
@@ -1111,6 +1216,14 @@ try {
|
|
|
1111
1216
|
localTimeoutRoute.model === "localllm-fast" && localTimeoutRoute.retryTimeoutMs === 90000,
|
|
1112
1217
|
"LocalLLM timeout retry did not switch to its same-boundary fast route"
|
|
1113
1218
|
);
|
|
1219
|
+
const defaultLocalTimeoutRoute = modelTimeoutRetryRoute({
|
|
1220
|
+
provider: "localllm",
|
|
1221
|
+
model: "localllm-fast",
|
|
1222
|
+
});
|
|
1223
|
+
assert(
|
|
1224
|
+
defaultLocalTimeoutRoute.timeoutMs === 300000 && defaultLocalTimeoutRoute.retryTimeoutMs === 600000,
|
|
1225
|
+
"LocalLLM default timeout did not allow bounded slow local generation and one longer retry"
|
|
1226
|
+
);
|
|
1114
1227
|
const artifactTimeoutMessages = buildModelTimeoutRetryMessages(
|
|
1115
1228
|
{
|
|
1116
1229
|
meta: {
|
|
@@ -1307,6 +1420,30 @@ try {
|
|
|
1307
1420
|
const unifiedText = await fs.readFile(path.join(workspace, "unified-target.txt"), "utf8");
|
|
1308
1421
|
assert(unified.ok && unifiedText === "alpha\nnew\nomega\n", "unified apply_patch did not update expected file");
|
|
1309
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
|
+
|
|
1310
1447
|
await fs.writeFile(path.join(workspace, "repair-report.md"), "old report\n", "utf8");
|
|
1311
1448
|
const ordinaryAddExistingError = await executeWorkspaceTool(
|
|
1312
1449
|
"apply_patch",
|
|
@@ -163,6 +163,10 @@ const runtimeMessages = buildContextBudgetCompactionMessages(
|
|
|
163
163
|
{ reason: "test context recovery" }
|
|
164
164
|
);
|
|
165
165
|
const runtimeText = runtimeMessages.map((message) => message.content || "").join("\n");
|
|
166
|
+
assert.ok(
|
|
167
|
+
estimateMessageTokens(runtimeMessages) <= 12288,
|
|
168
|
+
"runtime compaction exceeded the bounded LocalLLM retry target"
|
|
169
|
+
);
|
|
166
170
|
assert.ok(runtimeText.includes(HEAD));
|
|
167
171
|
assert.ok(runtimeText.includes(TAIL));
|
|
168
172
|
assert.ok(runtimeText.includes("latest interruption"));
|
|
@@ -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"
|