@lazyingart/agintiflow 0.20.205 → 0.20.207
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 +0 -5
- package/package.json +3 -2
- package/scripts/smoke-progressive-tool-selection.js +130 -0
- package/scripts/smoke-skills.js +7 -0
- package/scripts/smoke-supervision-ledger.js +1 -20
- package/scripts/supervision-ledger.js +11 -45
- package/skills/data-analysis/SKILL.md +3 -2
- package/src/agent-runner.js +47 -5
- package/src/integration-run-registry.js +44 -14
- package/src/integration-runtime-authority.js +686 -163
- package/src/interactive-cli.js +1 -1
- package/src/local-failure-recovery.js +194 -0
- package/src/progressive-tool-selection.js +146 -0
- package/src/skill-library.js +7 -2
- package/src/task-profiles.js +1 -1
|
@@ -38,11 +38,6 @@ 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
|
-
|
|
46
41
|
Inspect current coverage:
|
|
47
42
|
|
|
48
43
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.207",
|
|
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",
|
|
@@ -152,6 +152,7 @@
|
|
|
152
152
|
"smoke:runtime-core": "node scripts/smoke-runtime-core.js",
|
|
153
153
|
"smoke:localllm-auto-max": "node scripts/smoke-localllm-auto-max.js",
|
|
154
154
|
"smoke:localllm-code-routing": "node scripts/smoke-localllm-code-routing.js",
|
|
155
|
+
"smoke:local-failure-recovery": "node scripts/smoke-local-failure-recovery.js",
|
|
155
156
|
"smoke:local-resource-policy": "node scripts/smoke-local-resource-policy.js",
|
|
156
157
|
"smoke:context-budget-recovery": "node scripts/smoke-context-budget-recovery.js",
|
|
157
158
|
"smoke:localllm-model-tiers": "node scripts/smoke-localllm-model-tiers.js",
|
|
@@ -188,7 +189,7 @@
|
|
|
188
189
|
"storage:migrate": "node bin/aginti-cli.js storage migrate",
|
|
189
190
|
"publish:env": "node scripts/npm-publish-from-env.js publish --access public",
|
|
190
191
|
"publish:env:whoami": "node scripts/npm-publish-from-env.js whoami",
|
|
191
|
-
"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: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",
|
|
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",
|
|
192
193
|
"pack:dry-run": "npm pack --dry-run",
|
|
193
194
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
194
195
|
},
|
|
@@ -204,6 +204,136 @@ sameNames(
|
|
|
204
204
|
assert(!names(artifactValidationTools).includes("search_files"), "artifact validation phase reopened broad search");
|
|
205
205
|
assert(!names(artifactValidationTools).includes("open_url"), "artifact validation phase reopened browser discovery");
|
|
206
206
|
|
|
207
|
+
const localFailureRecoveryTools = selectProgressiveTools(allTools, {
|
|
208
|
+
config: { provider: "localllm", localFailureRecoveryActive: true },
|
|
209
|
+
goal: "Recover from repeated failed edits without restarting the task.",
|
|
210
|
+
profile: "code",
|
|
211
|
+
messages: [
|
|
212
|
+
{ role: "assistant", tool_calls: [{ id: "failed-edit", function: { name: "apply_patch", arguments: "{}" } }] },
|
|
213
|
+
{ role: "tool", tool_call_id: "failed-edit", content: '{"ok":false}' },
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
sameNames(
|
|
217
|
+
localFailureRecoveryTools,
|
|
218
|
+
["read_file", "apply_patch", "write_file", "run_command", "search_files", "inspect_project", "finish"],
|
|
219
|
+
"local failure recovery did not restore the bounded repair surface"
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const dataDiscoveryStarterTools = selectProgressiveTools(allTools, {
|
|
223
|
+
config: { provider: "localllm" },
|
|
224
|
+
goal: "Clean these experiment exports and leave a reproducible analysis.",
|
|
225
|
+
profile: "data",
|
|
226
|
+
});
|
|
227
|
+
sameNames(
|
|
228
|
+
dataDiscoveryStarterTools,
|
|
229
|
+
["inspect_project", "finish"],
|
|
230
|
+
"local data task exposed mutation tools before project inspection"
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const dataInspectionMessages = [
|
|
234
|
+
{ role: "user", content: "Goal: Clean these experiment exports and leave a reproducible analysis." },
|
|
235
|
+
{
|
|
236
|
+
role: "assistant",
|
|
237
|
+
tool_calls: [{ id: "data-inspect", function: { name: "inspect_project", arguments: '{"path":"."}' } }],
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
role: "tool",
|
|
241
|
+
tool_call_id: "data-inspect",
|
|
242
|
+
content: JSON.stringify({
|
|
243
|
+
ok: true,
|
|
244
|
+
recommendedReads: ["README.md", "tests/test_analysis.py"],
|
|
245
|
+
manifestFiles: [{ path: "README.md" }],
|
|
246
|
+
testFiles: [{ path: "tests/test_analysis.py" }],
|
|
247
|
+
topLevel: [
|
|
248
|
+
{ path: "analysis.py", type: "file" },
|
|
249
|
+
{ path: "TASK.md", type: "file" },
|
|
250
|
+
],
|
|
251
|
+
}),
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
const dataContextTools = selectProgressiveTools(allTools, {
|
|
255
|
+
config: { provider: "localllm" },
|
|
256
|
+
goal: "Clean these experiment exports and leave a reproducible analysis.",
|
|
257
|
+
profile: "data",
|
|
258
|
+
messages: dataInspectionMessages,
|
|
259
|
+
});
|
|
260
|
+
sameNames(
|
|
261
|
+
dataContextTools,
|
|
262
|
+
["read_file", "finish"],
|
|
263
|
+
"local data task exposed mutation tools before reading project contracts"
|
|
264
|
+
);
|
|
265
|
+
assertStrict.deepEqual(
|
|
266
|
+
enumFor(dataContextTools, "read_file", "path"),
|
|
267
|
+
["README.md"],
|
|
268
|
+
"local data instruction phase did not constrain read_file to discovered instruction paths"
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const dataImplementationMessages = [
|
|
272
|
+
...dataInspectionMessages,
|
|
273
|
+
{
|
|
274
|
+
role: "assistant",
|
|
275
|
+
tool_calls: [{ id: "data-readme", function: { name: "read_file", arguments: '{"path":"README.md"}' } }],
|
|
276
|
+
},
|
|
277
|
+
{ role: "tool", tool_call_id: "data-readme", content: '{"ok":true,"path":"README.md"}' },
|
|
278
|
+
];
|
|
279
|
+
const dataImplementationTools = selectProgressiveTools(allTools, {
|
|
280
|
+
config: { provider: "localllm" },
|
|
281
|
+
goal: "Clean these experiment exports and leave a reproducible analysis.",
|
|
282
|
+
profile: "data",
|
|
283
|
+
messages: dataImplementationMessages,
|
|
284
|
+
});
|
|
285
|
+
sameNames(
|
|
286
|
+
dataImplementationTools,
|
|
287
|
+
["read_file", "finish"],
|
|
288
|
+
"local data task exposed mutation tools before reading an analyzer/config/test surface"
|
|
289
|
+
);
|
|
290
|
+
assertStrict.deepEqual(
|
|
291
|
+
enumFor(dataImplementationTools, "read_file", "path"),
|
|
292
|
+
["tests/test_analysis.py", "analysis.py"],
|
|
293
|
+
"local data context phase did not constrain read_file to discovered analysis paths"
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const dataReadyMessages = [
|
|
297
|
+
...dataImplementationMessages,
|
|
298
|
+
{
|
|
299
|
+
role: "assistant",
|
|
300
|
+
tool_calls: [
|
|
301
|
+
{ id: "data-test", function: { name: "read_file", arguments: '{"path":"tests/test_analysis.py"}' } },
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
{ role: "tool", tool_call_id: "data-test", content: '{"ok":true,"path":"tests/test_analysis.py"}' },
|
|
305
|
+
];
|
|
306
|
+
const dataReadyTools = selectProgressiveTools(allTools, {
|
|
307
|
+
config: { provider: "localllm" },
|
|
308
|
+
goal: "Clean these experiment exports and leave a reproducible analysis.",
|
|
309
|
+
profile: "data",
|
|
310
|
+
messages: dataReadyMessages,
|
|
311
|
+
});
|
|
312
|
+
sameNames(dataReadyTools, LOCAL_COMPACT_CODE_TOOL_NAMES, "local data task did not unlock after bounded discovery");
|
|
313
|
+
|
|
314
|
+
const dataRecoveryBeforeDiscoveryTools = selectProgressiveTools(allTools, {
|
|
315
|
+
config: { provider: "localllm", localFailureRecoveryActive: true },
|
|
316
|
+
goal: "Recover this data task after a failed mutation.",
|
|
317
|
+
profile: "data",
|
|
318
|
+
});
|
|
319
|
+
sameNames(
|
|
320
|
+
dataRecoveryBeforeDiscoveryTools,
|
|
321
|
+
["inspect_project", "finish"],
|
|
322
|
+
"local recovery bypassed the incomplete data discovery gate"
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
const dataRecoveryAfterDiscoveryTools = selectProgressiveTools(allTools, {
|
|
326
|
+
config: { provider: "localllm", localFailureRecoveryActive: true },
|
|
327
|
+
goal: "Recover this data task after a failed mutation.",
|
|
328
|
+
profile: "data",
|
|
329
|
+
messages: dataReadyMessages,
|
|
330
|
+
});
|
|
331
|
+
sameNames(
|
|
332
|
+
dataRecoveryAfterDiscoveryTools,
|
|
333
|
+
["read_file", "apply_patch", "write_file", "run_command", "search_files", "inspect_project", "finish"],
|
|
334
|
+
"local recovery remained trapped after data discovery completed"
|
|
335
|
+
);
|
|
336
|
+
|
|
207
337
|
const artifactRepairTools = selectProgressiveTools(allTools, {
|
|
208
338
|
config: { provider: "localllm", artifactValidationPhase: true, artifactValidationNeedsRepair: true },
|
|
209
339
|
goal: "Repair the exact report from deterministic preflight evidence.",
|
package/scripts/smoke-skills.js
CHANGED
|
@@ -90,6 +90,13 @@ assert(selectedIds("debug a C# dotnet web API").includes("dotnet-csharp"), ".NET
|
|
|
90
90
|
assert(selectedIds("fix a PHP Laravel composer project").includes("php"), "PHP prompt did not select php");
|
|
91
91
|
assert(selectedIds("repair a Ruby Rails app with RSpec").includes("ruby"), "Ruby prompt did not select ruby");
|
|
92
92
|
assert(selectedIds("clean a CSV dataset and make plots").includes("data-analysis"), "data prompt did not select data-analysis");
|
|
93
|
+
const focusedDataSkills = selectedIds(
|
|
94
|
+
"these experiment exports are a mess; clean them, make a useful plot and report",
|
|
95
|
+
"data"
|
|
96
|
+
);
|
|
97
|
+
assert(focusedDataSkills.includes("data-analysis"), "explicit data profile omitted its core analysis skill");
|
|
98
|
+
assert(!focusedDataSkills.includes("novel-writing"), "explicit data profile selected unrelated novel guidance");
|
|
99
|
+
assert(!focusedDataSkills.includes("media-transcription-report"), "explicit data profile selected unrelated media guidance");
|
|
93
100
|
assert(selectedIds("convert scanned PDF EPUB and image sources to markdown with OCR").includes("source-ingestion"), "source ingestion prompt did not select source-ingestion");
|
|
94
101
|
assert(selectedIds("use json schema to fetch valid structured json for each chunk in parallel").includes("structured-json"), "structured JSON prompt did not select structured-json");
|
|
95
102
|
assert(selectedIds("write README API docs and a tutorial").includes("docs-knowledge"), "docs prompt did not select docs-knowledge");
|
|
@@ -19,17 +19,6 @@ function run(command, args = []) {
|
|
|
19
19
|
}));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function runFails(command, args, expected) {
|
|
23
|
-
try {
|
|
24
|
-
run(command, args);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
const output = `${error?.stdout || ""}\n${error?.stderr || ""}\n${error?.message || ""}`;
|
|
27
|
-
assert(expected.test(output), `unexpected ${command} failure: ${output}`);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
throw new Error(`${command} unexpectedly succeeded`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
22
|
function assert(condition, message) {
|
|
34
23
|
if (!condition) throw new Error(message);
|
|
35
24
|
}
|
|
@@ -44,14 +33,6 @@ run("scenario", [
|
|
|
44
33
|
"--prompt-quality", "normal", "--prompt", "Prepare the media chain without publishing.",
|
|
45
34
|
"--expected-outputs", "[\"readiness.md\"]", "--validation", "Verify routine paths and no external write.",
|
|
46
35
|
]);
|
|
47
|
-
runFails("test", [
|
|
48
|
-
"--id", "missing-capability", "--capability", "not-registered", "--scenario", "media-dry-run",
|
|
49
|
-
"--title", "Invalid capability reference", "--validation", "Must fail before insertion.",
|
|
50
|
-
], /Unknown capability/);
|
|
51
|
-
runFails("test", [
|
|
52
|
-
"--id", "missing-scenario", "--capability", "media-chain", "--scenario", "not-registered",
|
|
53
|
-
"--title", "Invalid scenario reference", "--validation", "Must fail before insertion.",
|
|
54
|
-
], /Unknown scenario/);
|
|
55
36
|
run("test", [
|
|
56
37
|
"--id", "media-dry-run-001", "--capability", "media-chain", "--scenario", "media-dry-run",
|
|
57
38
|
"--title", "Read-only established media routine probe", "--prompt-path", "TASK.md",
|
|
@@ -79,7 +60,7 @@ assert(status.ok, "ledger status did not succeed");
|
|
|
79
60
|
assert(fs.statSync(db).size > 0, "ledger database is empty");
|
|
80
61
|
assert(status.campaign?.id === "smoke", "campaign row was not preserved");
|
|
81
62
|
assert(status.capability_counts.some((row) => row.status === "passed_after_fix" && row.count === 1), "capability status was not updated");
|
|
82
|
-
assert(status.scenario_counts.some((row) => row.status === "
|
|
63
|
+
assert(status.scenario_counts.some((row) => row.status === "backlog" && row.count === 1), "scenario was not recorded");
|
|
83
64
|
assert(status.test_counts.some((row) => row.status === "passed_after_fix" && row.count === 1), "test result was not recorded");
|
|
84
65
|
assert(status.recent_tests[0]?.session_id === "session-smoke", "session evidence was not retained");
|
|
85
66
|
|
|
@@ -32,24 +32,6 @@ function jsonValue(value, fallback = []) {
|
|
|
32
32
|
return JSON.stringify(JSON.parse(String(value)));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function assertCampaignReference(db, table, id, campaignId, label) {
|
|
36
|
-
if (!id) return;
|
|
37
|
-
const row = db.prepare(`SELECT id FROM ${table} WHERE id=? AND campaign_id=?`).get(id, campaignId);
|
|
38
|
-
if (!row) throw new Error(`Unknown ${label} for campaign ${campaignId}: ${id}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function runTransaction(db, operation) {
|
|
42
|
-
db.exec("BEGIN IMMEDIATE");
|
|
43
|
-
try {
|
|
44
|
-
const result = operation();
|
|
45
|
-
db.exec("COMMIT");
|
|
46
|
-
return result;
|
|
47
|
-
} catch (error) {
|
|
48
|
-
db.exec("ROLLBACK");
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
35
|
function openLedger(dbPath) {
|
|
54
36
|
const resolved = path.resolve(dbPath);
|
|
55
37
|
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
@@ -149,10 +131,6 @@ function main() {
|
|
|
149
131
|
result = { scenario: id };
|
|
150
132
|
} else if (command === "test") {
|
|
151
133
|
const id = required(options, "id");
|
|
152
|
-
const capabilityId = String(options.capability || "");
|
|
153
|
-
const scenarioId = String(options.scenario || "");
|
|
154
|
-
assertCampaignReference(db, "capabilities", capabilityId, campaignId, "capability");
|
|
155
|
-
assertCampaignReference(db, "scenarios", scenarioId, campaignId, "scenario");
|
|
156
134
|
db.prepare(`INSERT INTO test_items
|
|
157
135
|
(id, campaign_id, capability_id, scenario_id, title, profile, prompt_quality,
|
|
158
136
|
prompt_path, expected_outputs_json, validation_plan, status, updated_at)
|
|
@@ -161,7 +139,7 @@ function main() {
|
|
|
161
139
|
title=excluded.title, profile=excluded.profile, prompt_quality=excluded.prompt_quality,
|
|
162
140
|
prompt_path=excluded.prompt_path, expected_outputs_json=excluded.expected_outputs_json,
|
|
163
141
|
validation_plan=excluded.validation_plan, updated_at=excluded.updated_at`).run(
|
|
164
|
-
id, campaignId,
|
|
142
|
+
id, campaignId, String(options.capability || ""), String(options.scenario || ""),
|
|
165
143
|
required(options, "title"), String(options.profile || "auto"),
|
|
166
144
|
String(options.prompt_quality || "normal"), String(options.prompt_path || ""),
|
|
167
145
|
jsonValue(options.expected_outputs), String(options.validation || ""),
|
|
@@ -188,28 +166,16 @@ function main() {
|
|
|
188
166
|
} else if (command === "finish") {
|
|
189
167
|
const id = required(options, "id");
|
|
190
168
|
const status = required(options, "status");
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
finished_at=?, updated_at=? WHERE id=? AND campaign_id=?`).run(
|
|
202
|
-
status, summary, evidence, timestamp, timestamp, id, campaignId
|
|
203
|
-
);
|
|
204
|
-
if (item.capability_id) {
|
|
205
|
-
db.prepare(`UPDATE capabilities SET status=?, last_test_id=?, updated_at=?
|
|
206
|
-
WHERE id=? AND campaign_id=?`).run(status, id, timestamp, item.capability_id, campaignId);
|
|
207
|
-
}
|
|
208
|
-
if (item.scenario_id) {
|
|
209
|
-
db.prepare("UPDATE scenarios SET status=?, updated_at=? WHERE id=? AND campaign_id=?")
|
|
210
|
-
.run(status, timestamp, item.scenario_id, campaignId);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
169
|
+
const changed = db.prepare(`UPDATE test_items SET status=?, result_summary=?, evidence_json=?,
|
|
170
|
+
finished_at=?, updated_at=? WHERE id=? AND campaign_id=?`).run(
|
|
171
|
+
status, required(options, "summary"), jsonValue(options.evidence), timestamp, timestamp, id, campaignId
|
|
172
|
+
);
|
|
173
|
+
if (Number(changed.changes || 0) !== 1) throw new Error(`Unknown test item: ${id}`);
|
|
174
|
+
const item = db.prepare("SELECT capability_id FROM test_items WHERE id=?").get(id);
|
|
175
|
+
if (item?.capability_id) {
|
|
176
|
+
db.prepare("UPDATE capabilities SET status=?, last_test_id=?, updated_at=? WHERE id=?")
|
|
177
|
+
.run(status, id, timestamp, item.capability_id);
|
|
178
|
+
}
|
|
213
179
|
result = { test: id, status };
|
|
214
180
|
} else if (command === "fix") {
|
|
215
181
|
const id = required(options, "id");
|
|
@@ -23,7 +23,8 @@ tools:
|
|
|
23
23
|
---
|
|
24
24
|
# Data Analysis And ETL
|
|
25
25
|
|
|
26
|
-
Preserve raw
|
|
26
|
+
Preserve raw/source/input exports as immutable evidence unless the user explicitly authorizes an in-place source change. Start with `inspect_project`, then read project instructions plus the relevant existing analyzer, configuration, and tests before editing or running ad hoc calculations. Inspect schemas, row counts, missing values, duplicate rows, units, aliases, time zones, and suspicious outliers before writing conclusions.
|
|
27
27
|
|
|
28
|
-
Prefer
|
|
28
|
+
Prefer the project's existing reproducible analyzer over one-off chat math; repair or extend it when needed. Save cleaned data, plots, and reports at the exact declared output paths. Keep machine outputs separate from raw inputs and make the report explain methods, evidence, limitations, and the practical result.
|
|
29
29
|
|
|
30
|
+
After an edit or patch conflict, reread the exact target and error before choosing a different bounded repair. Never repeat an unchanged failing patch. Rerun the smallest relevant check, then the full analysis and project tests. Verify cleaned schemas and row counts, inspect plot metadata or pixels, remove only generated caches, and do not claim success until every requested artifact exists and can be regenerated.
|
package/src/agent-runner.js
CHANGED
|
@@ -61,6 +61,12 @@ import {
|
|
|
61
61
|
resolveLocalCodeRoute,
|
|
62
62
|
restoreLocalCodePolicy,
|
|
63
63
|
} from "./local-code-routing.js";
|
|
64
|
+
import {
|
|
65
|
+
activateLocalFailureRecovery,
|
|
66
|
+
applyLocalFailureRecovery,
|
|
67
|
+
decideLocalFailureRecovery,
|
|
68
|
+
localFailureRecoveryInstruction,
|
|
69
|
+
} from "./local-failure-recovery.js";
|
|
64
70
|
import { resolveRuntimeConfig } from "./config.js";
|
|
65
71
|
import {
|
|
66
72
|
captureSessionRuntime,
|
|
@@ -2526,9 +2532,10 @@ export function recordExactOutputProgress(state = {}, toolResult = {}, config =
|
|
|
2526
2532
|
}
|
|
2527
2533
|
|
|
2528
2534
|
export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
2535
|
+
const runtimeConfig = applyLocalFailureRecovery(config, state);
|
|
2529
2536
|
if (state.meta?.artifactProgress?.complete) {
|
|
2530
2537
|
return {
|
|
2531
|
-
...
|
|
2538
|
+
...runtimeConfig,
|
|
2532
2539
|
artifactValidationPhase: true,
|
|
2533
2540
|
convergenceOutputPhase: false,
|
|
2534
2541
|
artifactValidationNeedsRepair: state.meta.artifactProgress.needsRepair === true,
|
|
@@ -2542,10 +2549,10 @@ export function nextStepRuntimeConfig(config = {}, state = {}) {
|
|
|
2542
2549
|
};
|
|
2543
2550
|
}
|
|
2544
2551
|
const staticTotal = Number(state.meta?.toolLoop?.staticTotal || 0);
|
|
2545
|
-
if (staticTotal < STATIC_DISCOVERY_CONVERGENCE_LIMIT) return
|
|
2552
|
+
if (staticTotal < STATIC_DISCOVERY_CONVERGENCE_LIMIT) return runtimeConfig;
|
|
2546
2553
|
const requiresPerSourceChecks = state.meta?.scs?.taskContract?.requiresPerSourceChecks === true;
|
|
2547
2554
|
return {
|
|
2548
|
-
...
|
|
2555
|
+
...runtimeConfig,
|
|
2549
2556
|
convergenceOutputPhase: true,
|
|
2550
2557
|
convergenceAllowRunCommand: requiresPerSourceChecks,
|
|
2551
2558
|
};
|
|
@@ -4168,11 +4175,15 @@ async function recordToolContractViolation({ config, state, store, observers, va
|
|
|
4168
4175
|
total: totalViolationCount,
|
|
4169
4176
|
lastCode: validation.code || "TOOL_CALL_INVALID",
|
|
4170
4177
|
};
|
|
4178
|
+
const localRecovery = decideLocalFailureRecovery(config, state);
|
|
4179
|
+
const deferStopToLocalRecovery =
|
|
4180
|
+
violationCount >= 2 && localRecovery.active === true && localRecovery.activated === true;
|
|
4171
4181
|
const result = {
|
|
4172
4182
|
ok: false,
|
|
4173
4183
|
blocked: true,
|
|
4174
|
-
recoverable: violationCount < 2,
|
|
4175
|
-
stopRun: violationCount >= 2,
|
|
4184
|
+
recoverable: violationCount < 2 || deferStopToLocalRecovery,
|
|
4185
|
+
stopRun: violationCount >= 2 && !deferStopToLocalRecovery,
|
|
4186
|
+
localFailureRecoveryPending: deferStopToLocalRecovery,
|
|
4176
4187
|
violationCount,
|
|
4177
4188
|
reason: validation.reason || "The model returned an invalid tool call and it was not dispatched.",
|
|
4178
4189
|
category: "tool-contract-violation",
|
|
@@ -5030,6 +5041,37 @@ export async function runAgent(config) {
|
|
|
5030
5041
|
const step = state.stepsCompleted + 1;
|
|
5031
5042
|
throwIfAborted(config);
|
|
5032
5043
|
await injectQueuedUserMessages(store, state, observers);
|
|
5044
|
+
const localFailureRecovery = activateLocalFailureRecovery(config, state);
|
|
5045
|
+
if (localFailureRecovery.active) {
|
|
5046
|
+
config = applyLocalFailureRecovery(config, state);
|
|
5047
|
+
}
|
|
5048
|
+
if (localFailureRecovery.activated) {
|
|
5049
|
+
state.provider = config.provider;
|
|
5050
|
+
state.model = config.model;
|
|
5051
|
+
state.meta.runtimeConfig = captureSessionRuntime(config, {
|
|
5052
|
+
revision: state.meta.runtimeConfig?.revision || 1,
|
|
5053
|
+
});
|
|
5054
|
+
const recoveryInstruction = localFailureRecoveryInstruction(localFailureRecovery);
|
|
5055
|
+
state.messages.push({ role: "user", content: recoveryInstruction });
|
|
5056
|
+
const detail = {
|
|
5057
|
+
step,
|
|
5058
|
+
fromModel: localFailureRecovery.fromModel,
|
|
5059
|
+
model: localFailureRecovery.model,
|
|
5060
|
+
failureCount: localFailureRecovery.failureCount,
|
|
5061
|
+
repeatedSignatureCount: localFailureRecovery.repeatedSignatureCount,
|
|
5062
|
+
contractViolationCount: localFailureRecovery.contractViolationCount || 0,
|
|
5063
|
+
failedTools: localFailureRecovery.failedTools || [],
|
|
5064
|
+
reason: localFailureRecovery.reason,
|
|
5065
|
+
};
|
|
5066
|
+
await store.appendEvent("provider.local_failure_recovery", detail);
|
|
5067
|
+
observers.event("provider.local_failure_recovery", detail);
|
|
5068
|
+
emitConsole(
|
|
5069
|
+
config,
|
|
5070
|
+
`Local route recovery: ${localFailureRecovery.fromModel} -> ${localFailureRecovery.model} after repeated tool failures.`,
|
|
5071
|
+
{ kind: "meta" }
|
|
5072
|
+
);
|
|
5073
|
+
await store.saveState(state);
|
|
5074
|
+
}
|
|
5033
5075
|
const snapshot = await buildSnapshot(browserState, store, step, config);
|
|
5034
5076
|
state.meta.lastUrl = snapshot.url || state.meta.lastUrl;
|
|
5035
5077
|
await saveBrowserState(browserState, store).catch(() => {});
|
|
@@ -4,9 +4,15 @@ import {
|
|
|
4
4
|
validateIntegrationThreadId,
|
|
5
5
|
} from "./integration-policy.js";
|
|
6
6
|
import { authorityFail } from "./integration-durable-common.js";
|
|
7
|
+
import { types as utilTypes } from "node:util";
|
|
7
8
|
|
|
8
9
|
export const INTEGRATION_RUN_REGISTRY_ATTESTATION_VERSION = "aginti-live-run-registry-v1";
|
|
9
10
|
|
|
11
|
+
const PromisePrototype = Promise.prototype;
|
|
12
|
+
const PromisePrototypeThen = Promise.prototype.then;
|
|
13
|
+
const ReflectApply = Reflect.apply;
|
|
14
|
+
const ReflectOwnKeys = Reflect.ownKeys;
|
|
15
|
+
|
|
10
16
|
function assertPrincipalId(value) {
|
|
11
17
|
if (typeof value !== "string" || !/^[A-Za-z0-9._~-]{16,128}$/u.test(value)) {
|
|
12
18
|
authorityFail("INVALID_PRINCIPAL", "Integration principal scope is invalid.", { status: 401 });
|
|
@@ -53,6 +59,27 @@ export function createIntegrationRunRegistry() {
|
|
|
53
59
|
const runs = new Map();
|
|
54
60
|
const threadActiveRun = new Map();
|
|
55
61
|
|
|
62
|
+
function releaseExactRecord(runId, record) {
|
|
63
|
+
const current = runs.get(runId);
|
|
64
|
+
if (current !== record) return Object.freeze({ released: false });
|
|
65
|
+
runs.delete(runId);
|
|
66
|
+
if (threadActiveRun.get(record.threadId) === runId) threadActiveRun.delete(record.threadId);
|
|
67
|
+
return Object.freeze({ released: true, runId });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function assertNativePromise(promise) {
|
|
71
|
+
if (promise && (typeof promise === "object" || typeof promise === "function") && utilTypes.isProxy(promise)) {
|
|
72
|
+
authorityFail("AGENT_UNAVAILABLE", "Live run registry requires a native run promise.");
|
|
73
|
+
}
|
|
74
|
+
if (!utilTypes.isPromise(promise) || Object.getPrototypeOf(promise) !== PromisePrototype) {
|
|
75
|
+
authorityFail("AGENT_UNAVAILABLE", "Live run registry requires a native run promise.");
|
|
76
|
+
}
|
|
77
|
+
if (ReflectOwnKeys(promise).length !== 0) {
|
|
78
|
+
authorityFail("AGENT_UNAVAILABLE", "Live run registry promise must not expose own fields.");
|
|
79
|
+
}
|
|
80
|
+
return promise;
|
|
81
|
+
}
|
|
82
|
+
|
|
56
83
|
function claimRun(claimInput = {}) {
|
|
57
84
|
const claim = normalizeClaim(claimInput);
|
|
58
85
|
const active = threadActiveRun.get(claim.threadId);
|
|
@@ -83,18 +110,23 @@ export function createIntegrationRunRegistry() {
|
|
|
83
110
|
const runId = validateIntegrationRunId(runIdInput);
|
|
84
111
|
const record = runs.get(runId);
|
|
85
112
|
if (!record) notFound("Run");
|
|
86
|
-
if (
|
|
87
|
-
authorityFail("
|
|
113
|
+
if (record.promise) {
|
|
114
|
+
authorityFail("RUN_CONFLICT", "Integration run already has a native run promise.", { status: 409 });
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const nativePromise = assertNativePromise(promise);
|
|
118
|
+
const release = () => {
|
|
119
|
+
releaseExactRecord(runId, record);
|
|
120
|
+
};
|
|
121
|
+
const observer = ReflectApply(PromisePrototypeThen, nativePromise, [release, release]);
|
|
122
|
+
ReflectApply(PromisePrototypeThen, observer, [undefined, () => {}]);
|
|
123
|
+
record.promise = nativePromise;
|
|
124
|
+
return nativePromise;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
releaseExactRecord(runId, record);
|
|
127
|
+
if (error?.code || error?.publicCode) throw error;
|
|
128
|
+
authorityFail("AGENT_UNAVAILABLE", "Live run registry could not attach the native run promise.");
|
|
88
129
|
}
|
|
89
|
-
record.promise = promise.finally(() => {
|
|
90
|
-
const current = runs.get(runId);
|
|
91
|
-
if (current === record) {
|
|
92
|
-
runs.delete(runId);
|
|
93
|
-
if (threadActiveRun.get(record.threadId) === runId) threadActiveRun.delete(record.threadId);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
record.promise.catch(() => {});
|
|
97
|
-
return record.promise;
|
|
98
130
|
}
|
|
99
131
|
|
|
100
132
|
function getRun(runIdInput, scope = {}) {
|
|
@@ -135,9 +167,7 @@ export function createIntegrationRunRegistry() {
|
|
|
135
167
|
const runId = validateIntegrationRunId(runIdInput);
|
|
136
168
|
const record = runs.get(runId);
|
|
137
169
|
if (!record) return Object.freeze({ released: false });
|
|
138
|
-
|
|
139
|
-
if (threadActiveRun.get(record.threadId) === runId) threadActiveRun.delete(record.threadId);
|
|
140
|
-
return Object.freeze({ released: true, runId });
|
|
170
|
+
return releaseExactRecord(runId, record);
|
|
141
171
|
}
|
|
142
172
|
|
|
143
173
|
function hasActiveThreadRun(threadIdInput) {
|