@tea-agent/loop-agent 0.39.0-next.25 → 0.39.0-next.27
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/CHANGELOG.md +11 -0
- package/bin/loop-agent.js +7 -3
- package/dist/build-stamp.json +2 -2
- package/dist/executors/dag-pi-executor.js +58 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-sdk-executor.js +182 -54
- package/dist/executors/shell-executor.js +54 -0
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/worker/observe/node-input.js +72 -3
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/state.js +2 -2
- package/dist/worker/observe/static/views/dag-inspector.js +51 -0
- package/dist/worker/observe/static/views/session-timeline.js +18 -5
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/failure-category.js +4 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +140 -39
- package/dist/workflows/dag/frontend-prewrite-gate.js +87 -135
- package/dist/workflows/dag/frontend-test-case-quality.js +5 -13
- package/dist/workflows/dag/frontend-test-environment-probe.js +227 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +10 -18
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +68 -0
- package/dist/workflows/dag/init-hybrid.js +52 -94
- package/dist/workflows/dag/node-execution.js +119 -8
- package/dist/workflows/dag/prompt.js +15 -1
- package/dist/workflows/dag/rerun-plan.js +22 -3
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +23 -0
- package/dist/workflows/dag/validate.js +41 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/package.json +1 -1
- package/skills/codebase-scout/SKILL.md +1 -1
|
@@ -10,6 +10,18 @@ export const dagNodeExecutorSchema = z.enum(["pi", "shell", "static"]);
|
|
|
10
10
|
export const CURSOR_DAG_EXECUTOR_REMOVED_ERROR = 'executor "cursor" is no longer supported; regenerate the DAG with Pi-only writers (implement-pi / repair-pi)';
|
|
11
11
|
export const CURSOR_EXECUTOR_MODELS_REMOVED_ERROR = "executorModels.cursor is no longer supported; use executorModels.pi only";
|
|
12
12
|
export const dagToolProfileSchema = z.enum(["read-only", "write"]);
|
|
13
|
+
/**
|
|
14
|
+
* Frozen Pi extension short ids for backend implementation DAGs (plan
|
|
15
|
+
* 2026-08-21 D2): the DAG carries only these ids; the executor resolves them
|
|
16
|
+
* against the Pi settings package inventory at run time. Adding a package
|
|
17
|
+
* requires changing this enum and the plan.
|
|
18
|
+
*/
|
|
19
|
+
export const dagPiExtensionIdSchema = z.enum(["pi-codegraph", "pi-lens"]);
|
|
20
|
+
/** Node-level explicit Pi extension allowlist (omitted = all off, R8.1). */
|
|
21
|
+
export const dagPiExtensionsSchema = z
|
|
22
|
+
.array(dagPiExtensionIdSchema)
|
|
23
|
+
.min(1)
|
|
24
|
+
.transform((ids) => Array.from(new Set(ids)));
|
|
13
25
|
/** Static command capabilities only; tasks cannot inject executables or shell prefixes. */
|
|
14
26
|
export const dagCommandCapabilitySchema = z.enum(["playwright-cli"]);
|
|
15
27
|
export const dagCommandPolicySchema = z.discriminatedUnion("mode", [
|
|
@@ -427,6 +439,8 @@ export const dagBackendTestPipelineSchema = z.enum([
|
|
|
427
439
|
"ingest-backend-test-gap",
|
|
428
440
|
]);
|
|
429
441
|
export const dagFrontendBrowserToolPreflightSchema = z.object({}).strict();
|
|
442
|
+
export const dagFrontendTestStandardScenariosSchema = z.object({}).strict();
|
|
443
|
+
export const dagFrontendTestEnvironmentProbeSchema = z.object({}).strict();
|
|
430
444
|
export const dagFrontendTestEvidenceValidationSchema = z.object({}).strict();
|
|
431
445
|
export const dagFrontendTestCaseChecklistSchema = z.object({}).strict();
|
|
432
446
|
export const dagFrontendTestHtmlReportSchema = z.object({}).strict();
|
|
@@ -517,6 +531,8 @@ export const dagShellConfigSchema = z.object({
|
|
|
517
531
|
frontendReviewContext: dagFrontendReviewContextSchema.optional(),
|
|
518
532
|
frontendTestCaseChecklist: dagFrontendTestCaseChecklistSchema.optional(),
|
|
519
533
|
frontendBrowserToolPreflight: dagFrontendBrowserToolPreflightSchema.optional(),
|
|
534
|
+
frontendTestStandardScenarios: dagFrontendTestStandardScenariosSchema.optional(),
|
|
535
|
+
frontendTestEnvironmentProbe: dagFrontendTestEnvironmentProbeSchema.optional(),
|
|
520
536
|
frontendTestEvidenceValidation: dagFrontendTestEvidenceValidationSchema.optional(),
|
|
521
537
|
finalWriteSetApprovalGate: dagFinalWriteSetApprovalGateSchema.optional(),
|
|
522
538
|
frontendTestL5Report: z.object({}).strict().optional(),
|
|
@@ -749,6 +765,13 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
|
|
|
749
765
|
role: dagRoleSchema.optional(),
|
|
750
766
|
skills: z.array(z.string()).optional(),
|
|
751
767
|
toolProfile: dagToolProfileSchema.optional(),
|
|
768
|
+
/**
|
|
769
|
+
* Explicit Pi extension allowlist (backend implementation DAGs only):
|
|
770
|
+
* frozen short ids resolved at run time against the Pi settings package
|
|
771
|
+
* inventory. Omitted = extension discovery stays fully closed (R8.1).
|
|
772
|
+
* Only `executor: "pi"` nodes may declare it (validated fail-closed).
|
|
773
|
+
*/
|
|
774
|
+
piExtensions: dagPiExtensionsSchema.optional(),
|
|
752
775
|
/** Default deny: file write (toolProfile=write) does not grant command execution. */
|
|
753
776
|
commandPolicy: dagCommandPolicySchema.optional(),
|
|
754
777
|
writePolicy: dagWritePolicySchema.optional(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_DAG_EXECUTOR_MODELS, ENV_VAR_NAME_PATTERN, dagCommandPolicyAllows, resolveDagCommandPolicy, } from "./types.js";
|
|
1
|
+
import { DEFAULT_DAG_EXECUTOR_MODELS, ENV_VAR_NAME_PATTERN, dagCommandPolicyAllows, dagPiExtensionIdSchema, resolveDagCommandPolicy, } from "./types.js";
|
|
2
2
|
import { resolveShellCommands } from "../../executors/shell-executor.js";
|
|
3
3
|
import { pathMatchesPattern } from "../../shared/git-progress.js";
|
|
4
4
|
import { resolveRepairTaskForGate } from "./repair-artifact.js";
|
|
@@ -465,6 +465,8 @@ function validateShellTaskConfig(task, spec, issues) {
|
|
|
465
465
|
!shell.backendTestPipeline &&
|
|
466
466
|
!shell.frontendPrewriteGate &&
|
|
467
467
|
!shell.frontendBrowserToolPreflight &&
|
|
468
|
+
!shell.frontendTestStandardScenarios &&
|
|
469
|
+
!shell.frontendTestEnvironmentProbe &&
|
|
468
470
|
!shell.frontendVerificationBundle &&
|
|
469
471
|
!shell.frontendReviewContext &&
|
|
470
472
|
!shell.frontendTestCaseChecklist &&
|
|
@@ -953,6 +955,7 @@ export function validateDagSpec(spec) {
|
|
|
953
955
|
validateCommandPolicyTaskConfig(task, issues);
|
|
954
956
|
validateDynamicChildCommandPolicy(task, issues);
|
|
955
957
|
validateProjectGovernanceTaskConfig(task, spec, issues);
|
|
958
|
+
validatePiExtensionsTaskConfig(task, issues);
|
|
956
959
|
validateFailureAwareDependsOn(task, spec, issues);
|
|
957
960
|
}
|
|
958
961
|
validateSameRankWriteSetConflicts(spec, ranks, issues);
|
|
@@ -963,6 +966,43 @@ export function validateDagSpec(spec) {
|
|
|
963
966
|
}
|
|
964
967
|
return issues;
|
|
965
968
|
}
|
|
969
|
+
/**
|
|
970
|
+
* Fail-closed `piExtensions` validation (plan 2026-08-21 D1/D2):
|
|
971
|
+
* - only `executor: "pi"` nodes may declare it (shell/static reject);
|
|
972
|
+
* - unknown ids outside the frozen enum are rejected at parse time by zod,
|
|
973
|
+
* and here for task objects that bypass schema parsing;
|
|
974
|
+
* - closeout nodes must stay fully closed (extension-free) — the two-bucket
|
|
975
|
+
* generator never assigns it there, so any appearance is contract drift.
|
|
976
|
+
*/
|
|
977
|
+
function validatePiExtensionsTaskConfig(task, issues) {
|
|
978
|
+
const ids = task.piExtensions;
|
|
979
|
+
if (ids === undefined)
|
|
980
|
+
return;
|
|
981
|
+
if (task.executor !== "pi") {
|
|
982
|
+
issues.push({
|
|
983
|
+
type: "invalid-pi-extensions-config",
|
|
984
|
+
message: `task ${task.id} piExtensions requires executor "pi" (found ${task.executor})`,
|
|
985
|
+
});
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const allowed = new Set(dagPiExtensionIdSchema.options);
|
|
989
|
+
for (const id of ids) {
|
|
990
|
+
if (!allowed.has(id)) {
|
|
991
|
+
issues.push({
|
|
992
|
+
type: "invalid-pi-extensions-config",
|
|
993
|
+
message: `task ${task.id} piExtensions has unknown id "${id}"; allowed: ${[
|
|
994
|
+
...allowed,
|
|
995
|
+
].join(", ")}`,
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (task.id === "closeout-pi" && ids.length > 0) {
|
|
1000
|
+
issues.push({
|
|
1001
|
+
type: "invalid-pi-extensions-config",
|
|
1002
|
+
message: "closeout-pi must stay extension-free (read buckets exclude closeout)",
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
966
1006
|
function validateProjectGovernanceTaskConfig(task, spec, issues) {
|
|
967
1007
|
if (task.governanceStandardReview) {
|
|
968
1008
|
if (task.executor !== "pi" ||
|
|
@@ -130,6 +130,18 @@
|
|
|
130
130
|
"toolProfile": {
|
|
131
131
|
"enum": ["read-only", "write"]
|
|
132
132
|
},
|
|
133
|
+
"piExtensionId": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"enum": ["pi-codegraph", "pi-lens"],
|
|
136
|
+
"description": "Frozen Pi extension short id resolved at run time against the Pi settings package inventory. Adding a package requires changing the plan and this enum."
|
|
137
|
+
},
|
|
138
|
+
"piExtensions": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"minItems": 1,
|
|
141
|
+
"uniqueItems": true,
|
|
142
|
+
"items": { "$ref": "#/$defs/piExtensionId" },
|
|
143
|
+
"description": "Node-level explicit Pi extension allowlist (backend implementation DAGs only). Omitted = extension discovery stays fully closed (R8.1). Only executor 'pi' nodes may declare it."
|
|
144
|
+
},
|
|
133
145
|
"role": {
|
|
134
146
|
"enum": ["planner", "scout", "implementer", "reviewer", "supervisor", "verifier", "closeout"]
|
|
135
147
|
},
|
|
@@ -356,6 +368,31 @@
|
|
|
356
368
|
"properties": { "schemaVersion": { "const": 1 }, "requireBaseline": { "const": true } }
|
|
357
369
|
},
|
|
358
370
|
"frontendTestCaseChecklist": { "type": "object", "additionalProperties": false },
|
|
371
|
+
"frontendBrowserToolPreflight": { "type": "object", "additionalProperties": false },
|
|
372
|
+
"frontendTestStandardScenarios": { "type": "object", "additionalProperties": false },
|
|
373
|
+
"frontendTestEnvironmentProbe": { "type": "object", "additionalProperties": false },
|
|
374
|
+
"frontendTestCaseManifest": {
|
|
375
|
+
"type": "object",
|
|
376
|
+
"additionalProperties": false,
|
|
377
|
+
"properties": {
|
|
378
|
+
"maxCases": { "type": "integer", "minimum": 1 },
|
|
379
|
+
"declaredAcIds": { "type": "array", "items": { "type": "string" } }
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
"frontendTestResultFinalize": {
|
|
383
|
+
"type": "object",
|
|
384
|
+
"additionalProperties": false,
|
|
385
|
+
"properties": {
|
|
386
|
+
"declaredAcIds": { "type": "array", "items": { "type": "string" } }
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
"frontendTestReports": {
|
|
390
|
+
"type": "object",
|
|
391
|
+
"additionalProperties": false,
|
|
392
|
+
"properties": {
|
|
393
|
+
"l5": { "type": "boolean" }
|
|
394
|
+
}
|
|
395
|
+
},
|
|
359
396
|
"frontendTestEvidenceValidation": { "type": "object", "additionalProperties": false },
|
|
360
397
|
"frontendTestHtmlReport": { "type": "object", "additionalProperties": false },
|
|
361
398
|
"backendTestPipeline": {
|
|
@@ -383,6 +420,12 @@
|
|
|
383
420
|
{ "required": ["frontendVerificationBundle"] },
|
|
384
421
|
{ "required": ["frontendReviewContext"] },
|
|
385
422
|
{ "required": ["frontendTestCaseChecklist"] },
|
|
423
|
+
{ "required": ["frontendBrowserToolPreflight"] },
|
|
424
|
+
{ "required": ["frontendTestStandardScenarios"] },
|
|
425
|
+
{ "required": ["frontendTestEnvironmentProbe"] },
|
|
426
|
+
{ "required": ["frontendTestCaseManifest"] },
|
|
427
|
+
{ "required": ["frontendTestResultFinalize"] },
|
|
428
|
+
{ "required": ["frontendTestReports"] },
|
|
386
429
|
{ "required": ["frontendTestEvidenceValidation"] },
|
|
387
430
|
{ "required": ["frontendTestHtmlReport"] },
|
|
388
431
|
{ "required": ["backendTestPipeline"] }
|
|
@@ -485,6 +528,7 @@
|
|
|
485
528
|
"items": { "type": "string", "minLength": 1 }
|
|
486
529
|
},
|
|
487
530
|
"toolProfile": { "$ref": "#/$defs/toolProfile" },
|
|
531
|
+
"piExtensions": { "$ref": "#/$defs/piExtensions" },
|
|
488
532
|
"governanceStandardReview": {
|
|
489
533
|
"type": "boolean",
|
|
490
534
|
"description": "Explicitly opts this node into writer-change-scoped AGENTS.md and repository-local code-standard review. Never inferred from node id or role."
|
|
@@ -66,12 +66,11 @@
|
|
|
66
66
|
".harness/**",
|
|
67
67
|
"artifacts/**"
|
|
68
68
|
],
|
|
69
|
-
"outputContract": "Write testcase/frontend/rag/standard-scenarios.v1.json for generate-time standard scenario coverage.",
|
|
70
|
-
"subtask_prompt": "
|
|
69
|
+
"outputContract": "Write testcase/frontend/rag/standard-scenarios.v1.json for generate-time standard scenario coverage. Copy docs/templates or harness.json governanceRoot templates (including ai_workspace/loop-agent/templates) when present; otherwise write the minimal STD-FE-SMOKE-ENTRY fallback.",
|
|
70
|
+
"subtask_prompt": "Prepare frontend-test package: materialize standard-scenarios.v1.json into the RAG package from docs/templates, governanceRoot/templates, or the init-projected ai_workspace/loop-agent/templates path.",
|
|
71
71
|
"shell": {
|
|
72
|
-
"commands": [
|
|
73
|
-
|
|
74
|
-
],
|
|
72
|
+
"commands": [],
|
|
73
|
+
"frontendTestStandardScenarios": {},
|
|
75
74
|
"cwd": ".",
|
|
76
75
|
"timeoutMs": 60000
|
|
77
76
|
}
|
|
@@ -120,12 +119,11 @@
|
|
|
120
119
|
".harness/**",
|
|
121
120
|
"artifacts/**"
|
|
122
121
|
],
|
|
123
|
-
"outputContract": "Fail-closed environment preflight: absolute non-production baseUrl + curl HTTP reachability; writes environmentProbe facts; unreachable => blockedReason frontend-base-url-unreachable (
|
|
124
|
-
"subtask_prompt": "Parse the concrete baseUrl selected by retrieve-frontend-test-context-pi from testcase/frontend/rag/context.md. Reject production, non-http(s), credentials, query and fragment. Probe with curl (HEAD then GET fallback; connect/max-time; no auth/cookie). 2xx/3xx => reachable and continue. 4xx/5xx/DNS/timeout/
|
|
122
|
+
"outputContract": "Fail-closed environment preflight: absolute non-production baseUrl + curl HTTP reachability; writes environmentProbe facts; unreachable => blockedReason frontend-base-url-unreachable with errorClass (connection-refused / dns-unresolved / connect-timeout / http-N). Node ERROR so generate/map do not run. Does not start the app.",
|
|
123
|
+
"subtask_prompt": "Parse the concrete baseUrl selected by retrieve-frontend-test-context-pi from testcase/frontend/rag/context.md. Reject production, non-http(s), credentials, query and fragment. Probe with curl (HEAD then GET fallback; connect/max-time; no auth/cookie). 2xx/3xx => reachable and continue. Connection refused records errorClass=connection-refused and tells the operator to start the local app then rerun from this node. 4xx/5xx/DNS/timeout/TLS => blockedReason frontend-base-url-unreachable. Missing curl => blockedReason curl-unavailable. Do not start the app.",
|
|
125
124
|
"shell": {
|
|
126
|
-
"commands": [
|
|
127
|
-
|
|
128
|
-
],
|
|
125
|
+
"commands": [],
|
|
126
|
+
"frontendTestEnvironmentProbe": {},
|
|
129
127
|
"cwd": ".",
|
|
130
128
|
"timeoutMs": 60000
|
|
131
129
|
}
|
|
@@ -17,5 +17,5 @@ Do not claim the environment is reachable until preflight completes. Preflight d
|
|
|
17
17
|
|
|
18
18
|
## Standard scenario coverage
|
|
19
19
|
|
|
20
|
-
- Copy or reference `docs/templates/frontend-test-standard-scenarios.v1.json` into `testcase/frontend/rag/standard-scenarios.v1.json` when available.
|
|
20
|
+
- Copy or reference `docs/templates/frontend-test-standard-scenarios.v1.json`, `harness.json` `governanceRoot`/templates, or the init-projected `ai_workspace/loop-agent/templates/frontend-test-standard-scenarios.v1.json` into `testcase/frontend/rag/standard-scenarios.v1.json` when available.
|
|
21
21
|
- Add `## Standard scenario coverage` to coverage-map.md with planned/n/a for each must scenario.
|
package/package.json
CHANGED