@tea-agent/loop-agent 0.39.0-next.8 → 0.39.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 +4 -1
- package/CHANGELOG.md +264 -67
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/dag/generate-task-dag.js +9 -0
- package/dist/application/task-lifecycle/advance.js +4 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +124 -18
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +554 -33
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-failure-category.js +138 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +181 -27
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +28 -0
- package/dist/task/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/deferred-turn.js +91 -0
- package/dist/worker/console/chat/mcp-inventory.js +221 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +165 -83
- package/dist/worker/console/chat/repo-walk.js +10 -1
- package/dist/worker/console/chat/routes.js +480 -55
- package/dist/worker/console/chat/semantic-activity.js +20 -8
- package/dist/worker/console/chat/session-store.js +146 -2
- package/dist/worker/console/chat/turn-process.js +10 -10
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operator-actions.js +195 -4
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +5 -4
- package/dist/worker/console/recovery-cta.js +4 -4
- package/dist/worker/console/recovery-error-copy.js +4 -4
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +42 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-Br92EGb7.js} +1 -1
- package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-oulImtQq.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-tyHc6aqO.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-CY86BSpT.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-BKKsDmNs.js} +1 -1
- package/dist/worker/console/static/assets/channel-TY36zt-i.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-CXZdjl3B.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-B-aIFDQ5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-BDgAW_B2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-DABa4UPR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-aGbysVOx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-B8MSZDVs.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-CpelV73g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-BE04ty3z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-BUxTkaaR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-CYYBVKfX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-CEQF_xmQ.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-CqpxN5IT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-B3PzanVi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-BSUGCNx1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-CdLevU5T.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BjpF_Kok.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C4I6NJny.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-C8hecCr5.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-Daod6tOS.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-BvvmazGv.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-DeN_vfV4.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-D6dq1t9z.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-DziIdNY4.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js} +1 -1
- package/dist/worker/console/static/assets/index-C3GgHg-g.css +1 -0
- package/dist/worker/console/static/assets/index-Ci8ksvBm.js +406 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-Czv5eYx2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-DxGmNj5K.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-CYCFqkO_.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-B0YzRwrd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-t3EZtIGh.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-BTXpTv0T.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-DU8UkKqA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-DP4y8ohp.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-B07vNaYR.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-xDWktb2i.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-BLNMKqeS.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-n0adalxH.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-BbnS-2DL.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-B4uPpCLu.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA--MMbB0fX.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-BXUgp2QY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-CsaMxki7.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-C2IuG9iH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-CmEWOXCW.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-D_9LE0MG.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +1 -1
- package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +74 -26
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +54 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +237 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +50 -22
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +827 -53
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
- package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
- package/dist/workflows/dag/backend-test-intake-context.js +32 -0
- package/dist/workflows/dag/backend-test-layout.js +133 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +167 -32
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
- package/dist/workflows/dag/backend-test-scenario-partitions.js +321 -0
- package/dist/workflows/dag/backend-test-writer-completeness.js +100 -56
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +3 -1
- package/dist/workflows/dag/convergence/controller.js +141 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +25 -9
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-implementation-contract.js +377 -4
- package/dist/workflows/dag/frontend-plan-render.js +1 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +96 -84
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +894 -205
- package/dist/workflows/dag/node-execution.js +183 -15
- package/dist/workflows/dag/prompt.js +60 -1
- package/dist/workflows/dag/rerun-feedback.js +212 -0
- package/dist/workflows/dag/rerun-plan.js +224 -27
- package/dist/workflows/dag/rerun-run.js +61 -6
- package/dist/workflows/dag/rerun-task.js +61 -2
- package/dist/workflows/dag/retry-policy.js +109 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +143 -1
- package/dist/workflows/dag/validate.js +64 -20
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +60 -35
- package/docs/templates/frontend-implementation-contract.schema.json +1 -1
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +6 -4
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-BBOTJHvD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/index-B-qWGThd.js +0 -325
- package/dist/worker/console/static/assets/index-C-xRgY_0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +0 -8
|
@@ -4,7 +4,8 @@ import { appendFile, lstat, mkdir, readFile, realpath, stat, } from "node:fs/pro
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { processTreeSpawnOptions, terminateProcessTree, } from "./process-tree.js";
|
|
6
6
|
import { resolvePlaywrightCliLauncher } from "./playwright-cli-launcher.js";
|
|
7
|
-
import { PLAYWRIGHT_CLI_ALLOWED_COMMANDS, isPlaywrightCliCommand, } from "../shared/playwright-cli-command-policy.js";
|
|
7
|
+
import { PLAYWRIGHT_CLI_ALLOWED_COMMANDS, isPlaywrightCliCommand, parsePlaywrightCliFindMatchCount, } from "../shared/playwright-cli-command-policy.js";
|
|
8
|
+
import { defaultFrontendTestLayout, escapeRegexLiteral, } from "../workflows/dag/frontend-test-layout.js";
|
|
8
9
|
export { PLAYWRIGHT_CLI_ALLOWED_COMMANDS, isPlaywrightCliCommand, };
|
|
9
10
|
/** Logical capability name only; execution always uses the verified JS launcher. */
|
|
10
11
|
export const PLAYWRIGHT_CLI_EXECUTABLE = "playwright-cli";
|
|
@@ -111,6 +112,13 @@ export async function readPlaywrightCliReceipts(runDir, nodeId) {
|
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
export function summarizePlaywrightCliReceipts(receipts) {
|
|
115
|
+
// Sequence numbers are assigned at dispatch time, which is the controller's
|
|
116
|
+
// causal order. Receipt lines are appended when each command COMPLETES, so
|
|
117
|
+
// concurrent commands (e.g. parallel find calls issued in one turn) persist
|
|
118
|
+
// out of sequence order. Judge chain contiguity on the dispatch order by
|
|
119
|
+
// sorting on `sequence` first; a genuinely missing receipt (gap) or a
|
|
120
|
+
// duplicated sequence still fails the contiguity check below.
|
|
121
|
+
const orderedReceipts = [...receipts].sort((a, b) => a.sequence - b.sequence);
|
|
114
122
|
let hasSuccessfulOpen = false;
|
|
115
123
|
let hasInteractionOrAssertion = false;
|
|
116
124
|
let hasMeaningfulAssertion = false;
|
|
@@ -122,7 +130,7 @@ export function summarizePlaywrightCliReceipts(receipts) {
|
|
|
122
130
|
let hasContiguousControllerSequence = true;
|
|
123
131
|
let previousSequence = 0;
|
|
124
132
|
const caseIds = new Set();
|
|
125
|
-
for (const receipt of
|
|
133
|
+
for (const receipt of orderedReceipts) {
|
|
126
134
|
caseIds.add(receipt.caseId);
|
|
127
135
|
const succeeded = receipt.exitCode === 0 && !receipt.timedOut;
|
|
128
136
|
const ordered = receipt.sequence === previousSequence + 1;
|
|
@@ -137,8 +145,10 @@ export function summarizePlaywrightCliReceipts(receipts) {
|
|
|
137
145
|
if (PLAYWRIGHT_CLI_INTERACTION_COMMANDS.has(receipt.command) && succeeded) {
|
|
138
146
|
hasInteractionOrAssertion = true;
|
|
139
147
|
}
|
|
140
|
-
// A successful find
|
|
141
|
-
|
|
148
|
+
// A successful find (or find-unique, its semantic wrapper) is the only
|
|
149
|
+
// model-visible semantic assertion in v1.
|
|
150
|
+
if ((receipt.command === "find" || receipt.command === "find-unique") &&
|
|
151
|
+
succeeded) {
|
|
142
152
|
hasMeaningfulAssertion = true;
|
|
143
153
|
if (ordered && sawOpen)
|
|
144
154
|
sawMeaningfulAssertionAfterOpen = true;
|
|
@@ -258,7 +268,8 @@ function validateOpenArgs(args, baseUrl) {
|
|
|
258
268
|
}
|
|
259
269
|
function resolveEvidenceRelative(rawPath, ctx) {
|
|
260
270
|
const evidenceDir = normalizePosix(ctx.evidenceDir).replace(/\/$/, "");
|
|
261
|
-
const
|
|
271
|
+
const layout = ctx.layout ?? defaultFrontendTestLayout();
|
|
272
|
+
const evidencePrefix = `${layout.evidenceDir}/${ctx.caseId}`;
|
|
262
273
|
if (!(evidenceDir === evidencePrefix ||
|
|
263
274
|
evidenceDir.startsWith(`${evidencePrefix}/`))) {
|
|
264
275
|
throw new PlaywrightCliPolicyError("evidence-dir-invalid", `evidenceDir must be under ${evidencePrefix}`);
|
|
@@ -282,7 +293,7 @@ function resolveEvidenceRelative(rawPath, ctx) {
|
|
|
282
293
|
if (candidate.startsWith(`${evidenceDir}/`) || candidate === evidenceDir) {
|
|
283
294
|
return candidate;
|
|
284
295
|
}
|
|
285
|
-
if (candidate.startsWith(
|
|
296
|
+
if (candidate.startsWith(`${layout.evidenceDir}/`) &&
|
|
286
297
|
!candidate.startsWith(`${evidencePrefix}/`)) {
|
|
287
298
|
throw new PlaywrightCliPolicyError("path-cross-case", "output path escapes current case evidenceDir");
|
|
288
299
|
}
|
|
@@ -463,11 +474,12 @@ function assertInputPathAllowed(rawPath, ctx) {
|
|
|
463
474
|
if (!isSafeRelativePosix(candidate)) {
|
|
464
475
|
throw new PlaywrightCliPolicyError("input-path-unsafe", `unsafe input path: ${rawPath}`);
|
|
465
476
|
}
|
|
477
|
+
const layout = ctx.layout ?? defaultFrontendTestLayout();
|
|
466
478
|
const allowedPrefixes = [
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
479
|
+
`${layout.evidenceDir}/${ctx.caseId}/`,
|
|
480
|
+
`${layout.casesDir}/`,
|
|
481
|
+
`${layout.ragDir}/`,
|
|
482
|
+
`${layout.fixturesDir}/`,
|
|
471
483
|
];
|
|
472
484
|
if (!allowedPrefixes.some((prefix) => candidate === prefix.slice(0, -1) || candidate.startsWith(prefix)) &&
|
|
473
485
|
!candidate.startsWith(normalizePosix(ctx.evidenceDir) + "/")) {
|
|
@@ -523,6 +535,17 @@ export function preparePlaywrightCliArgv(input, ctx) {
|
|
|
523
535
|
else if (command === "pdf" || command === "snapshot") {
|
|
524
536
|
args = normalizePdfOrSnapshotArgs(command, rawArgs, ctx);
|
|
525
537
|
}
|
|
538
|
+
else if (command === "find-unique") {
|
|
539
|
+
// find-unique is a controller-side semantic wrapper: the underlying CLI
|
|
540
|
+
// only knows `find`; uniqueness (matchCount === 1) is judged by the
|
|
541
|
+
// executor after parsing the find output. Require exactly one text
|
|
542
|
+
// literal — a unique-control assertion is meaningless with multiple.
|
|
543
|
+
const literals = rawArgs.filter((arg) => !arg.startsWith("-"));
|
|
544
|
+
if (rawArgs.length !== 1 || literals.length !== 1) {
|
|
545
|
+
throw new PlaywrightCliPolicyError("find-unique-args", "find-unique requires exactly one text literal and no flags");
|
|
546
|
+
}
|
|
547
|
+
args = literals;
|
|
548
|
+
}
|
|
526
549
|
else {
|
|
527
550
|
args = rewriteArgsForPaths(command, rawArgs, ctx);
|
|
528
551
|
}
|
|
@@ -609,8 +632,8 @@ async function validatePlaywrightCliOutputPath(command, args, ctx) {
|
|
|
609
632
|
if (!outputFilename)
|
|
610
633
|
return;
|
|
611
634
|
const repoRoot = path.resolve(ctx.repoRoot);
|
|
612
|
-
const caseEvidenceRoot = path.resolve(repoRoot,
|
|
613
|
-
const evidenceDir =
|
|
635
|
+
const caseEvidenceRoot = path.resolve(repoRoot, ctx.evidenceDir);
|
|
636
|
+
const evidenceDir = caseEvidenceRoot;
|
|
614
637
|
const target = path.resolve(repoRoot, outputFilename);
|
|
615
638
|
if (!pathIsContained(repoRoot, caseEvidenceRoot) ||
|
|
616
639
|
!pathIsContained(caseEvidenceRoot, evidenceDir) ||
|
|
@@ -818,7 +841,10 @@ export async function executePlaywrightCliCommand(input, ctx, signal) {
|
|
|
818
841
|
};
|
|
819
842
|
}
|
|
820
843
|
let executable = PLAYWRIGHT_CLI_EXECUTABLE;
|
|
821
|
-
|
|
844
|
+
// find-unique executes as the underlying CLI `find` command; uniqueness is
|
|
845
|
+
// a controller-side judgment on the parsed match count.
|
|
846
|
+
const cliCommand = command === "find-unique" ? "find" : command;
|
|
847
|
+
let argv = [cliCommand, ...args];
|
|
822
848
|
try {
|
|
823
849
|
await validatePlaywrightCliOutputPath(command, args, ctx);
|
|
824
850
|
await validatePlaywrightCliInputFiles(command, args, ctx);
|
|
@@ -827,7 +853,7 @@ export async function executePlaywrightCliCommand(input, ctx, signal) {
|
|
|
827
853
|
if (!ctx.runCommand) {
|
|
828
854
|
const launcher = resolvePlaywrightCliLauncher(ctx.repoRoot);
|
|
829
855
|
executable = launcher.executable;
|
|
830
|
-
argv = [...launcher.argvPrefix,
|
|
856
|
+
argv = [...launcher.argvPrefix, cliCommand, ...args];
|
|
831
857
|
}
|
|
832
858
|
}
|
|
833
859
|
catch (error) {
|
|
@@ -887,6 +913,19 @@ export async function executePlaywrightCliCommand(input, ctx, signal) {
|
|
|
887
913
|
}
|
|
888
914
|
const finishedAt = new Date();
|
|
889
915
|
const combined = `${runResult.stdout}\n${runResult.stderr}`;
|
|
916
|
+
// find-family commands report their match count in stdout with exit code
|
|
917
|
+
// 0 even on zero matches — parse the count so receipts carry the real
|
|
918
|
+
// assertion signal for finalize-side audits.
|
|
919
|
+
const matchCount = command === "find" || command === "find-unique"
|
|
920
|
+
? parsePlaywrightCliFindMatchCount(runResult.stdout || "")
|
|
921
|
+
: undefined;
|
|
922
|
+
// find-unique fails closed unless exactly one match was found (including
|
|
923
|
+
// zero and unparseable outputs).
|
|
924
|
+
const findUniqueFailed = command === "find-unique" && matchCount !== 1 && !runResult.timedOut;
|
|
925
|
+
const effectiveExitCode = findUniqueFailed && runResult.exitCode === 0 ? 1 : runResult.exitCode;
|
|
926
|
+
const effectiveStderr = findUniqueFailed
|
|
927
|
+
? `${runResult.stderr}${runResult.stderr ? "\n" : ""}find-unique: expected exactly 1 match, got ${matchCount === undefined ? "unparseable output" : `${matchCount}`}`
|
|
928
|
+
: runResult.stderr;
|
|
890
929
|
const receipt = {
|
|
891
930
|
schemaVersion: 1,
|
|
892
931
|
caseId: ctx.caseId,
|
|
@@ -895,25 +934,28 @@ export async function executePlaywrightCliCommand(input, ctx, signal) {
|
|
|
895
934
|
argsRedacted: redactPlaywrightCliArgs(command, args),
|
|
896
935
|
startedAt: startedAt.toISOString(),
|
|
897
936
|
finishedAt: finishedAt.toISOString(),
|
|
898
|
-
exitCode:
|
|
937
|
+
exitCode: effectiveExitCode,
|
|
899
938
|
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
900
939
|
outputSha256: createHash("sha256").update(combined).digest("hex"),
|
|
901
940
|
toolVersion: ctx.toolVersion ?? "unknown",
|
|
902
941
|
timedOut: runResult.timedOut || undefined,
|
|
942
|
+
...(matchCount !== undefined ? { matchCount } : {}),
|
|
903
943
|
errorClass: runResult.timedOut
|
|
904
944
|
? "timeout"
|
|
905
945
|
: runResult.signal === "abort"
|
|
906
946
|
? "abort"
|
|
907
|
-
:
|
|
947
|
+
: effectiveExitCode === 0
|
|
908
948
|
? undefined
|
|
909
|
-
:
|
|
949
|
+
: findUniqueFailed
|
|
950
|
+
? "find-unique-count-mismatch"
|
|
951
|
+
: "nonzero-exit",
|
|
910
952
|
};
|
|
911
953
|
await appendReceipt(ctx, receipt);
|
|
912
954
|
return {
|
|
913
|
-
ok:
|
|
914
|
-
exitCode:
|
|
955
|
+
ok: effectiveExitCode === 0 && !runResult.timedOut,
|
|
956
|
+
exitCode: effectiveExitCode,
|
|
915
957
|
stdout: runResult.stdout,
|
|
916
|
-
stderr:
|
|
958
|
+
stderr: effectiveStderr,
|
|
917
959
|
timedOut: runResult.timedOut,
|
|
918
960
|
receipt,
|
|
919
961
|
errorClass: receipt.errorClass,
|
|
@@ -985,22 +1027,26 @@ export async function createPlaywrightCliTool(ctx) {
|
|
|
985
1027
|
export const PI_COMMAND_CAPABILITY_REGISTRY = {
|
|
986
1028
|
"playwright-cli": createPlaywrightCliTool,
|
|
987
1029
|
};
|
|
988
|
-
export function resolveCaseIdFromWriteSet(writeSet) {
|
|
1030
|
+
export function resolveCaseIdFromWriteSet(writeSet, layout = defaultFrontendTestLayout()) {
|
|
1031
|
+
const prefix = layout.evidenceDir.replace(/\/$/, "");
|
|
1032
|
+
const escaped = escapeRegexLiteral(prefix);
|
|
1033
|
+
const exact = new RegExp(`^${escaped}/([^/]+)(?:/\\*\\*)?$`);
|
|
1034
|
+
const nested = new RegExp(`^${escaped}/([^/]+)/`);
|
|
989
1035
|
for (const entry of writeSet ?? []) {
|
|
990
1036
|
const normalized = normalizePosix(entry);
|
|
991
|
-
const match = normalized.match(
|
|
1037
|
+
const match = normalized.match(exact);
|
|
992
1038
|
if (match?.[1] && match[1] !== "**" && match[1] !== "*") {
|
|
993
1039
|
return match[1];
|
|
994
1040
|
}
|
|
995
|
-
const
|
|
996
|
-
if (
|
|
997
|
-
return
|
|
1041
|
+
const nestedMatch = normalized.match(nested);
|
|
1042
|
+
if (nestedMatch?.[1])
|
|
1043
|
+
return nestedMatch[1];
|
|
998
1044
|
}
|
|
999
1045
|
return undefined;
|
|
1000
1046
|
}
|
|
1001
|
-
export function resolveEvidenceDirFromWriteSet(writeSet) {
|
|
1002
|
-
const caseId = resolveCaseIdFromWriteSet(writeSet);
|
|
1047
|
+
export function resolveEvidenceDirFromWriteSet(writeSet, layout = defaultFrontendTestLayout()) {
|
|
1048
|
+
const caseId = resolveCaseIdFromWriteSet(writeSet, layout);
|
|
1003
1049
|
if (!caseId)
|
|
1004
1050
|
return undefined;
|
|
1005
|
-
return
|
|
1051
|
+
return `${layout.evidenceDir}/${caseId}`;
|
|
1006
1052
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { appendFile, mkdir } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { computeExtensionToolAllowlistExtras } from "./pi-extension-resolver.js";
|
|
4
|
+
import { BoundedTextPreview, classifyPiFailure, createPiJsonlStreamCollector, DEFAULT_ABORT_GRACE_MS, DEFAULT_STALL_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, extractPiEventStopReason, extractAssistantTextFromPiJson, } from "./pi-executor.js";
|
|
4
5
|
import { serializeSessionEvent } from "./pi-event-serializer.js";
|
|
5
6
|
import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
|
|
7
|
+
/** Same-session recovery after a transport stall. Exactly one continue per SDK attempt. */
|
|
8
|
+
export const PI_SDK_STALL_CONTINUE_MAX = 1;
|
|
9
|
+
/**
|
|
10
|
+
* Fixed follow-up sent after aborting a stalled prompt.
|
|
11
|
+
* Kept short so the model can resume without repeating the original task payload.
|
|
12
|
+
*/
|
|
13
|
+
export const PI_SDK_STALL_CONTINUE_PROMPT = "继续。上一轮因长时间无 provider 活动已被中止;请从中断处接着完成,不要重复已完成的步骤。若此前没有有效输出,请重新执行原任务。";
|
|
6
14
|
let sdkSessionFactoryOverride;
|
|
7
15
|
let sdkImportOverrideForTests;
|
|
8
16
|
let sdkModuleOverrideForTests;
|
|
@@ -44,15 +52,15 @@ export async function resetPiSdkReuseScopeForTests() {
|
|
|
44
52
|
export function setPiSdkSessionFactoryForTests(factory) {
|
|
45
53
|
sdkSessionFactoryOverride = factory;
|
|
46
54
|
}
|
|
47
|
-
/** Test hook: simulate SDK import success/failure without relying on
|
|
55
|
+
/** Test hook: simulate SDK import success/failure without relying on installed SDK state. */
|
|
48
56
|
export function setPiSdkImportOverrideForTests(fn) {
|
|
49
57
|
sdkImportOverrideForTests = fn;
|
|
50
58
|
}
|
|
51
|
-
/** Test hook: inject a mock Pi SDK module to exercise resolveSdkSessionFactory without
|
|
59
|
+
/** Test hook: inject a mock Pi SDK module to exercise resolveSdkSessionFactory without loading the real SDK. */
|
|
52
60
|
export function setPiSdkModuleOverrideForTests(fn) {
|
|
53
61
|
sdkModuleOverrideForTests = fn;
|
|
54
62
|
}
|
|
55
|
-
/** Check whether the Pi SDK
|
|
63
|
+
/** Check whether the Pi SDK runtime dependency satisfies the 0.83.0 runtime contract. */
|
|
56
64
|
export async function checkPiSdkAvailability(_repoRoot) {
|
|
57
65
|
if (sdkSessionFactoryOverride) {
|
|
58
66
|
return { ok: true, detail: "pi SDK session factory override active" };
|
|
@@ -68,10 +76,10 @@ export async function checkPiSdkAvailability(_repoRoot) {
|
|
|
68
76
|
typeof ModelRuntime?.create !== "function") {
|
|
69
77
|
return {
|
|
70
78
|
ok: false,
|
|
71
|
-
detail: "pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.
|
|
79
|
+
detail: "pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.83.0 contract)",
|
|
72
80
|
};
|
|
73
81
|
}
|
|
74
|
-
return { ok: true, detail: "pi SDK 0.
|
|
82
|
+
return { ok: true, detail: "pi SDK 0.83.0 contract available" };
|
|
75
83
|
}
|
|
76
84
|
catch (error) {
|
|
77
85
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -222,6 +230,9 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
222
230
|
noContextFiles: true,
|
|
223
231
|
noSkills: true,
|
|
224
232
|
noExtensions: true,
|
|
233
|
+
...(input.additionalExtensionPaths && input.additionalExtensionPaths.length > 0
|
|
234
|
+
? { additionalExtensionPaths: input.additionalExtensionPaths }
|
|
235
|
+
: {}),
|
|
225
236
|
...(settingsManager ? { settingsManager } : {}),
|
|
226
237
|
appendSystemPromptOverride: (base) => [
|
|
227
238
|
...base,
|
|
@@ -229,6 +240,19 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
229
240
|
],
|
|
230
241
|
});
|
|
231
242
|
await loader.reload();
|
|
243
|
+
// Discover extension-registered tool names BEFORE session creation (plan
|
|
244
|
+
// 2026-08-21 D5): verified against the real SDK — passing extension tool
|
|
245
|
+
// names in createAgentSession `tools` activates them, while a post-create
|
|
246
|
+
// setActiveToolsByName pin leaves the active set unchanged. Merge filtered
|
|
247
|
+
// extras into the create-time allowlist; protected builtins, the deny
|
|
248
|
+
// surface, and write-capable extension tools never join.
|
|
249
|
+
const extensionToolExtras = [];
|
|
250
|
+
if (input.additionalExtensionPaths &&
|
|
251
|
+
input.additionalExtensionPaths.length > 0) {
|
|
252
|
+
const registered = collectLoaderExtensionToolNames(loader);
|
|
253
|
+
extensionToolExtras.push(...computeExtensionToolAllowlistExtras(registered.map((name) => ({ name })), input.toolNames));
|
|
254
|
+
}
|
|
255
|
+
const sessionToolNames = [...input.toolNames, ...extensionToolExtras];
|
|
232
256
|
if (input.requireWriterCustomTools) {
|
|
233
257
|
if (!Array.isArray(input.customTools) || input.customTools.length === 0) {
|
|
234
258
|
throw new Error("pi writer tool policy missing customTools; refusing to create uncontrolled writer session");
|
|
@@ -238,7 +262,7 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
238
262
|
cwd: input.cwd,
|
|
239
263
|
sessionManager: SessionManager.inMemory(input.cwd),
|
|
240
264
|
resourceLoader: loader,
|
|
241
|
-
tools:
|
|
265
|
+
tools: sessionToolNames,
|
|
242
266
|
modelRuntime,
|
|
243
267
|
...(settingsManager ? { settingsManager } : {}),
|
|
244
268
|
...(model ? { model } : {}),
|
|
@@ -247,6 +271,22 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
247
271
|
? { customTools: input.customTools }
|
|
248
272
|
: {}),
|
|
249
273
|
});
|
|
274
|
+
if (extensionToolExtras.length > 0) {
|
|
275
|
+
// Fail closed when merged extension tools did not activate: a silent
|
|
276
|
+
// pin failure must never let the node run without its declared tools.
|
|
277
|
+
const activeTools = created.session.agent?.state?.tools;
|
|
278
|
+
if (!Array.isArray(activeTools)) {
|
|
279
|
+
throw new Error("pi SDK session does not expose agent.state.tools; refusing unverifiable extension-tool activation");
|
|
280
|
+
}
|
|
281
|
+
const activeNames = new Set(activeTools
|
|
282
|
+
.map((tool) => typeof tool?.name === "string" ? tool.name : undefined)
|
|
283
|
+
.filter((name) => Boolean(name)));
|
|
284
|
+
for (const extensionToolName of extensionToolExtras) {
|
|
285
|
+
if (!activeNames.has(extensionToolName)) {
|
|
286
|
+
throw new Error(`pi SDK extension-tool activation mismatch for ${extensionToolName}: tool did not activate at session creation`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
250
290
|
const customToolNames = (input.customTools ?? [])
|
|
251
291
|
.map((tool) => isRecord(tool) && typeof tool.name === "string" ? tool.name : undefined)
|
|
252
292
|
.filter((name) => Boolean(name));
|
|
@@ -259,7 +299,7 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
259
299
|
.map((tool) => typeof tool?.name === "string" ? tool.name : undefined)
|
|
260
300
|
.filter((name) => Boolean(name)));
|
|
261
301
|
for (const customToolName of customToolNames) {
|
|
262
|
-
const expectedActive =
|
|
302
|
+
const expectedActive = sessionToolNames.includes(customToolName);
|
|
263
303
|
if (activeNames.has(customToolName) !== expectedActive) {
|
|
264
304
|
throw new Error(`pi SDK custom-tool activation mismatch for ${customToolName}: expectedActive=${expectedActive}`);
|
|
265
305
|
}
|
|
@@ -267,6 +307,47 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
267
307
|
}
|
|
268
308
|
return created.session;
|
|
269
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Collect tool names registered by loader-resolved extensions. The SDK exposes
|
|
312
|
+
* each extension's `tools` as a Record (name → definition) or Map; both shapes
|
|
313
|
+
* are handled. Missing getExtensions (older SDK) yields no extras — the node
|
|
314
|
+
* then runs with the baseline allowlist only.
|
|
315
|
+
*/
|
|
316
|
+
function collectLoaderExtensionToolNames(loader) {
|
|
317
|
+
const getExtensions = loader?.getExtensions;
|
|
318
|
+
if (typeof getExtensions !== "function")
|
|
319
|
+
return [];
|
|
320
|
+
let parsed;
|
|
321
|
+
try {
|
|
322
|
+
parsed = getExtensions.call(loader);
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
if (!parsed || typeof parsed !== "object")
|
|
328
|
+
return [];
|
|
329
|
+
const extensions = parsed.extensions;
|
|
330
|
+
if (!Array.isArray(extensions))
|
|
331
|
+
return [];
|
|
332
|
+
const names = [];
|
|
333
|
+
for (const extension of extensions) {
|
|
334
|
+
if (!extension || typeof extension !== "object")
|
|
335
|
+
continue;
|
|
336
|
+
const tools = extension.tools;
|
|
337
|
+
if (tools instanceof Map) {
|
|
338
|
+
for (const key of tools.keys()) {
|
|
339
|
+
if (typeof key === "string")
|
|
340
|
+
names.push(key);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else if (tools && typeof tools === "object") {
|
|
344
|
+
for (const key of Object.keys(tools)) {
|
|
345
|
+
names.push(key);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return names;
|
|
350
|
+
}
|
|
270
351
|
/**
|
|
271
352
|
* Throttle high-noise Pi SDK events from session-events.jsonl.
|
|
272
353
|
* Always keeps tool lifecycle, turn/agent end, assistant messages, and errors.
|
|
@@ -404,25 +485,6 @@ function extractSdkUsageSample(event) {
|
|
|
404
485
|
const responseKey = responseKeyCandidates.find((value) => typeof value === "string" && value.length > 0);
|
|
405
486
|
return responseKey ? { responseKey, tokens } : { tokens };
|
|
406
487
|
}
|
|
407
|
-
/** Extract a non-empty stop reason from a terminal SDK event.
|
|
408
|
-
* Probes multiple field shapes across SDK versions so a missing field fails
|
|
409
|
-
* open (undefined) rather than misclassifying an attempt. */
|
|
410
|
-
function readStopReason(event) {
|
|
411
|
-
const message = isRecord(event.message) ? event.message : undefined;
|
|
412
|
-
const candidates = [
|
|
413
|
-
message?.stop_reason,
|
|
414
|
-
message?.stopReason,
|
|
415
|
-
event.stopReason,
|
|
416
|
-
event.stop_reason,
|
|
417
|
-
];
|
|
418
|
-
for (const candidate of candidates) {
|
|
419
|
-
if (typeof candidate === "string" &&
|
|
420
|
-
candidate.trim().length > 0) {
|
|
421
|
-
return candidate.trim();
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return undefined;
|
|
425
|
-
}
|
|
426
488
|
function aggregateSdkTokenUsage(samples) {
|
|
427
489
|
const identified = new Map();
|
|
428
490
|
let anonymousMaximum = 0;
|
|
@@ -444,6 +506,8 @@ function aggregateSdkTokenUsage(samples) {
|
|
|
444
506
|
* When reuseScope is active, only shared auth/model resources are reused; each attempt still
|
|
445
507
|
* creates and disposes its own session and resource loader. Session reuse across steps is
|
|
446
508
|
* deferred until a proven reset/isolation strategy exists.
|
|
509
|
+
* A transport stall may abort the current prompt and send one continue on the
|
|
510
|
+
* same session; that is in-attempt recovery, not cross-step session reuse.
|
|
447
511
|
*/
|
|
448
512
|
export async function executeSingleSdkAttempt(options) {
|
|
449
513
|
const modelConfig = options.modelConfig;
|
|
@@ -494,7 +558,7 @@ export async function executeSingleSdkAttempt(options) {
|
|
|
494
558
|
type === "message_end" ||
|
|
495
559
|
type === "agent_end" ||
|
|
496
560
|
type === "agent_settled") {
|
|
497
|
-
const candidate =
|
|
561
|
+
const candidate = extractPiEventStopReason(event);
|
|
498
562
|
if (candidate)
|
|
499
563
|
stopReason = candidate;
|
|
500
564
|
}
|
|
@@ -555,20 +619,37 @@ export async function executeSingleSdkAttempt(options) {
|
|
|
555
619
|
? { customTools: writerCustomTools }
|
|
556
620
|
: {}),
|
|
557
621
|
...(requireWriterCustomTools ? { requireWriterCustomTools: true } : {}),
|
|
622
|
+
...(options.piExtensionPaths && options.piExtensionPaths.length > 0
|
|
623
|
+
? { additionalExtensionPaths: options.piExtensionPaths }
|
|
624
|
+
: {}),
|
|
558
625
|
});
|
|
559
626
|
let resolveStall;
|
|
560
627
|
let resolveSettlement;
|
|
561
628
|
let settled = false;
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
629
|
+
let abortConfirmed = true;
|
|
630
|
+
const persistRuntimeNote = (event) => {
|
|
631
|
+
const line = serializeSessionEvent(event);
|
|
632
|
+
stdoutPreview.append(`${line}\n`);
|
|
633
|
+
stdoutCollector.append(`${line}\n`);
|
|
634
|
+
sessionEventAppender?.append(line, event);
|
|
635
|
+
};
|
|
636
|
+
const resetSettlement = () => {
|
|
637
|
+
settled = false;
|
|
638
|
+
return new Promise((resolve) => {
|
|
639
|
+
resolveSettlement = resolve;
|
|
640
|
+
});
|
|
641
|
+
};
|
|
642
|
+
const resetStallPromise = () => {
|
|
643
|
+
if (stallTimeoutMs <= 0) {
|
|
644
|
+
resolveStall = undefined;
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
return new Promise((resolve) => {
|
|
567
648
|
resolveStall = resolve;
|
|
568
|
-
})
|
|
569
|
-
|
|
649
|
+
});
|
|
650
|
+
};
|
|
570
651
|
const armStallWatchdog = () => {
|
|
571
|
-
if (
|
|
652
|
+
if (stallTimeoutMs <= 0 || !resolveStall)
|
|
572
653
|
return;
|
|
573
654
|
if (stallHandle)
|
|
574
655
|
clearTimeout(stallHandle);
|
|
@@ -609,17 +690,6 @@ export async function executeSingleSdkAttempt(options) {
|
|
|
609
690
|
stdoutCollector.append(`${line}\n`);
|
|
610
691
|
sessionEventAppender?.append(line, event);
|
|
611
692
|
});
|
|
612
|
-
const filePrefix = options.attachedFiles
|
|
613
|
-
.map((file) => `@${file}`)
|
|
614
|
-
.join(" ");
|
|
615
|
-
const promptMessage = filePrefix
|
|
616
|
-
? `${filePrefix}\n${options.userMessage}`
|
|
617
|
-
: options.userMessage;
|
|
618
|
-
const promptPromise = session.prompt(promptMessage);
|
|
619
|
-
// A settlement event is permitted to win the race while prompt() remains
|
|
620
|
-
// pending. Observe a later rejection so it never becomes unhandled.
|
|
621
|
-
void promptPromise.catch(() => { });
|
|
622
|
-
armStallWatchdog();
|
|
623
693
|
const timeoutPromise = timeoutMs > 0
|
|
624
694
|
? new Promise((resolve) => {
|
|
625
695
|
timeoutHandle = setTimeout(() => {
|
|
@@ -627,21 +697,79 @@ export async function executeSingleSdkAttempt(options) {
|
|
|
627
697
|
}, timeoutMs);
|
|
628
698
|
})
|
|
629
699
|
: null;
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
700
|
+
const runTurn = async (userMessage) => {
|
|
701
|
+
const settlementPromise = resetSettlement();
|
|
702
|
+
const stallPromise = resetStallPromise();
|
|
703
|
+
const promptPromise = session.prompt(userMessage);
|
|
704
|
+
// A settlement event is permitted to win the race while prompt() remains
|
|
705
|
+
// pending. Observe a later rejection so it never becomes unhandled.
|
|
706
|
+
void promptPromise.catch(() => { });
|
|
707
|
+
armStallWatchdog();
|
|
708
|
+
return Promise.race([
|
|
709
|
+
promptPromise.then(() => "done"),
|
|
710
|
+
settlementPromise,
|
|
711
|
+
...(timeoutPromise ? [timeoutPromise] : []),
|
|
712
|
+
...(stallPromise ? [stallPromise] : []),
|
|
713
|
+
]);
|
|
714
|
+
};
|
|
715
|
+
const disposeSession = async () => {
|
|
642
716
|
disposeAttempted = true;
|
|
643
717
|
const disposeConfirmed = await runSessionActionWithGrace("dispose", () => session.dispose());
|
|
644
718
|
terminationConfirmed = abortConfirmed && disposeConfirmed;
|
|
719
|
+
};
|
|
720
|
+
const failClosedAfterWait = async (reason, extraStderr, abortFirst = true) => {
|
|
721
|
+
timedOut = true;
|
|
722
|
+
appendStderr(reason === "stall"
|
|
723
|
+
? `pi SDK step stalled after ${stallTimeoutMs}ms with no provider activity`
|
|
724
|
+
: `pi SDK step timed out after ${timeoutMs}ms`);
|
|
725
|
+
if (extraStderr)
|
|
726
|
+
appendStderr(extraStderr);
|
|
727
|
+
if (abortFirst) {
|
|
728
|
+
abortConfirmed = await runSessionActionWithGrace("abort", () => session.abort());
|
|
729
|
+
}
|
|
730
|
+
await disposeSession();
|
|
731
|
+
};
|
|
732
|
+
const filePrefix = options.attachedFiles
|
|
733
|
+
.map((file) => `@${file}`)
|
|
734
|
+
.join(" ");
|
|
735
|
+
const promptMessage = filePrefix
|
|
736
|
+
? `${filePrefix}\n${options.userMessage}`
|
|
737
|
+
: options.userMessage;
|
|
738
|
+
let continuesSent = 0;
|
|
739
|
+
let raced = await runTurn(promptMessage);
|
|
740
|
+
if (raced === "stall") {
|
|
741
|
+
abortConfirmed = await runSessionActionWithGrace("abort", () => session.abort());
|
|
742
|
+
const withinAbsoluteTimeout = timeoutMs <= 0 || Date.now() - startedAt < timeoutMs;
|
|
743
|
+
const canContinue = abortConfirmed &&
|
|
744
|
+
writeToolCallCount === 0 &&
|
|
745
|
+
withinAbsoluteTimeout &&
|
|
746
|
+
continuesSent < PI_SDK_STALL_CONTINUE_MAX;
|
|
747
|
+
if (canContinue) {
|
|
748
|
+
// The aborted turn's stopReason must not leak onto a recovered continue.
|
|
749
|
+
stopReason = undefined;
|
|
750
|
+
continuesSent += 1;
|
|
751
|
+
persistRuntimeNote({
|
|
752
|
+
type: "loop-agent-stall-continue",
|
|
753
|
+
prompt: PI_SDK_STALL_CONTINUE_PROMPT,
|
|
754
|
+
maxContinues: PI_SDK_STALL_CONTINUE_MAX,
|
|
755
|
+
});
|
|
756
|
+
raced = await runTurn(PI_SDK_STALL_CONTINUE_PROMPT);
|
|
757
|
+
if (raced !== "done" && raced !== "settled") {
|
|
758
|
+
await failClosedAfterWait(raced === "stall" ? "stall" : "absolute-timeout", raced === "stall"
|
|
759
|
+
? "pi SDK stall continue did not recover; treating as timeout"
|
|
760
|
+
: "pi SDK stall continue hit the absolute timeout", true);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
await failClosedAfterWait("stall", !abortConfirmed
|
|
765
|
+
? undefined
|
|
766
|
+
: writeToolCallCount > 0
|
|
767
|
+
? "pi SDK stall continue skipped because write tools already ran"
|
|
768
|
+
: "pi SDK stall continue skipped because absolute timeout elapsed", false);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
else if (raced === "absolute-timeout") {
|
|
772
|
+
await failClosedAfterWait("absolute-timeout");
|
|
645
773
|
}
|
|
646
774
|
}
|
|
647
775
|
catch (error) {
|