mixdog 0.9.130 → 0.9.132
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/package.json +2 -2
- package/scripts/fixtures/patch-replay-corpus.json +9 -9
- package/scripts/reduction-trace-report.mjs +29 -0
- package/scripts/release-timing-report.mjs +112 -0
- package/scripts/tool-failures.mjs +36 -0
- package/scripts/tool-stress.mjs +2 -2
- package/src/lib/rules-builder.cjs +0 -3
- package/src/rules/lead/01-general.md +1 -9
- package/src/rules/shared/01-tool.md +76 -45
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -10
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +74 -11
- package/src/runtime/agent/orchestrator/agent-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +212 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +2100 -0
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +615 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +65 -35
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +35 -6
- package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +142 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +16 -5
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +64 -52
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +12 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/evidence-union.mjs +411 -0
- package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +157 -0
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +68 -11
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +122 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +8 -9
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +24 -2
- package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +124 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +17 -14
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/provider-cache-key.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +3 -6
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +47 -2
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +18 -6
- package/src/runtime/agent/orchestrator/session/reduction-metrics.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +87 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -4
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +55 -37
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +93 -2
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +141 -22
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +111 -71
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +31 -17
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +76 -210
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +81 -44
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +57 -23
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +56 -0
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +134 -30
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +595 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +132 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +74 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +64 -0
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +8 -5
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +132 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +90 -10
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +167 -25
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +21 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +33 -8
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +17 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +9 -15
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +40 -51
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +115 -56
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +0 -106
- package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +18 -63
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +12 -57
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +6 -47
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +5 -85
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +8 -15
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +20 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +23 -18
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +92 -116
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +5 -45
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +0 -17
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +15 -16
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/tool-batch-trace.mjs +7 -0
- package/src/runtime/channels/lib/session-discovery.mjs +66 -1
- package/src/runtime/channels/lib/session-discovery.test.mjs +32 -0
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -1
- package/src/runtime/shared/agent-route-config.mjs +3 -0
- package/src/runtime/shared/background-tasks.mjs +0 -4
- package/src/runtime/shared/child-guardian.mjs +14 -0
- package/src/runtime/shared/edit-tool-dialect.mjs +30 -0
- package/src/runtime/shared/pristine-execution-contract.json +1 -0
- package/src/runtime/shared/schedule-model-ref.mjs +9 -5
- package/src/runtime/shared/schedule-session-run.mjs +2 -0
- package/src/runtime/shared/session-shard-health.mjs +89 -0
- package/src/runtime/shared/session-shard-health.test.mjs +30 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-surface.mjs +5 -0
- package/src/runtime/shared/webhook-session-run.mjs +8 -1
- package/src/session-runtime/config-helpers.mjs +18 -1
- package/src/session-runtime/lifecycle-api.mjs +13 -2
- package/src/session-runtime/model-capabilities.mjs +24 -2
- package/src/session-runtime/model-recency.mjs +11 -0
- package/src/session-runtime/model-route-api.mjs +37 -6
- package/src/session-runtime/model-settings-persist.test.mjs +6 -1
- package/src/session-runtime/native-search.mjs +1 -0
- package/src/session-runtime/provider-auth-api.mjs +13 -11
- package/src/session-runtime/provider-models.mjs +8 -4
- package/src/session-runtime/runtime-core.mjs +6 -31
- package/src/session-runtime/session-lifecycle.mjs +13 -1
- package/src/session-runtime/session-turn-api.mjs +1 -13
- package/src/session-runtime/tool-catalog-data.mjs +8 -5
- package/src/session-runtime/tool-catalog-schema.mjs +2 -3
- package/src/session-runtime/tool-catalog.mjs +6 -1
- package/src/session-runtime/tool-defs.mjs +0 -28
- package/src/session-runtime/tool-policy-surface.test.mjs +32 -0
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/workflow-agents-api.mjs +9 -1
- package/src/session-runtime/workflow.mjs +5 -0
- package/src/standalone/agent-tool/lead-worker-index.mjs +144 -0
- package/src/standalone/agent-tool/notify.mjs +6 -2
- package/src/standalone/agent-tool/tag-registry.mjs +9 -1
- package/src/standalone/agent-tool/tool-def.mjs +1 -0
- package/src/standalone/agent-tool/worker-index.mjs +13 -12
- package/src/standalone/agent-tool.mjs +3 -0
- package/src/standalone/channel-client.mjs +18 -6
- package/src/standalone/channel-restart.test.mjs +163 -0
- package/src/standalone/channel-transport.mjs +54 -15
- package/src/standalone/channel-worker.mjs +6 -2
- package/src/standalone/provider-admin.mjs +14 -4
- package/src/standalone/session-runtime-pool-health.test.mjs +85 -0
- package/src/standalone/session-runtime-pool.mjs +22 -3
- package/src/standalone/session-runtime-worker.mjs +35 -0
- package/src/standalone/usage-dashboard.mjs +11 -1
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/model-picker.mjs +64 -11
- package/src/tui/app/provider-setup-picker.mjs +32 -28
- package/src/tui/dist/index.mjs +111 -40
- package/src/tui/session/agent-envelope.mjs +1 -1
- package/src/tui/session/context-state.mjs +1 -0
- package/src/tui/session/session-api-ext.mjs +2 -0
- package/src/tui/session/session-api.mjs +1 -1
- package/src/tui/session/session-flow.mjs +32 -19
- package/src/workflows/default/WORKFLOW.md +3 -0
- package/src/workflows/solo/WORKFLOW.md +3 -1
- package/src/rules/lead/lead-tool.md +0 -3
- package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +0 -98
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +0 -279
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixdog",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.132",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone mixdog coding-agent CLI/TUI workspace.",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"test:tool-contracts": "npm run build:spawn:test && node scripts/tool-smoke.mjs",
|
|
60
60
|
"smoke:patch": "node scripts/apply-patch-edit-smoke.mjs",
|
|
61
61
|
"test:shellhardening": "npm run build:spawn:test && node --test scripts/suite-shellhardening-test.mjs scripts/suite-shell-direct-exe-test.mjs",
|
|
62
|
-
"test:providers": "node --test scripts/provider-toolcall-test.mjs scripts/provider-contract-test.mjs scripts/provider-stream-outcome-test.mjs",
|
|
62
|
+
"test:providers": "node --test scripts/provider-toolcall-test.mjs scripts/provider-contract-test.mjs scripts/provider-stream-outcome-test.mjs scripts/cursor-provider-test.mjs",
|
|
63
63
|
"test:embedding-runtime:warmup": "node scripts/verify-embedding-runtime.mjs --warmup",
|
|
64
64
|
"test:release-assets": "node --check scripts/verify-release-assets.mjs && node --check scripts/release-gate-test.mjs && node --check scripts/prepare-native-assets.mjs && node --test scripts/release-gate-test.mjs scripts/prepare-native-assets-test.mjs",
|
|
65
65
|
"test:native-edit-wire": "node scripts/native-edit-wire-test.mjs",
|
|
@@ -169,9 +169,9 @@
|
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
"id": "add-overwrite-crlf",
|
|
172
|
-
"note": "Add File
|
|
173
|
-
"expect": "
|
|
174
|
-
"
|
|
172
|
+
"note": "Add File must reject an existing file without changing its CRLF content",
|
|
173
|
+
"expect": "rejected",
|
|
174
|
+
"expect_error": "Add File target already exists",
|
|
175
175
|
"file_snapshots": { "a.txt": "old\r\n" },
|
|
176
176
|
"args": { "patch": "*** Begin Patch\n*** Add File: a.txt\n+NEW\n*** End Patch\n" }
|
|
177
177
|
},
|
|
@@ -194,24 +194,24 @@
|
|
|
194
194
|
{
|
|
195
195
|
"id": "already-applied-insert",
|
|
196
196
|
"note": "observed: re-sent insertion whose unique new-side is already on disk",
|
|
197
|
-
"expect": "
|
|
198
|
-
"
|
|
197
|
+
"expect": "rejected",
|
|
198
|
+
"expect_error": "context not found",
|
|
199
199
|
"file_snapshots": { "a.js": "const STATUS = {\n pending: 1,\n archived: 2,\n}\n" },
|
|
200
200
|
"args": { "patch": "*** Begin Patch\n*** Update File: a.js\n@@\n const STATUS = {\n pending: 1,\n+ archived: 2,\n }\n*** End Patch\n" }
|
|
201
201
|
},
|
|
202
202
|
{
|
|
203
203
|
"id": "already-applied-replace",
|
|
204
204
|
"note": "observed: re-sent replacement whose unique new-side is already on disk",
|
|
205
|
-
"expect": "
|
|
206
|
-
"
|
|
205
|
+
"expect": "rejected",
|
|
206
|
+
"expect_error": "context not found",
|
|
207
207
|
"file_snapshots": { "a.js": "head\nreturn ALL.has(v) || v === 'core' || v === 'lineage'\ntail\n" },
|
|
208
208
|
"args": { "patch": "*** Begin Patch\n*** Update File: a.js\n@@\n head\n-return ALL.has(v) || v === 'core'\n+return ALL.has(v) || v === 'core' || v === 'lineage'\n tail\n*** End Patch\n" }
|
|
209
209
|
},
|
|
210
210
|
{
|
|
211
211
|
"id": "already-applied-mixed-remaining",
|
|
212
212
|
"note": "already-applied hunks skip; a later unique old-side hunk still applies",
|
|
213
|
-
"expect": "
|
|
214
|
-
"
|
|
213
|
+
"expect": "rejected",
|
|
214
|
+
"expect_error": "context not found",
|
|
215
215
|
"file_snapshots": { "a.js": "const STATUS = {\n pending: 1,\n archived: 2,\n}\n// pending/active only\n" },
|
|
216
216
|
"args": { "patch": "*** Begin Patch\n*** Update File: a.js\n@@\n const STATUS = {\n pending: 1,\n+ archived: 2,\n }\n@@\n-// pending/active only\n+// lineage backfill\n*** End Patch\n" }
|
|
217
217
|
},
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { summarizeReductionTraceRows } from '../src/runtime/agent/orchestrator/session/reduction-metrics.mjs';
|
|
6
|
+
|
|
7
|
+
export function parseTraceJsonl(text) {
|
|
8
|
+
const rows = [];
|
|
9
|
+
for (const line of String(text || '').split(/\r?\n/)) {
|
|
10
|
+
if (!line.trim()) continue;
|
|
11
|
+
try { rows.push(JSON.parse(line)); } catch { /* retain valid rows */ }
|
|
12
|
+
}
|
|
13
|
+
return rows;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function main(argv) {
|
|
17
|
+
const tracePath = argv[2];
|
|
18
|
+
if (!tracePath) {
|
|
19
|
+
process.stderr.write('Usage: node scripts/reduction-trace-report.mjs <agent-trace.jsonl>\n');
|
|
20
|
+
return 2;
|
|
21
|
+
}
|
|
22
|
+
const rows = parseTraceJsonl(readFileSync(resolve(tracePath), 'utf8'));
|
|
23
|
+
process.stdout.write(`${JSON.stringify(summarizeReductionTraceRows(rows), null, 2)}\n`);
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (resolve(process.argv[1] || '') === fileURLToPath(import.meta.url)) {
|
|
28
|
+
process.exitCode = main(process.argv);
|
|
29
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { appendFile, readFile } from 'node:fs/promises';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
|
|
7
|
+
function payloadJobs(payload) {
|
|
8
|
+
const pages = Array.isArray(payload) ? payload : [payload];
|
|
9
|
+
return pages.flatMap((page) => Array.isArray(page?.jobs) ? page.jobs : []);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function elapsedSeconds(startedAt, completedAt) {
|
|
13
|
+
const start = Date.parse(startedAt || '');
|
|
14
|
+
const end = Date.parse(completedAt || '');
|
|
15
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return null;
|
|
16
|
+
return Math.round((end - start) / 1000);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function timingRows(payload) {
|
|
20
|
+
return payloadJobs(payload).flatMap((job) => (job.steps || []).flatMap((step) => {
|
|
21
|
+
const seconds = elapsedSeconds(step.started_at, step.completed_at);
|
|
22
|
+
if (seconds === null) return [];
|
|
23
|
+
return [{
|
|
24
|
+
key: `${job.name} / ${step.name}`,
|
|
25
|
+
job: job.name,
|
|
26
|
+
step: step.name,
|
|
27
|
+
seconds,
|
|
28
|
+
}];
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function workflowSeconds(payload) {
|
|
33
|
+
const jobs = payloadJobs(payload);
|
|
34
|
+
const starts = jobs.map((job) => Date.parse(job.started_at || '')).filter(Number.isFinite);
|
|
35
|
+
const ends = jobs.map((job) => Date.parse(job.completed_at || '')).filter(Number.isFinite);
|
|
36
|
+
if (starts.length === 0 || ends.length === 0) return null;
|
|
37
|
+
return Math.round((Math.max(...ends) - Math.min(...starts)) / 1000);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function percentChange(current, baseline) {
|
|
41
|
+
if (!(baseline > 0)) return null;
|
|
42
|
+
return Math.round(((current - baseline) / baseline) * 100);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function buildReleaseTimingReport(currentPayload, baselinePayload = []) {
|
|
46
|
+
const currentRows = timingRows(currentPayload);
|
|
47
|
+
const baselineRows = timingRows(baselinePayload);
|
|
48
|
+
const baselineByKey = new Map(baselineRows.map((row) => [row.key, row]));
|
|
49
|
+
const regressions = currentRows.flatMap((row) => {
|
|
50
|
+
const baseline = baselineByKey.get(row.key);
|
|
51
|
+
if (!baseline) return [];
|
|
52
|
+
const percent = percentChange(row.seconds, baseline.seconds);
|
|
53
|
+
if (percent === null || percent <= 10 || row.seconds - baseline.seconds < 15) return [];
|
|
54
|
+
return [{ ...row, baselineSeconds: baseline.seconds, percent }];
|
|
55
|
+
}).sort((a, b) => b.percent - a.percent || b.seconds - a.seconds);
|
|
56
|
+
|
|
57
|
+
const currentWorkflowSeconds = workflowSeconds(currentPayload);
|
|
58
|
+
const baselineWorkflowSeconds = workflowSeconds(baselinePayload);
|
|
59
|
+
const workflowPercent = currentWorkflowSeconds !== null && baselineWorkflowSeconds !== null
|
|
60
|
+
? percentChange(currentWorkflowSeconds, baselineWorkflowSeconds)
|
|
61
|
+
: null;
|
|
62
|
+
const slowest = [...currentRows].sort((a, b) => b.seconds - a.seconds).slice(0, 15);
|
|
63
|
+
const markdown = [
|
|
64
|
+
'## Release timing',
|
|
65
|
+
'',
|
|
66
|
+
`- Current workflow span: ${currentWorkflowSeconds ?? 'n/a'}s`,
|
|
67
|
+
`- Previous successful span: ${baselineWorkflowSeconds ?? 'n/a'}s`,
|
|
68
|
+
`- Material step regressions (>10% and ≥15s): ${regressions.length}`,
|
|
69
|
+
'',
|
|
70
|
+
'| Slowest step | Current | Previous | Change |',
|
|
71
|
+
'| --- | ---: | ---: | ---: |',
|
|
72
|
+
...slowest.map((row) => {
|
|
73
|
+
const baseline = baselineByKey.get(row.key);
|
|
74
|
+
const change = baseline ? percentChange(row.seconds, baseline.seconds) : null;
|
|
75
|
+
return `| ${row.key.replaceAll('|', '\\|')} | ${row.seconds}s | ${baseline ? `${baseline.seconds}s` : 'n/a'} | ${change === null ? 'n/a' : `${change > 0 ? '+' : ''}${change}%`} |`;
|
|
76
|
+
}),
|
|
77
|
+
'',
|
|
78
|
+
].join('\n');
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
currentWorkflowSeconds,
|
|
82
|
+
baselineWorkflowSeconds,
|
|
83
|
+
workflowPercent,
|
|
84
|
+
regressions,
|
|
85
|
+
markdown,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function commandValue(value) {
|
|
90
|
+
return String(value).replaceAll('%', '%25').replaceAll('\r', '%0D').replaceAll('\n', '%0A');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const invokedPath = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : '';
|
|
94
|
+
if (invokedPath === import.meta.url) {
|
|
95
|
+
const currentPath = process.argv[2];
|
|
96
|
+
const baselinePath = process.argv[3];
|
|
97
|
+
if (!currentPath) throw new Error('Usage: release-timing-report.mjs <current-jobs.json> [baseline-jobs.json]');
|
|
98
|
+
const current = JSON.parse(await readFile(currentPath, 'utf8'));
|
|
99
|
+
const baseline = baselinePath ? JSON.parse(await readFile(baselinePath, 'utf8')) : [];
|
|
100
|
+
const report = buildReleaseTimingReport(current, baseline);
|
|
101
|
+
if (process.env.GITHUB_STEP_SUMMARY) {
|
|
102
|
+
await appendFile(process.env.GITHUB_STEP_SUMMARY, `${report.markdown}\n`);
|
|
103
|
+
}
|
|
104
|
+
if (report.workflowPercent > 10
|
|
105
|
+
&& report.currentWorkflowSeconds - report.baselineWorkflowSeconds >= 30) {
|
|
106
|
+
console.log(`::warning title=Release duration regression::Workflow span increased ${report.workflowPercent}% to ${report.currentWorkflowSeconds}s`);
|
|
107
|
+
}
|
|
108
|
+
for (const row of report.regressions) {
|
|
109
|
+
console.log(`::warning title=Release step regression::${commandValue(row.key)} increased ${row.percent}% (${row.baselineSeconds}s to ${row.seconds}s)`);
|
|
110
|
+
}
|
|
111
|
+
console.log(`Release timing recorded: ${report.currentWorkflowSeconds ?? 'n/a'}s, ${report.regressions.length} material step regressions.`);
|
|
112
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
|
+
import { classifyToolFailure } from '../src/runtime/agent/orchestrator/agent-trace-format.mjs';
|
|
5
6
|
|
|
6
7
|
function argValue(name, fallback = null) {
|
|
7
8
|
const idx = process.argv.indexOf(name);
|
|
@@ -87,9 +88,40 @@ function rowCategory(row) {
|
|
|
87
88
|
return row.category || row.result_kind || row.resultKind || '(uncategorized)';
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
function rowErrorText(row) {
|
|
92
|
+
return row.error_preview || row.result || row.error || row.message || row.error_first_line || '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isKnownTestFixture(row) {
|
|
96
|
+
if (row.session_id !== 'no-session' || row.agent != null || row.model != null) return false;
|
|
97
|
+
const tool = rowTool(row);
|
|
98
|
+
if (tool === 'unknown_test_tool') return true;
|
|
99
|
+
return tool === 'apply_patch' && /^Error:\s*patch failed\s*$/i.test(String(rowErrorText(row)).trim());
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeRowCategory(row) {
|
|
103
|
+
const storedCategory = rowCategory(row);
|
|
104
|
+
let category = storedCategory;
|
|
105
|
+
if (isKnownTestFixture(row)) {
|
|
106
|
+
category = 'expected-test';
|
|
107
|
+
} else if (rowTool(row) === 'apply_patch') {
|
|
108
|
+
const derived = classifyToolFailure(rowErrorText(row), 'apply_patch');
|
|
109
|
+
// Failure previews are bounded and may end before the nested cause. Never
|
|
110
|
+
// downgrade a stored specific category to the generic fallback merely
|
|
111
|
+
// because the historical preview lacks that tail.
|
|
112
|
+
if (derived !== 'runtime/failure' || storedCategory === 'runtime/failure') {
|
|
113
|
+
category = derived;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return category === storedCategory
|
|
117
|
+
? row
|
|
118
|
+
: { ...row, stored_category: storedCategory, category };
|
|
119
|
+
}
|
|
120
|
+
|
|
90
121
|
const sinceTs = parseSince(sinceArg);
|
|
91
122
|
const onlyArg = String(argValue('--only', 'all') || 'all').toLowerCase();
|
|
92
123
|
const rows = files.flatMap(readRows)
|
|
124
|
+
.map(normalizeRowCategory)
|
|
93
125
|
.filter((row) => sinceTs == null || Number(row.ts || 0) >= sinceTs)
|
|
94
126
|
.filter((row) => !toolFilter || rowTool(row) === toolFilter)
|
|
95
127
|
.filter((row) => !agentFilter || String(row.agent || '-') === agentFilter)
|
|
@@ -150,6 +182,8 @@ const actionableByFamily = tally(actionableRows, categoryFamily);
|
|
|
150
182
|
const commandExitByTool = tally(commandExitRows, rowTool);
|
|
151
183
|
const expectedByCategory = tally(expectedRows, rowCategory);
|
|
152
184
|
const patchByCategory = tally(patchRows, rowCategory);
|
|
185
|
+
const reclassifiedRows = rows.filter((row) => row.stored_category && row.stored_category !== rowCategory(row));
|
|
186
|
+
const reclassifiedByCategory = tally(reclassifiedRows, (row) => `${row.stored_category} -> ${rowCategory(row)}`);
|
|
153
187
|
|
|
154
188
|
if (jsonMode) {
|
|
155
189
|
console.log(JSON.stringify({
|
|
@@ -160,6 +194,7 @@ if (jsonMode) {
|
|
|
160
194
|
expected_absorbed: { shown: expectedRecent.length, matched: expectedRows.length },
|
|
161
195
|
session_cancellations: { shown: 0, matched: cancellationRows.length },
|
|
162
196
|
patch_failures: { matched: patchRows.length, categories: asObject(patchByCategory) },
|
|
197
|
+
reclassified: { matched: reclassifiedRows.length, categories: asObject(reclassifiedByCategory) },
|
|
163
198
|
since: sinceTs ? new Date(sinceTs).toISOString() : null,
|
|
164
199
|
filters: {
|
|
165
200
|
tool: toolFilter,
|
|
@@ -200,6 +235,7 @@ console.log(`actionable families (matched): ${asText(actionableByFamily)}`);
|
|
|
200
235
|
console.log(`patch failures (matched): ${patchRows.length} — ${asText(patchByCategory)}`);
|
|
201
236
|
console.log(`command-exit tools (matched): ${asText(commandExitByTool)}`);
|
|
202
237
|
console.log(`expected/absorbed categories (matched): ${asText(expectedByCategory)}`);
|
|
238
|
+
console.log(`reclassified rows (matched): ${reclassifiedRows.length} — ${asText(reclassifiedByCategory)}`);
|
|
203
239
|
console.log(`shown categories: ${asText(byCategory)}`);
|
|
204
240
|
for (const row of recent) {
|
|
205
241
|
const tool = rowTool(row);
|
package/scripts/tool-stress.mjs
CHANGED
|
@@ -129,13 +129,13 @@ try {
|
|
|
129
129
|
|
|
130
130
|
// ── Phase D: cancellation under load ─────────────────────────────────────
|
|
131
131
|
const bg = await timed('shell-async', /task_id/, () => executeBuiltinTool('shell', {
|
|
132
|
-
command: 'node -e "setTimeout(()=>{}, 30000)"',
|
|
132
|
+
command: 'node -e "setTimeout(()=>{}, 30000)"',
|
|
133
133
|
}, root, { sessionId: 'stress-cancel' }));
|
|
134
134
|
const bgId = (/task_id:\s*(\S+)/.exec(String(bg)) || [])[1];
|
|
135
135
|
if (!bgId) failures.push('async shell did not return task_id');
|
|
136
136
|
else {
|
|
137
137
|
await timed('task-cancel', /cancelled/, () => executeBuiltinTool('task', { action: 'cancel', task_id: bgId }, root, { sessionId: 'stress-cancel' }));
|
|
138
|
-
const st = await timed('task-status', /cancelled|failed/, () => executeBuiltinTool('task', { action: '
|
|
138
|
+
const st = await timed('task-status', /cancelled|failed/, () => executeBuiltinTool('task', { action: 'read', task_id: bgId }, root, { sessionId: 'stress-cancel' }));
|
|
139
139
|
if (!/cancelled/.test(String(st))) failures.push(`cancelled task not reported cancelled: ${String(st).slice(0, 120)}`);
|
|
140
140
|
}
|
|
141
141
|
} finally {
|
|
@@ -211,9 +211,6 @@ function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR, includeLeadBrief = true }
|
|
|
211
211
|
const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
|
|
212
212
|
const parts = [];
|
|
213
213
|
|
|
214
|
-
const toolLead = readOptional(path.join(LEAD_DIR, 'lead-tool.md'));
|
|
215
|
-
if (toolLead) parts.push(toolLead);
|
|
216
|
-
|
|
217
214
|
if (includeLeadBrief) {
|
|
218
215
|
const briefLead = readOptional(path.join(LEAD_DIR, 'lead-brief.md'));
|
|
219
216
|
if (briefLead) parts.push(briefLead);
|
|
@@ -7,17 +7,9 @@
|
|
|
7
7
|
- Confirm destructive/hard-to-reverse actions against explicit validated paths;
|
|
8
8
|
never `~`, a root, or unresolved variables/globs; report material deletion
|
|
9
9
|
recoverability.
|
|
10
|
-
- Ask only for decisions.
|
|
11
|
-
- Investigate, build, and verify only what the requested outcome requires;
|
|
12
|
-
trust internal and framework guarantees.
|
|
13
|
-
- Blocking tests cover only essential integrity, security, compatibility, and
|
|
14
|
-
buildability invariants. Treat mutable behavior, UX, exact text, snapshots,
|
|
15
|
-
and implementation shape as advisory specifications; update them when the
|
|
16
|
-
requested behavior changes instead of preserving obsolete behavior.
|
|
17
10
|
- After required work, run final verification only when the outcome needs
|
|
18
11
|
evidence the successful tool result does not already give, and run only
|
|
19
|
-
affected blocking invariants.
|
|
20
|
-
reopening already obtained content. Combine commands when dependency or
|
|
12
|
+
affected blocking invariants. Combine commands when dependency or
|
|
21
13
|
atomicity requires it.
|
|
22
14
|
- Mid-task: replacement supersedes; addition folds in; status gets a brief
|
|
23
15
|
answer while work continues. After compaction, resume the summary.
|
|
@@ -5,15 +5,18 @@
|
|
|
5
5
|
path/name only→`find`; wildcard/recursive paths→`glob` (including known-root
|
|
6
6
|
unknown descendants); exact directory entries→`list`;
|
|
7
7
|
file-content search→`grep`; known-file content→`read`;
|
|
8
|
-
exact symbol, body, or relation→`code_graph
|
|
8
|
+
exact symbol, body, or relation→`code_graph`
|
|
9
|
+
(identifier declarations/usages→`code_graph`; literal values/strings→`grep`);
|
|
10
|
+
local Git repository inspection and mutation→`git`;
|
|
9
11
|
program execution, calculations, data transformation, file generation, or
|
|
10
12
|
unsupported formats→`shell`;
|
|
11
13
|
web/current→`search`; returned URL body→`web_fetch`;
|
|
12
14
|
prior work→`recall` (history only, never current local state);
|
|
13
15
|
durable compact English memory→`memory`;
|
|
14
16
|
explicit Project change→`cwd`
|
|
15
|
-
(a shell-local `cd` never changes the Project)
|
|
16
|
-
|
|
17
|
+
(a shell-local `cd` never changes the Project).
|
|
18
|
+
Paths reachable by expanding an environment variable or the home directory
|
|
19
|
+
are resolved locations, not unknowns.
|
|
17
20
|
Use only named tools present in the current tool surface.
|
|
18
21
|
- Requirements define what must be true; evidence establishes what is true.
|
|
19
22
|
Never use one as the other. Treat supplied target locations as resolved;
|
|
@@ -22,30 +25,56 @@
|
|
|
22
25
|
inspect the original content itself. Within the current project, pass project-relative
|
|
23
26
|
paths and omit optional scopes equal to its root; explicit paths may be
|
|
24
27
|
outside cwd only for targets outside the project.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
For a required new file, Add File itself is the atomic absence check: call
|
|
29
|
+
it directly, and inspect only if it reports that the target already exists.
|
|
30
|
+
- Evidence economy: investigate, build, and verify only what the requested
|
|
31
|
+
outcome requires; trust internal and framework guarantees. After
|
|
32
|
+
identifying all result-critical evidence needs, plan the fewest
|
|
33
|
+
evidence-complete dependent rounds, then the fewest calls.
|
|
34
|
+
Known state — system/framework guarantees, supplied facts, exact lines or
|
|
35
|
+
values already visible here, tool returns, applied patches, and proved
|
|
36
|
+
checks — is never re-found, re-derived, or re-verified at any granularity:
|
|
37
|
+
no re-query call, no confirmation subcommand inside a shell command, no
|
|
38
|
+
availability probe for what the operation itself would report, no reopening
|
|
39
|
+
a file to rebuild context or confirm an edit, no rerun of a passed check.
|
|
30
40
|
A hole (needed content absent and not reconstructable) is fetched once;
|
|
31
|
-
a change re-opens only that hole.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
a change re-opens only that hole. Returned output is fully mined before the
|
|
42
|
+
next round. `code_graph references` supplies the declaration and scoped
|
|
43
|
+
usages and ends that facet; values/locations end at the context grep
|
|
44
|
+
returns; `read` covers only what returned spans cannot, as an anchored
|
|
45
|
+
offset/limit window. Already obtained hunk text is any visible span —
|
|
46
|
+
`grep`, `code_graph`, `shell`, system/reminder text, or `read` — not only
|
|
47
|
+
`read`. Each follow-up may address only facts left unresolved or changed by
|
|
48
|
+
prior results. Evidence that determines the answer, edit, or deliverable
|
|
49
|
+
ends retrieval — patch if needed.
|
|
50
|
+
- Parallel batching: independent calls share one batch by default — one
|
|
51
|
+
best-routed call per facet. Cost is counted in rounds, not calls: a batch
|
|
52
|
+
of N calls in one message is one round, so a call-count saving never
|
|
53
|
+
justifies folding work into a single worse-routed call.
|
|
54
|
+
The opening round is a one-shot sweep: every facet knowable from the
|
|
55
|
+
request alone — enumeration, content probes, file samples — launches in
|
|
56
|
+
that single first batch. Each later round exists only for facets the
|
|
57
|
+
previous round's results created; a facet no result produced belonged in
|
|
58
|
+
the round before it.
|
|
59
|
+
Opening-round batching never licenses a guessed path. `glob.path` must be
|
|
60
|
+
an established existing directory; omit it for the current Project. When
|
|
61
|
+
the location itself is unknown, use `find` first and call `glob` only if
|
|
62
|
+
wildcard descendants are still needed.
|
|
63
|
+
Serialize two calls only when the later call's
|
|
64
|
+
inputs are actually produced by the earlier result; the mere possibility
|
|
65
|
+
that a result could reshape later work never defers an independent call.
|
|
66
|
+
Before each batch, deduplicate the remaining necessary facets and route
|
|
67
|
+
each once to the cheapest sufficient tool — never split or duplicate a
|
|
68
|
+
facet across tools, mutate merely to widen retrieval, reserve known work,
|
|
69
|
+
or cap fanout. Applying one analysis to many targets is a single
|
|
70
|
+
parameterized call over all targets, not one call per target.
|
|
71
|
+
Enumerating sibling directories or same-kind files is one wildcard call
|
|
72
|
+
(`glob`, or `read` with a glob for content sampling), never a
|
|
73
|
+
directory-by-directory `list` walk or one `read` per file.
|
|
74
|
+
- Blocking checks cover only essential integrity, security, compatibility,
|
|
75
|
+
and buildability invariants. Treat mutable behavior, UX, exact text,
|
|
76
|
+
snapshots, and implementation shape as advisory specifications; update them
|
|
77
|
+
when the requested behavior changes instead of preserving obsolete behavior.
|
|
49
78
|
- A successful verification closes the task unless later changes affect it.
|
|
50
79
|
Rerun a failed action only after its inputs or subject changes; otherwise
|
|
51
80
|
report it unresolved.
|
|
@@ -53,26 +82,28 @@
|
|
|
53
82
|
mutate only when the deliverable requires it, first preserving evidence
|
|
54
83
|
at risk. Never mutate merely to clear an obstacle or unexpected state;
|
|
55
84
|
unrecoverably lost evidence ends its search — report best effort.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Hand-authored text is edited only with
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
85
|
+
- Before the exposed file-editing tool, use only already obtained exact source
|
|
86
|
+
text. Never infer edit context from another file, a sample, or expected text.
|
|
87
|
+
For context patches, include exact unchanged lines around each change and use
|
|
88
|
+
a class/function locator when that context is not unique.
|
|
89
|
+
- Apply all determined changes in the fewest safe calls the active tool
|
|
90
|
+
supports. Hand-authored text is edited only with the exposed file-editing
|
|
91
|
+
tool.
|
|
92
|
+
- Avoid Shell for file operations covered by dedicated tools unless explicitly
|
|
93
|
+
instructed or after verifying that a dedicated tool cannot do the job.
|
|
94
|
+
Shell otherwise joins investigation only for facts requiring execution or
|
|
95
|
+
unsupported decoding; an already-open shell is never a routing reason.
|
|
96
|
+
- Shell commands start in the foreground. If still running after 15 seconds,
|
|
97
|
+
the command continues as a tracked `task_id`; completion arrives by notification.
|
|
98
|
+
Only when the request explicitly requires
|
|
68
99
|
a service to survive after the run exits, detach it at shell level (for
|
|
69
100
|
example, `nohup ... &`); never detach ordinary jobs merely to avoid tracking.
|
|
70
101
|
A sync call may return a `task_id` and partial output after its blocking
|
|
71
102
|
budget. Do not poll: completion resumes automatically. When intermediate output must drive
|
|
72
|
-
decisions or the user requests monitoring,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
decisions or the user explicitly requests monitoring, call `task read` once
|
|
104
|
+
to return the current status and output snapshot. If it is still running,
|
|
105
|
+
await the completion notification; do not
|
|
106
|
+
call `task` again unless the user explicitly asks for another snapshot.
|
|
107
|
+
Omit `timeout_ms` by default, including for long jobs. A positive value is a
|
|
108
|
+
hard total deadline that kills the command even after task promotion; `0`
|
|
109
|
+
means no deadline.
|
|
@@ -173,6 +173,9 @@ function maintenanceRouteToPreset(routeOrName, agent) {
|
|
|
173
173
|
const effort = String(routeOrName.effort || '').trim();
|
|
174
174
|
if (effort) out.effort = effort;
|
|
175
175
|
if (routeOrName.fast === true) out.fast = true;
|
|
176
|
+
if (routeOrName.modelParameters && typeof routeOrName.modelParameters === 'object') {
|
|
177
|
+
out.modelParameters = { ...routeOrName.modelParameters };
|
|
178
|
+
}
|
|
176
179
|
return out;
|
|
177
180
|
}
|
|
178
181
|
|
|
@@ -49,8 +49,9 @@ function isAgentProgressWatchdogAbortError(err) {
|
|
|
49
49
|
return typeof msg === 'string' && WATCHDOG_ABORT_RE.test(msg);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// Tools that enforce their own execution deadline:
|
|
53
|
-
//
|
|
52
|
+
// Tools that enforce their own execution deadline: shell kills the process
|
|
53
|
+
// only when the caller supplied a positive timeout_ms. These are NOT
|
|
54
|
+
// blanket-exempted from the
|
|
54
55
|
// tool-running watchdog — if their own
|
|
55
56
|
// deadline timer dies the session would otherwise hang forever. Instead the
|
|
56
57
|
// watchdog raises the tool-running ceiling to their self-deadline + a grace
|
|
@@ -60,10 +61,6 @@ const SELF_DEADLINE_TOOLS = new Set(['shell']);
|
|
|
60
61
|
// Grace added on top of a tool's own deadline before the watchdog steps in, so
|
|
61
62
|
// the tool's in-process kill always fires first under normal operation.
|
|
62
63
|
const TOOL_SELF_DEADLINE_GRACE_MS = 60_000;
|
|
63
|
-
// Fallback deadline matching the shell implementation's 120s foreground
|
|
64
|
-
// timeout.
|
|
65
|
-
const SHELL_DEFAULT_TIMEOUT_MS = 120_000;
|
|
66
|
-
|
|
67
64
|
function bareToolName(toolName) {
|
|
68
65
|
if (typeof toolName !== 'string' || !toolName) return '';
|
|
69
66
|
// Strip any MCP/server prefix (e.g. 'server__shell' or 'server.shell').
|
|
@@ -80,17 +77,16 @@ function isSelfDeadlineTool(toolName) {
|
|
|
80
77
|
* recorded into the progress snapshot at dispatch time. Returns a positive
|
|
81
78
|
* number when the tool enforces its own deadline, or null when unknown/missing
|
|
82
79
|
* (caller then falls back to the plain toolRunningMs ceiling).
|
|
83
|
-
* - shell: explicit `
|
|
80
|
+
* - shell: explicit positive `timeout_ms`; omitted/0 has no self-deadline.
|
|
84
81
|
*/
|
|
85
82
|
export function resolveToolSelfDeadlineMs(toolName, args) {
|
|
86
83
|
if (!isSelfDeadlineTool(toolName)) return null;
|
|
87
84
|
const bare = bareToolName(toolName);
|
|
88
85
|
const a = (args && typeof args === 'object') ? args : {};
|
|
89
86
|
if (bare === 'shell') {
|
|
90
|
-
const t = Number(a.
|
|
87
|
+
const t = Number(a.timeout_ms);
|
|
91
88
|
if (Number.isFinite(t) && t > 0) return t;
|
|
92
|
-
|
|
93
|
-
return envDefault > 0 ? envDefault : SHELL_DEFAULT_TIMEOUT_MS;
|
|
89
|
+
return null;
|
|
94
90
|
}
|
|
95
91
|
return null;
|
|
96
92
|
}
|