@tea-agent/loop-agent 0.25.6 → 0.26.0
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/AGENTS.md +2 -1
- package/CHANGELOG.md +1020 -1006
- package/bin/loop-agent.js +21 -21
- package/dist/commands/cursor-prompt.js +6 -6
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/executors/dag-pi-executor.js +26 -20
- package/dist/executors/model-routing.js +34 -18
- package/dist/governance/manifest-types.js +33 -5
- package/dist/sidecars/cursor-prompt/executor.js +1 -1
- package/dist/task/task-demand-routing.js +3 -1
- package/dist/worker/console/chat/model-resolver.js +15 -3
- package/dist/worker/observe/static/constants.js +3 -2
- package/dist/worker/observe/static/copy.js +67 -67
- package/dist/worker/observe/static/dag-layout.d.ts +31 -31
- package/dist/worker/observe/static/dag-layout.js +83 -83
- package/dist/worker/observe/static/dag-model.js +1 -0
- package/dist/worker/observe/static/dom.js +220 -220
- package/dist/worker/observe/static/relations.js +133 -133
- package/dist/worker/observe/static/router.js +93 -93
- package/dist/worker/observe/static/run-processing.js +148 -148
- package/dist/worker/observe/static/styles.css +182 -42
- package/dist/worker/observe/static/views/batch.js +227 -227
- package/dist/worker/observe/static/views/dag-graph.js +172 -172
- package/dist/worker/observe/static/views/failures.js +143 -143
- package/dist/worker/observe/static/views/feature.js +492 -492
- package/dist/worker/observe/static/views/run.js +453 -453
- package/dist/worker/observe/static/views/shell.js +7 -7
- package/dist/worker/observe/static/views/timeline.js +163 -163
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/lifecycle.js +40 -30
- package/dist/workflows/dag/node-execution.js +13 -0
- package/dist/workflows/dag/types.js +59 -19
- package/docs/skills/README.md +7 -7
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +473 -473
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/backend-test-result.schema.json +99 -99
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/frontend-design-contract.md +42 -42
- package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
- package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
- package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
- package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
- package/docs/templates/frontend-eval/metrics.md +138 -138
- package/docs/templates/frontend-eval/smoke-targets.md +53 -53
- package/docs/templates/frontend-task-constraints.md +35 -35
- package/docs/templates/frontend-task-requirement.md +70 -70
- package/docs/templates/harness.schema.json +29 -7
- package/docs/templates/init-evolution-review.md +35 -35
- package/docs/templates/interactive-ui-round2-experiment.md +66 -66
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
- package/docs/templates/knowledge-sync-dag.json +178 -178
- package/docs/templates/knowledge-sync-draft.schema.json +71 -71
- package/docs/templates/product-line/closeout.yaml +9 -9
- package/docs/templates/product-line/design.md +13 -13
- package/docs/templates/product-line/links.md +10 -10
- package/docs/templates/product-line/requirement.md +17 -17
- package/docs/templates/product-line/test-plan.md +7 -7
- package/docs/templates/production-readiness-checklist.md +57 -57
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +80 -80
- package/docs/templates/worker-dogfood-setup.md +68 -68
- package/harness.json +1 -2
- package/package.json +1 -1
- package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
- package/scripts/kb-graph-incremental-prepare.mjs +386 -386
- package/scripts/kb-graph-materialize.mjs +105 -105
- package/scripts/kb-graph-promote.mjs +164 -164
- package/scripts/kb-query.mjs +554 -554
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/analyze-product-dependencies/SKILL.md +67 -67
- package/skills/analyze-product-dependencies/agents/openai.yaml +4 -4
- package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -30
- package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -28
- package/skills/analyze-product-dependencies/references/example.md +76 -76
- package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -35
- package/skills/analyze-product-dependencies/references/input-contract.md +11 -11
- package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -61
- package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -267
- package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -101
- package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -142
- package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -76
- package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -146
- package/skills/analyze-product-requirements/SKILL.md +90 -90
- package/skills/analyze-product-requirements/agents/openai.yaml +4 -4
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -91
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -56
- package/skills/analyze-product-requirements/references/example.md +86 -86
- package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -66
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -32
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -33
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -35
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -193
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -69
- package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -97
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -98
- package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -156
- package/skills/browser-tools/browser-content.js +103 -103
- package/skills/browser-tools/browser-cookies.js +35 -35
- package/skills/browser-tools/browser-eval.js +53 -53
- package/skills/browser-tools/browser-hn-scraper.js +108 -108
- package/skills/browser-tools/browser-nav.js +44 -44
- package/skills/browser-tools/browser-pick.js +162 -162
- package/skills/browser-tools/browser-screenshot.js +34 -34
- package/skills/browser-tools/browser-start.js +86 -86
- package/skills/browser-tools/package-lock.json +2556 -2556
- package/skills/browser-tools/package.json +19 -19
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/grill-me/SKILL.md +10 -10
- package/skills/loop-agent/references/README.md +67 -67
- package/skills/loop-agent/references/docs-converge.md +126 -126
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/long-running-loop.md +57 -57
- package/skills/loop-agent/references/model-routing.md +2 -0
- package/skills/loop-agent/references/one-shot-runs.md +85 -85
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
- package/skills/playwright-cli/SKILL.md +420 -420
- package/skills/playwright-cli/references/element-attributes.md +23 -23
- package/skills/playwright-cli/references/playwright-tests.md +39 -39
- package/skills/playwright-cli/references/request-mocking.md +87 -87
- package/skills/playwright-cli/references/running-code.md +241 -241
- package/skills/playwright-cli/references/session-management.md +225 -225
- package/skills/playwright-cli/references/storage-state.md +275 -275
- package/skills/playwright-cli/references/test-generation.md +433 -433
- package/skills/playwright-cli/references/tracing.md +139 -139
- package/skills/playwright-cli/references/video-recording.md +143 -143
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/using-git-worktrees/SKILL.md +215 -215
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
package/bin/loop-agent.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
|
|
6
|
-
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
-
const cliEntry = join(packageRoot, "dist", "cli.js");
|
|
8
|
-
|
|
9
|
-
if (!existsSync(cliEntry)) {
|
|
10
|
-
console.error(
|
|
11
|
-
`loop-agent: cannot find built CLI at ${cliEntry}. Run \`npm run build\` before using the package bin.`,
|
|
12
|
-
);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
await import(pathToFileURL(cliEntry).href);
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const cliEntry = join(packageRoot, "dist", "cli.js");
|
|
8
|
+
|
|
9
|
+
if (!existsSync(cliEntry)) {
|
|
10
|
+
console.error(
|
|
11
|
+
`loop-agent: cannot find built CLI at ${cliEntry}. Run \`npm run build\` before using the package bin.`,
|
|
12
|
+
);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await import(pathToFileURL(cliEntry).href);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
@@ -141,7 +141,7 @@ async function runCursorPromptBatch(task, cwd, model, timeoutMs) {
|
|
|
141
141
|
}
|
|
142
142
|
async function runCursorPromptStreaming(task, cwd, model, timeoutMs) {
|
|
143
143
|
const startedAt = Date.now();
|
|
144
|
-
process.stderr.write(`[cursor-prompt] streaming (model=${model}, cwd=${cwd})
|
|
144
|
+
process.stderr.write(`[cursor-prompt] streaming (model=${model}, cwd=${cwd})
|
|
145
145
|
`);
|
|
146
146
|
const runDir = (await computeRunDir(cwd, task)) ?? undefined;
|
|
147
147
|
const result = await executeCursorPromptStream({
|
|
@@ -164,15 +164,15 @@ async function runCursorPromptStreaming(task, cwd, model, timeoutMs) {
|
|
|
164
164
|
});
|
|
165
165
|
const elapsed = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
166
166
|
if (result.ok) {
|
|
167
|
-
process.stderr.write(`
|
|
168
|
-
[cursor-prompt] done in ${elapsed}s, status=${result.status}
|
|
167
|
+
process.stderr.write(`
|
|
168
|
+
[cursor-prompt] done in ${elapsed}s, status=${result.status}
|
|
169
169
|
`);
|
|
170
170
|
}
|
|
171
171
|
else {
|
|
172
172
|
const stderr = result.stderr || "(no output)";
|
|
173
|
-
process.stderr.write(`
|
|
174
|
-
[cursor-prompt] FAILED in ${elapsed}s (${result.failureCategory}):
|
|
175
|
-
${stderr}
|
|
173
|
+
process.stderr.write(`
|
|
174
|
+
[cursor-prompt] FAILED in ${elapsed}s (${result.failureCategory}):
|
|
175
|
+
${stderr}
|
|
176
176
|
`);
|
|
177
177
|
process.exit(1);
|
|
178
178
|
}
|
|
@@ -37,17 +37,17 @@ export function parseLoopBenchmarkArgs(args) {
|
|
|
37
37
|
return { json, markdown, outputPath };
|
|
38
38
|
}
|
|
39
39
|
export function printLoopBenchmarkUsage() {
|
|
40
|
-
console.log(`usage: loop-benchmark [options]
|
|
41
|
-
|
|
42
|
-
Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
|
|
43
|
-
|
|
44
|
-
Options:
|
|
45
|
-
--json Emit JSON (default when no format flag is set)
|
|
46
|
-
--markdown Emit Markdown report
|
|
47
|
-
--output <path> Write Markdown report to a repo-relative or absolute path
|
|
48
|
-
-h, --help Show this help
|
|
49
|
-
|
|
50
|
-
Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
|
|
40
|
+
console.log(`usage: loop-benchmark [options]
|
|
41
|
+
|
|
42
|
+
Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--json Emit JSON (default when no format flag is set)
|
|
46
|
+
--markdown Emit Markdown report
|
|
47
|
+
--output <path> Write Markdown report to a repo-relative or absolute path
|
|
48
|
+
-h, --help Show this help
|
|
49
|
+
|
|
50
|
+
Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
|
|
51
51
|
Recommendation never changes convergence.enabled default.`);
|
|
52
52
|
}
|
|
53
53
|
export async function runLoopBenchmark(repoRoot, rawArgs) {
|
|
@@ -106,22 +106,22 @@ export function parsePiReuseBenchmarkArgs(args) {
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
export function printPiReuseBenchmarkUsage() {
|
|
109
|
-
console.log(`usage: pi-reuse-benchmark [options]
|
|
110
|
-
|
|
111
|
-
Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
|
|
112
|
-
|
|
113
|
-
Options:
|
|
114
|
-
--report <path> Benchmark report markdown (approval status)
|
|
115
|
-
--approval <path> Explicit approval JSON artifact
|
|
116
|
-
--off-executor <path> Baseline executor.jsonl (reuse off)
|
|
117
|
-
--on-executor <path> Treatment executor.jsonl (reuse on)
|
|
118
|
-
--off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
|
|
119
|
-
--on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
|
|
120
|
-
--json Emit JSON (default when no format flag is set)
|
|
121
|
-
--markdown Emit Markdown summary
|
|
122
|
-
-h, --help Show this help
|
|
123
|
-
|
|
124
|
-
Recommendations: defer | maintain-opt-in | eligible-for-human-review
|
|
109
|
+
console.log(`usage: pi-reuse-benchmark [options]
|
|
110
|
+
|
|
111
|
+
Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
|
|
112
|
+
|
|
113
|
+
Options:
|
|
114
|
+
--report <path> Benchmark report markdown (approval status)
|
|
115
|
+
--approval <path> Explicit approval JSON artifact
|
|
116
|
+
--off-executor <path> Baseline executor.jsonl (reuse off)
|
|
117
|
+
--on-executor <path> Treatment executor.jsonl (reuse on)
|
|
118
|
+
--off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
|
|
119
|
+
--on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
|
|
120
|
+
--json Emit JSON (default when no format flag is set)
|
|
121
|
+
--markdown Emit Markdown summary
|
|
122
|
+
-h, --help Show this help
|
|
123
|
+
|
|
124
|
+
Recommendations: defer | maintain-opt-in | eligible-for-human-review
|
|
125
125
|
Never changes CODE_AGENT_PI_REUSE_RUNTIME default (off).`);
|
|
126
126
|
}
|
|
127
127
|
function resolveRepoRelative(repoRoot, filePath) {
|
|
@@ -169,14 +169,25 @@ export function buildDagPiUserMessage(task, persona, step) {
|
|
|
169
169
|
"Do not wrap the output in code fences and do not add conversational preamble.",
|
|
170
170
|
].join(" ");
|
|
171
171
|
}
|
|
172
|
-
function resolveDagPiModelConfig(
|
|
173
|
-
const
|
|
172
|
+
export function resolveDagPiModelConfig(modelReference, options) {
|
|
173
|
+
const separatorIndex = modelReference.indexOf("/");
|
|
174
|
+
const qualified = separatorIndex >= 0;
|
|
175
|
+
const provider = qualified
|
|
176
|
+
? modelReference.slice(0, separatorIndex)
|
|
177
|
+
: (DAG_PI_MODEL_PROVIDERS[modelReference] ?? DEFAULT_DAG_PI_PROVIDER);
|
|
178
|
+
const model = qualified
|
|
179
|
+
? modelReference.slice(separatorIndex + 1)
|
|
180
|
+
: modelReference;
|
|
181
|
+
if (!provider || !model) {
|
|
182
|
+
throw new Error(`invalid DAG Pi model reference "${modelReference}": expected non-empty provider/model`);
|
|
183
|
+
}
|
|
184
|
+
const explicitThinking = options?.thinking?.trim();
|
|
185
|
+
const thinking = explicitThinking ??
|
|
186
|
+
(provider === "wizard-local" && model === "gpt-5.5" ? "low" : undefined);
|
|
174
187
|
return {
|
|
175
188
|
provider,
|
|
176
189
|
model,
|
|
177
|
-
...(
|
|
178
|
-
? { thinking: "low" }
|
|
179
|
-
: {}),
|
|
190
|
+
...(thinking ? { thinking } : {}),
|
|
180
191
|
};
|
|
181
192
|
}
|
|
182
193
|
const SUMMARY_STDOUT_MAX = 4_000;
|
|
@@ -291,7 +302,7 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
|
|
|
291
302
|
: undefined;
|
|
292
303
|
const result = await piStepFn({
|
|
293
304
|
attachedFiles: [],
|
|
294
|
-
modelConfig: resolveDagPiModelConfig(input.model),
|
|
305
|
+
modelConfig: resolveDagPiModelConfig(input.model, input.thinking ? { thinking: input.thinking } : undefined),
|
|
295
306
|
prompt: input.prompt,
|
|
296
307
|
repoRoot: input.cwd,
|
|
297
308
|
step,
|
|
@@ -304,8 +315,8 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
|
|
|
304
315
|
abortGraceMs: input.abortGraceMs,
|
|
305
316
|
onActivity: bridgeActivity
|
|
306
317
|
? (activity) => {
|
|
307
|
-
if (activity.kind === "lease"
|
|
308
|
-
|
|
318
|
+
if (activity.kind === "lease" ||
|
|
319
|
+
activity.kind === "synthetic-heartbeat") {
|
|
309
320
|
return;
|
|
310
321
|
}
|
|
311
322
|
bridgeActivity(activity.kind, activity.at);
|
|
@@ -372,7 +383,9 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
|
|
|
372
383
|
}
|
|
373
384
|
}
|
|
374
385
|
}
|
|
375
|
-
if (writeGuardOk &&
|
|
386
|
+
if (writeGuardOk &&
|
|
387
|
+
beforeStatus !== undefined &&
|
|
388
|
+
changeManifestChangedFiles !== undefined) {
|
|
376
389
|
await persistWriterChangeManifest({
|
|
377
390
|
runDir: meta.runDir,
|
|
378
391
|
nodeId: input.task.id,
|
|
@@ -445,12 +458,8 @@ function parseWriterImplementationOutcome(text) {
|
|
|
445
458
|
const normalized = normalizeProtocolLine(line, WRITER_OUTCOME_PROTOCOL_LINE, nextLine);
|
|
446
459
|
if (normalized === undefined)
|
|
447
460
|
continue;
|
|
448
|
-
const value = normalized
|
|
449
|
-
|
|
450
|
-
.trim();
|
|
451
|
-
const outcome = isWriterImplementationOutcome(value)
|
|
452
|
-
? value
|
|
453
|
-
: undefined;
|
|
461
|
+
const value = normalized.slice(WRITER_OUTCOME_PROTOCOL_LINE.length).trim();
|
|
462
|
+
const outcome = isWriterImplementationOutcome(value) ? value : undefined;
|
|
454
463
|
candidates.push({
|
|
455
464
|
lineIndex,
|
|
456
465
|
value,
|
|
@@ -474,9 +483,7 @@ function parseWriterImplementationOutcome(text) {
|
|
|
474
483
|
};
|
|
475
484
|
}
|
|
476
485
|
function isWriterImplementationOutcome(value) {
|
|
477
|
-
return (value === "changed" ||
|
|
478
|
-
value === "already-satisfied" ||
|
|
479
|
-
value === "blocked");
|
|
486
|
+
return (value === "changed" || value === "already-satisfied" || value === "blocked");
|
|
480
487
|
}
|
|
481
488
|
function writerOutcomeDiagnostics(text, parsed, changedFiles) {
|
|
482
489
|
const firstNonEmpty = text
|
|
@@ -543,8 +550,7 @@ function canonicalizeProtocolFirstLine(assistantText, firstProtocolLine) {
|
|
|
543
550
|
if (protocolNextLineIndex === protocolIndex + 1) {
|
|
544
551
|
after.shift();
|
|
545
552
|
}
|
|
546
|
-
while (before.at(-1)?.trim() === "" &&
|
|
547
|
-
after.at(0)?.trim() === "") {
|
|
553
|
+
while (before.at(-1)?.trim() === "" && after.at(0)?.trim() === "") {
|
|
548
554
|
after.shift();
|
|
549
555
|
}
|
|
550
556
|
const bodyLines = [...before, ...after];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_DAG_EXECUTOR_MODELS, } from
|
|
1
|
+
import { DEFAULT_DAG_EXECUTOR_MODELS, } from "../workflows/dag/types.js";
|
|
2
|
+
import { normalizeExecutorTierValue, } from "../governance/manifest-types.js";
|
|
2
3
|
export const DEFAULT_DAG_MODELS = {
|
|
3
4
|
HIGH: "gpt-5.5",
|
|
4
5
|
MED: "gpt-5.5",
|
|
@@ -8,6 +9,16 @@ export const DEFAULT_DAG_MODELS = {
|
|
|
8
9
|
* DAG executor model tier keys that may carry a per-complexity override.
|
|
9
10
|
*/
|
|
10
11
|
const EXECUTOR_MODEL_TIERS = ["LOW", "MED", "HIGH"];
|
|
12
|
+
/** Resolve one tier to a model and optional explicit thinking override. */
|
|
13
|
+
export function resolveExecutorTierSelection(execConfig, tier) {
|
|
14
|
+
const tierSelection = normalizeExecutorTierValue(execConfig?.[tier]);
|
|
15
|
+
if (tierSelection)
|
|
16
|
+
return tierSelection;
|
|
17
|
+
const defaultModel = execConfig?.defaultModel;
|
|
18
|
+
if (defaultModel && defaultModel !== "default")
|
|
19
|
+
return { model: defaultModel };
|
|
20
|
+
return { model: DEFAULT_DAG_EXECUTOR_MODELS.pi[tier] };
|
|
21
|
+
}
|
|
11
22
|
/**
|
|
12
23
|
* Resolve the DAG executor model matrix for Pi from harness `executors.pi`.
|
|
13
24
|
*
|
|
@@ -19,22 +30,23 @@ const EXECUTOR_MODEL_TIERS = ["LOW", "MED", "HIGH"];
|
|
|
19
30
|
* The "default" literal (injected by the schema `.default("default")`) and
|
|
20
31
|
* absent/undefined both mean "no override, fall through".
|
|
21
32
|
*/
|
|
22
|
-
export function resolveExecutorModelMatrix(
|
|
23
|
-
const tierValue = (tier) => {
|
|
24
|
-
const tierOverride = execConfig?.[tier];
|
|
25
|
-
if (tierOverride && tierOverride !== "default")
|
|
26
|
-
return tierOverride;
|
|
27
|
-
const defaultModel = execConfig?.defaultModel;
|
|
28
|
-
if (defaultModel && defaultModel !== "default")
|
|
29
|
-
return defaultModel;
|
|
30
|
-
return DEFAULT_DAG_EXECUTOR_MODELS[executor][tier];
|
|
31
|
-
};
|
|
33
|
+
export function resolveExecutorModelMatrix(_executor, execConfig) {
|
|
32
34
|
return {
|
|
33
|
-
LOW:
|
|
34
|
-
MED:
|
|
35
|
-
HIGH:
|
|
35
|
+
LOW: resolveExecutorTierSelection(execConfig, "LOW").model,
|
|
36
|
+
MED: resolveExecutorTierSelection(execConfig, "MED").model,
|
|
37
|
+
HIGH: resolveExecutorTierSelection(execConfig, "HIGH").model,
|
|
36
38
|
};
|
|
37
39
|
}
|
|
40
|
+
/** Resolve only explicitly configured per-tier thinking values. */
|
|
41
|
+
export function resolveExecutorThinkingMatrix(execConfig) {
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const tier of EXECUTOR_MODEL_TIERS) {
|
|
44
|
+
const selection = normalizeExecutorTierValue(execConfig?.[tier]);
|
|
45
|
+
if (selection?.thinking)
|
|
46
|
+
result[tier] = selection.thinking;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
38
50
|
/**
|
|
39
51
|
* Resolve the Pi DAG executor model matrix from a harness manifest.
|
|
40
52
|
*/
|
|
@@ -55,7 +67,9 @@ export function resolveModelSelection(manifest, taskConfig, step, options) {
|
|
|
55
67
|
};
|
|
56
68
|
}
|
|
57
69
|
const profileName = resolveProfileName(manifest, taskConfig.complexity, step, retryAttempt);
|
|
58
|
-
const profile = profileName
|
|
70
|
+
const profile = profileName
|
|
71
|
+
? manifest.modelProfiles?.[profileName]
|
|
72
|
+
: undefined;
|
|
59
73
|
if (!profile) {
|
|
60
74
|
return {
|
|
61
75
|
modelConfig: manifest.models?.[step],
|
|
@@ -74,7 +88,9 @@ export function resolveModelSelection(manifest, taskConfig, step, options) {
|
|
|
74
88
|
};
|
|
75
89
|
}
|
|
76
90
|
function resolveProfileName(manifest, complexity, step, retryAttempt) {
|
|
77
|
-
if (step ===
|
|
91
|
+
if (step === "implement" &&
|
|
92
|
+
retryAttempt > 0 &&
|
|
93
|
+
manifest.modelRouting?.implementRetry) {
|
|
78
94
|
return manifest.modelRouting.implementRetry;
|
|
79
95
|
}
|
|
80
96
|
const route = manifest.modelRouting?.[step];
|
|
@@ -85,11 +101,11 @@ function resolveProfileName(manifest, complexity, step, retryAttempt) {
|
|
|
85
101
|
}
|
|
86
102
|
export function formatModelSelectionLabel(modelConfig, profileName) {
|
|
87
103
|
if (!modelConfig) {
|
|
88
|
-
return profileName ? `${profileName}` :
|
|
104
|
+
return profileName ? `${profileName}` : "default";
|
|
89
105
|
}
|
|
90
106
|
const base = modelConfig.provider && modelConfig.model
|
|
91
107
|
? `${modelConfig.provider}/${modelConfig.model}`
|
|
92
|
-
: modelConfig.model ??
|
|
108
|
+
: (modelConfig.model ?? "default");
|
|
93
109
|
return profileName ? `${profileName}:${base}` : base;
|
|
94
110
|
}
|
|
95
111
|
export function formatFallbackLabel(profile) {
|
|
@@ -18,6 +18,34 @@ export const worktreeManifestConfigSchema = z.object({
|
|
|
18
18
|
});
|
|
19
19
|
export const taskExecutorSchema = z.enum(["pi"]);
|
|
20
20
|
export const CURSOR_TASK_EXECUTOR_REMOVED_ERROR = 'task executor "cursor" is no longer supported; governed runtime is Pi-only';
|
|
21
|
+
/** Per-tier model override: bare model id string, or `{ model, thinking? }`. */
|
|
22
|
+
export const executorTierModelSchema = z.union([
|
|
23
|
+
z.string(),
|
|
24
|
+
z.object({
|
|
25
|
+
model: z.string().min(1),
|
|
26
|
+
thinking: z.string().optional(),
|
|
27
|
+
}),
|
|
28
|
+
]);
|
|
29
|
+
/** Normalize an executors.pi LOW|MED|HIGH value. */
|
|
30
|
+
export function normalizeExecutorTierValue(value) {
|
|
31
|
+
if (value === null || value === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (typeof value === "string") {
|
|
34
|
+
if (value === "default" || value.length === 0)
|
|
35
|
+
return undefined;
|
|
36
|
+
return { model: value };
|
|
37
|
+
}
|
|
38
|
+
if (typeof value !== "object" || Array.isArray(value))
|
|
39
|
+
return undefined;
|
|
40
|
+
const model = value.model;
|
|
41
|
+
if (typeof model !== "string" || model.length === 0 || model === "default") {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const thinking = value.thinking;
|
|
45
|
+
return typeof thinking === "string" && thinking.length > 0
|
|
46
|
+
? { model, thinking }
|
|
47
|
+
: { model };
|
|
48
|
+
}
|
|
21
49
|
export const executorManifestSchema = z.object({
|
|
22
50
|
description: z.string().optional(),
|
|
23
51
|
enabled: z.boolean().optional(),
|
|
@@ -27,9 +55,9 @@ export const executorManifestSchema = z.object({
|
|
|
27
55
|
* these take priority over defaultModel for the matching DAG executor tier.
|
|
28
56
|
* "default" literal and absent/undefined both mean "no override, fall through".
|
|
29
57
|
*/
|
|
30
|
-
LOW:
|
|
31
|
-
MED:
|
|
32
|
-
HIGH:
|
|
58
|
+
LOW: executorTierModelSchema.optional(),
|
|
59
|
+
MED: executorTierModelSchema.optional(),
|
|
60
|
+
HIGH: executorTierModelSchema.optional(),
|
|
33
61
|
requiresApiKey: z.string().optional(),
|
|
34
62
|
});
|
|
35
63
|
export const workflowPolicyProfileNameSchema = z.enum([
|
|
@@ -98,7 +126,7 @@ export const workflowPolicySchema = z
|
|
|
98
126
|
})
|
|
99
127
|
.optional()
|
|
100
128
|
.default({});
|
|
101
|
-
export const CURSOR_HARNESS_EXECUTOR_REMOVED_ERROR =
|
|
129
|
+
export const CURSOR_HARNESS_EXECUTOR_REMOVED_ERROR = "harness executors.cursor is no longer supported; remove it and use executors.pi only (Cursor is available only via cursor-prompt sidecar)";
|
|
102
130
|
export const harnessManifestSchema = z
|
|
103
131
|
.object({
|
|
104
132
|
version: z.number(),
|
|
@@ -160,7 +188,7 @@ export const harnessManifestSchema = z
|
|
|
160
188
|
if ("cursorExecutorUsage" in entrypoints) {
|
|
161
189
|
ctx.addIssue({
|
|
162
190
|
code: z.ZodIssueCode.custom,
|
|
163
|
-
message:
|
|
191
|
+
message: "entrypoints.cursorExecutorUsage is no longer supported; remove it (Cursor is cursor-prompt sidecar only)",
|
|
164
192
|
path: ["entrypoints", "cursorExecutorUsage"],
|
|
165
193
|
});
|
|
166
194
|
}
|
|
@@ -29,7 +29,7 @@ export function resolveArtifactWriteDir(options) {
|
|
|
29
29
|
}
|
|
30
30
|
export function buildArtifactPathPrompt(writeDir) {
|
|
31
31
|
if (!writeDir)
|
|
32
|
-
return `After changes, write artifacts/修改记录.md and artifacts/验证结果.md with verification evidence.
|
|
32
|
+
return `After changes, write artifacts/修改记录.md and artifacts/验证结果.md with verification evidence.
|
|
33
33
|
${ARTIFACT_INSTRUCTIONS}`;
|
|
34
34
|
return [
|
|
35
35
|
`After changes, write the following files:`,
|
|
@@ -156,7 +156,7 @@ function isDocumentationOnlyClause(clause) {
|
|
|
156
156
|
if (DOCUMENTATION_ONLY_MARKERS.test(clause)) {
|
|
157
157
|
return !PRODUCT_AND_DOCUMENT_DELIVERY.test(clause);
|
|
158
158
|
}
|
|
159
|
-
return TEST_ONLY_MARKERS.test(clause) && !PRODUCT_AND_TEST_DELIVERY.test(clause);
|
|
159
|
+
return (TEST_ONLY_MARKERS.test(clause) && !PRODUCT_AND_TEST_DELIVERY.test(clause));
|
|
160
160
|
}
|
|
161
161
|
function masksExistingBackendDependency(clause) {
|
|
162
162
|
return clause
|
|
@@ -335,6 +335,8 @@ export function classifyTaskDemand(input) {
|
|
|
335
335
|
}
|
|
336
336
|
const backendDelivery = titleSignals.backendDelivery || requirementSignals.backendDelivery;
|
|
337
337
|
const frontendProjectDefaultImplementation = hasStrongFrontendProjectEvidence &&
|
|
338
|
+
frontendPath &&
|
|
339
|
+
!hasBackendTaskType &&
|
|
338
340
|
!backendDelivery &&
|
|
339
341
|
!frontendNegated &&
|
|
340
342
|
!allowedPathsOnlyCoverNonProductArtifacts;
|
|
@@ -20,6 +20,18 @@ import { readFile } from "node:fs/promises";
|
|
|
20
20
|
import path from "node:path";
|
|
21
21
|
/** Tier keys in harness executors.pi that may override the default. */
|
|
22
22
|
const EXECUTOR_TIERS = ["LOW", "MED", "HIGH"];
|
|
23
|
+
function modelIdFromTierValue(value) {
|
|
24
|
+
if (typeof value === "string") {
|
|
25
|
+
return value && value !== "default" ? value : undefined;
|
|
26
|
+
}
|
|
27
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const model = value.model;
|
|
31
|
+
return typeof model === "string" && model && model !== "default"
|
|
32
|
+
? model
|
|
33
|
+
: undefined;
|
|
34
|
+
}
|
|
23
35
|
/**
|
|
24
36
|
* Read executors.pi.<tier> from the repo harness.json. Returns undefined when
|
|
25
37
|
* harness.json is absent or the tier is unset / "default" sentinel.
|
|
@@ -33,9 +45,9 @@ export async function readHarnessPiModelTier(repoRoot, tier = "MED") {
|
|
|
33
45
|
const pi = manifest.executors?.pi;
|
|
34
46
|
if (!pi)
|
|
35
47
|
return undefined;
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
return
|
|
48
|
+
const tierModel = modelIdFromTierValue(pi[tier]);
|
|
49
|
+
if (tierModel)
|
|
50
|
+
return tierModel;
|
|
39
51
|
const defaultModel = pi.defaultModel;
|
|
40
52
|
if (defaultModel && defaultModel !== "default")
|
|
41
53
|
return defaultModel;
|
|
@@ -67,8 +67,8 @@ export const STATUS_LABELS = {
|
|
|
67
67
|
done: "完成",
|
|
68
68
|
failed: "失败",
|
|
69
69
|
error: "错误",
|
|
70
|
-
partial_failed: "
|
|
71
|
-
partialfailed: "
|
|
70
|
+
partial_failed: "部分成功",
|
|
71
|
+
partialfailed: "部分成功",
|
|
72
72
|
blocked: "阻塞",
|
|
73
73
|
stale: "心跳失联",
|
|
74
74
|
reused: "复用",
|
|
@@ -104,6 +104,7 @@ export const DAG_EFFECTIVE_STATUS_LABELS = {
|
|
|
104
104
|
interrupted: "执行已中断",
|
|
105
105
|
"remote-unknown": "远端状态未知",
|
|
106
106
|
finished: "已完成",
|
|
107
|
+
partial_failed: "部分成功",
|
|
107
108
|
failed: "执行失败",
|
|
108
109
|
superseded: "任务已另行完成",
|
|
109
110
|
abandoned: "已放弃",
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
/** Copy-only recommended commands (Observe UI R5). */
|
|
2
|
-
import { el } from "./dom.js";
|
|
3
|
-
|
|
4
|
-
export async function copyText(text) {
|
|
5
|
-
const value = String(text ?? "");
|
|
6
|
-
if (!value) return false;
|
|
7
|
-
try {
|
|
8
|
-
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
9
|
-
await navigator.clipboard.writeText(value);
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
} catch {
|
|
13
|
-
// fall through
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
const ta = document.createElement("textarea");
|
|
17
|
-
ta.value = value;
|
|
18
|
-
ta.setAttribute("readonly", "");
|
|
19
|
-
ta.style.position = "fixed";
|
|
20
|
-
ta.style.left = "-9999px";
|
|
21
|
-
document.body.appendChild(ta);
|
|
22
|
-
ta.select();
|
|
23
|
-
const ok = document.execCommand("copy");
|
|
24
|
-
document.body.removeChild(ta);
|
|
25
|
-
return ok;
|
|
26
|
-
} catch {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Advisory recommended command: copy-only, never executes. */
|
|
32
|
-
export function renderRecommendedCommand(action) {
|
|
33
|
-
const block = el("div", "recommended-command");
|
|
34
|
-
if (!action) {
|
|
35
|
-
block.appendChild(el("span", "muted", "缺失"));
|
|
36
|
-
return block;
|
|
37
|
-
}
|
|
38
|
-
const head = el("div", "recommended-command-head");
|
|
39
|
-
if (action.kind) {
|
|
40
|
-
head.appendChild(el("span", "recommended-command-kind", action.kind));
|
|
41
|
-
}
|
|
42
|
-
head.appendChild(
|
|
43
|
-
el("span", "recommended-command-label", action.label ?? "—"),
|
|
44
|
-
);
|
|
45
|
-
block.appendChild(head);
|
|
46
|
-
if (action.command) {
|
|
47
|
-
const body = el("div", "recommended-command-body");
|
|
48
|
-
const code = el("code", "recommended-command-text", action.command);
|
|
49
|
-
body.appendChild(code);
|
|
50
|
-
const btn = el("button", "copy-command", "复制");
|
|
51
|
-
btn.type = "button";
|
|
52
|
-
btn.addEventListener("click", (e) => {
|
|
53
|
-
e.preventDefault();
|
|
54
|
-
e.stopPropagation();
|
|
55
|
-
void copyText(action.command).then((ok) => {
|
|
56
|
-
btn.textContent = ok ? "已复制" : "复制失败";
|
|
57
|
-
setTimeout(() => {
|
|
58
|
-
btn.textContent = "复制";
|
|
59
|
-
}, 1500);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
body.appendChild(btn);
|
|
63
|
-
block.appendChild(body);
|
|
64
|
-
}
|
|
65
|
-
return block;
|
|
66
|
-
}
|
|
67
|
-
|
|
1
|
+
/** Copy-only recommended commands (Observe UI R5). */
|
|
2
|
+
import { el } from "./dom.js";
|
|
3
|
+
|
|
4
|
+
export async function copyText(text) {
|
|
5
|
+
const value = String(text ?? "");
|
|
6
|
+
if (!value) return false;
|
|
7
|
+
try {
|
|
8
|
+
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
9
|
+
await navigator.clipboard.writeText(value);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
} catch {
|
|
13
|
+
// fall through
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const ta = document.createElement("textarea");
|
|
17
|
+
ta.value = value;
|
|
18
|
+
ta.setAttribute("readonly", "");
|
|
19
|
+
ta.style.position = "fixed";
|
|
20
|
+
ta.style.left = "-9999px";
|
|
21
|
+
document.body.appendChild(ta);
|
|
22
|
+
ta.select();
|
|
23
|
+
const ok = document.execCommand("copy");
|
|
24
|
+
document.body.removeChild(ta);
|
|
25
|
+
return ok;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Advisory recommended command: copy-only, never executes. */
|
|
32
|
+
export function renderRecommendedCommand(action) {
|
|
33
|
+
const block = el("div", "recommended-command");
|
|
34
|
+
if (!action) {
|
|
35
|
+
block.appendChild(el("span", "muted", "缺失"));
|
|
36
|
+
return block;
|
|
37
|
+
}
|
|
38
|
+
const head = el("div", "recommended-command-head");
|
|
39
|
+
if (action.kind) {
|
|
40
|
+
head.appendChild(el("span", "recommended-command-kind", action.kind));
|
|
41
|
+
}
|
|
42
|
+
head.appendChild(
|
|
43
|
+
el("span", "recommended-command-label", action.label ?? "—"),
|
|
44
|
+
);
|
|
45
|
+
block.appendChild(head);
|
|
46
|
+
if (action.command) {
|
|
47
|
+
const body = el("div", "recommended-command-body");
|
|
48
|
+
const code = el("code", "recommended-command-text", action.command);
|
|
49
|
+
body.appendChild(code);
|
|
50
|
+
const btn = el("button", "copy-command", "复制");
|
|
51
|
+
btn.type = "button";
|
|
52
|
+
btn.addEventListener("click", (e) => {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
e.stopPropagation();
|
|
55
|
+
void copyText(action.command).then((ok) => {
|
|
56
|
+
btn.textContent = ok ? "已复制" : "复制失败";
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
btn.textContent = "复制";
|
|
59
|
+
}, 1500);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
body.appendChild(btn);
|
|
63
|
+
block.appendChild(body);
|
|
64
|
+
}
|
|
65
|
+
return block;
|
|
66
|
+
}
|
|
67
|
+
|