@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.19
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 +1 -1
- package/CHANGELOG.md +68 -2309
- package/README.md +7 -0
- package/dist/application/dag/args.js +8 -0
- package/dist/application/task-lifecycle/advance.js +38 -7
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +2 -0
- package/dist/cli/program.js +3 -1
- package/dist/cli/update/init-surface-notifier.js +2 -0
- package/dist/cli/update/policy.js +2 -0
- package/dist/commands/cursor-prompt.js +2 -0
- package/dist/commands/dag-approve.js +2 -0
- package/dist/commands/dag-final-verification.js +2 -0
- package/dist/commands/dag-init-hybrid.js +2 -0
- package/dist/commands/dag-reconcile-tasks.js +2 -0
- package/dist/commands/dag-reject.js +2 -0
- package/dist/commands/dag-report.js +2 -0
- package/dist/commands/dag-rerun.js +2 -0
- package/dist/commands/dag-resume.js +2 -0
- package/dist/commands/dag-workflow-compile.js +2 -0
- package/dist/commands/dag-workflow-plan.js +2 -0
- package/dist/commands/dag-workflow-validate.js +2 -0
- package/dist/commands/delegate.js +2 -0
- package/dist/commands/examples.js +2 -0
- package/dist/commands/import-prd.js +2 -0
- package/dist/commands/init-model-catalog.js +2 -0
- package/dist/commands/init.js +19 -13
- package/dist/commands/instructions.js +2 -0
- package/dist/commands/knowledge.js +2 -0
- package/dist/commands/loop-benchmark.js +2 -0
- package/dist/commands/pi-prompt.js +2 -0
- package/dist/commands/pi-reuse-benchmark.js +2 -0
- package/dist/commands/promote-run.js +2 -0
- package/dist/commands/stats.js +1 -1
- package/dist/commands/study-init.js +2 -0
- package/dist/commands/task-advance.js +34 -0
- package/dist/commands/task-source-prepare.js +2 -0
- package/dist/commands/worktree-create.js +2 -0
- package/dist/commands/worktree-remove.js +2 -0
- package/dist/executors/dag-pi-executor.js +1640 -194
- package/dist/executors/pi-executor.js +85 -3
- package/dist/executors/pi-sdk-executor.js +18 -0
- package/dist/executors/shell-executor.js +92 -7
- package/dist/executors/shell-verification.js +3 -0
- package/dist/governance/exec-plans.js +6 -3
- package/dist/infrastructure/console/app-data.js +6 -0
- package/dist/shared/artifacts-core.js +2 -0
- package/dist/shared/backend-dogfood-preflight.js +47 -0
- package/dist/shared/dag-failure-category.js +3 -0
- package/dist/shared/git-progress.js +2 -0
- package/dist/shared/one-shot-prompt-args.js +2 -0
- package/dist/shared/operator/capabilities.js +180 -0
- package/dist/shared/package-metadata.js +6 -4
- package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
- package/dist/shared/reference-context.js +2 -0
- package/dist/task/config-types.js +29 -0
- package/dist/task/contract/project.js +9 -0
- package/dist/task/contract/recover.js +6 -0
- package/dist/task/contract/schema.js +17 -0
- package/dist/task/source-prepare/artifact-meta.js +9 -2
- package/dist/task/source-prepare/build-draft.js +60 -0
- package/dist/task/source-prepare/fragment-inventory.js +4 -1
- package/dist/task/source-prepare/parse-intent.js +36 -11
- package/dist/task/task-demand-routing.js +5 -2
- package/dist/worker/console/chat/browser-automation.js +468 -308
- package/dist/worker/console/chat/browser-routes.js +175 -57
- package/dist/worker/console/chat/explore-tools.js +5 -2
- package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +115 -20
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +24 -16
- package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
- package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
- package/dist/worker/console/chat/pi-runtime.js +95 -28
- package/dist/worker/console/chat/routes.js +20 -1
- package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
- package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
- package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
- package/dist/worker/console/chat/session-mode.js +8 -4
- package/dist/worker/console/chat/session-store.js +42 -9
- package/dist/worker/console/chat/tool-preview.js +115 -0
- package/dist/worker/console/chat/tools.js +7 -1
- package/dist/worker/console/chat/turn-order.js +13 -0
- package/dist/worker/console/chat/turn-process.js +32 -30
- package/dist/worker/console/operator-actions.js +50 -0
- package/dist/worker/console/prd-intake-bridge.js +54 -1
- package/dist/worker/console/scheduled-goal-host.js +98 -0
- package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
- package/dist/worker/console/server.js +15 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
- package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-eoQiMvuk.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
- package/dist/worker/console/static/assets/{index-wXlQohNs.js → index-Cdkvw_H6.js} +97 -97
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-BDZ9riWi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-7pKqYtpZ.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
- package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +2 -5
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
- package/dist/worker/console/static-src/prd-file-import.js +3 -1
- package/dist/worker/console/workspace-context.js +17 -1
- package/dist/worker/delivery/git-transaction.js +2 -0
- package/dist/worker/feature/fullstack-validate.js +2 -0
- package/dist/worker/loop-agent/loop-agent-client.js +8 -2
- package/dist/worker/materialize/harness-task-materializer.js +4 -1
- package/dist/worker/metrics/projector.js +1 -1
- package/dist/worker/observability/event-store.js +4 -1
- package/dist/worker/observability/read-model.js +4 -1
- package/dist/worker/observe/node-transparency.js +23 -1
- package/dist/worker/observe/routes.js +4 -0
- package/dist/worker/observe/static/operator-chrome.js +3 -1
- package/dist/worker/outcomes/declared-artifacts.js +2 -0
- package/dist/worker/preflight.js +3 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
- package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
- package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
- package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
- package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
- package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
- package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
- package/dist/worker/task-spec/validate.js +4 -1
- package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
- package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
- package/dist/workflows/dag/dag-retry-schema.js +11 -0
- package/dist/workflows/dag/decision-envelope.js +2 -0
- package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
- package/dist/workflows/dag/frontend-closeout.js +4 -2
- package/dist/workflows/dag/frontend-committed-facts.js +461 -0
- package/dist/workflows/dag/frontend-durable-tools.js +15 -3
- package/dist/workflows/dag/frontend-implementation-contract.js +365 -8
- package/dist/workflows/dag/frontend-plan-canary.js +53 -0
- package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
- package/dist/workflows/dag/frontend-plan-render.js +0 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
- package/dist/workflows/dag/frontend-recovery-run.js +57 -0
- package/dist/workflows/dag/frontend-review-context.js +43 -70
- package/dist/workflows/dag/frontend-risk.js +92 -10
- package/dist/workflows/dag/frontend-session-budget.js +117 -3
- package/dist/workflows/dag/frontend-shape.js +11 -55
- package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
- package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
- package/dist/workflows/dag/frontend-verification-trace.js +25 -12
- package/dist/workflows/dag/frontend-writer-admission.js +3 -47
- package/dist/workflows/dag/frontend-writer-status.js +0 -23
- package/dist/workflows/dag/init-hybrid.js +202 -111
- package/dist/workflows/dag/interrupt-request.js +2 -0
- package/dist/workflows/dag/lifecycle.js +11 -2
- package/dist/workflows/dag/node-execution.js +50 -13
- package/dist/workflows/dag/reconcile-run.js +2 -0
- package/dist/workflows/dag/repair-artifact.js +3 -1
- package/dist/workflows/dag/report.js +2 -0
- package/dist/workflows/dag/rerun-plan.js +10 -0
- package/dist/workflows/dag/rerun-task.js +79 -1
- package/dist/workflows/dag/retry-policy.js +18 -0
- package/dist/workflows/dag/scheduler.js +2 -4
- package/dist/workflows/dag/types.js +44 -6
- package/dist/workflows/dag/validate.js +4 -0
- package/docs/README.md +1 -0
- package/docs/governance/README.md +1 -0
- package/docs/init-surface.manifest.json +1 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +14 -10
- package/docs/templates/frontend-implementation-contract.schema.json +0 -7
- package/docs/templates/frontend-implementation-dag.json +8 -9
- package/package.json +8 -3
- package/skills/frontend-bounded-implement/SKILL.md +3 -4
- package/skills/frontend-design-review/SKILL.md +7 -12
- package/skills/frontend-design-review/references/review-checklist.md +7 -7
- package/skills/frontend-plan/SKILL.md +8 -18
- package/skills/frontend-plan/references/decision-contract.md +19 -26
- package/skills/frontend-review/SKILL.md +21 -25
- package/skills/frontend-review/references/review-findings.md +48 -24
- package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
- package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Definitions and execute() live here; ConsolePiRuntime owns caching and
|
|
4
4
|
* composition with session-bound tools.
|
|
5
5
|
*/
|
|
6
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
7
|
+
import { scheduledGoalBookingSchema } from "../../scheduled-goal-booking.js";
|
|
6
8
|
import { authorizeChatSessionTool, DEFAULT_CONSOLE_CHAT_SESSION_MODE, } from "../../session-mode.js";
|
|
7
9
|
import { buildModelCallableToolSchemas } from "../../tool-adapter.js";
|
|
8
10
|
/**
|
|
@@ -17,7 +19,7 @@ export async function buildCustomOperatorTools(options) {
|
|
|
17
19
|
import("typebox"),
|
|
18
20
|
import("@earendil-works/pi-coding-agent"),
|
|
19
21
|
]);
|
|
20
|
-
const schemas = buildModelCallableToolSchemas();
|
|
22
|
+
const schemas = buildModelCallableToolSchemas().filter((schema) => schema.toolId !== "prepareScheduledTask");
|
|
21
23
|
const ctx = options.actionContext;
|
|
22
24
|
return schemas.map((schema) => {
|
|
23
25
|
// TypeBox schema is built dynamically; cast through unknown to the builder
|
|
@@ -29,22 +31,28 @@ export async function buildCustomOperatorTools(options) {
|
|
|
29
31
|
? { description: param.description }
|
|
30
32
|
: {};
|
|
31
33
|
let t;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
case "boolean":
|
|
37
|
-
t = Type.Boolean(desc);
|
|
38
|
-
break;
|
|
39
|
-
case "number":
|
|
40
|
-
t = Type.Number(desc);
|
|
41
|
-
break;
|
|
42
|
-
case "array":
|
|
43
|
-
t = Type.Array(Type.String({}), Object.keys(desc).length ? desc : {});
|
|
44
|
-
break;
|
|
45
|
-
default:
|
|
46
|
-
t = Type.Object({}, { additionalProperties: true });
|
|
34
|
+
if (schema.toolId === "prepareScheduledGoal" && param.name === "booking") {
|
|
35
|
+
// Provider adapters need every nested type (not an opaque object).
|
|
36
|
+
// Keep the strict service schema as the single input contract.
|
|
37
|
+
t = zodToJsonSchema(scheduledGoalBookingSchema, { $refStrategy: "none" });
|
|
47
38
|
}
|
|
39
|
+
else
|
|
40
|
+
switch (param.type) {
|
|
41
|
+
case "string":
|
|
42
|
+
t = Type.String(desc);
|
|
43
|
+
break;
|
|
44
|
+
case "boolean":
|
|
45
|
+
t = Type.Boolean(desc);
|
|
46
|
+
break;
|
|
47
|
+
case "number":
|
|
48
|
+
t = Type.Number(desc);
|
|
49
|
+
break;
|
|
50
|
+
case "array":
|
|
51
|
+
t = Type.Array(Type.String({}), Object.keys(desc).length ? desc : {});
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
t = Type.Object({}, { additionalProperties: true });
|
|
55
|
+
}
|
|
48
56
|
acc[param.name] = param.required ? t : Type.Optional(t);
|
|
49
57
|
return acc;
|
|
50
58
|
}, {}), { additionalProperties: false });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
2
|
+
import { scheduledTaskRequestSchema } from "../../scheduled-goal-request.js";
|
|
3
|
+
import { buildModelCallableToolSchemas } from "../../tool-adapter.js";
|
|
4
|
+
/** Host-bound identity is not a model parameter and cannot leak across sessions. */
|
|
5
|
+
export async function buildScheduledGoalTools(options) {
|
|
6
|
+
const { Type } = await import("typebox");
|
|
7
|
+
const { defineTool } = await import("@earendil-works/pi-coding-agent");
|
|
8
|
+
const schema = buildModelCallableToolSchemas().find((tool) => tool.action === "prepareScheduledTask");
|
|
9
|
+
if (!schema)
|
|
10
|
+
throw new Error("scheduled task capability missing");
|
|
11
|
+
return [defineTool({
|
|
12
|
+
name: schema.toolId, label: "准备定时任务", description: schema.description, promptSnippet: schema.description,
|
|
13
|
+
parameters: Type.Object({ request: zodToJsonSchema(scheduledTaskRequestSchema, { $refStrategy: "none" }) }, { additionalProperties: false }),
|
|
14
|
+
async execute(callId, params) {
|
|
15
|
+
const { dispatchChatToolCall } = await import("../../session-store.js");
|
|
16
|
+
const result = await dispatchChatToolCall({ ...options.actionContext, chatSessionRef: options.sessionId }, schema.toolId, params, callId, "workflow");
|
|
17
|
+
const value = result.ok ? result.result : { ok: false, error: result.message, code: result.errorCode,
|
|
18
|
+
nextAction: "Correct the business request and retry prepareScheduledTask. Do not edit PRD/runtime files, substitute sleep/immediate execution, or claim a booking exists." };
|
|
19
|
+
return { isError: !result.ok, content: [{ type: "text", text: JSON.stringify(value) }], details: value };
|
|
20
|
+
},
|
|
21
|
+
})];
|
|
22
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/** Model exposure only. Capability registry and dispatch remain the authority. */
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { buildModelCallableToolSchemas } from "../tool-adapter.js";
|
|
4
|
+
import { computeChatActiveToolNames } from "../session-mode.js";
|
|
5
|
+
import { filterActiveInterviewTools } from "../../interview/tools.js";
|
|
6
|
+
export const TOOL_EXPOSURE_ENTRY = "console-tool-exposure";
|
|
7
|
+
export const TOOL_CATALOG_VERSION = 1;
|
|
8
|
+
export const CORE_WORKFLOW_TOOLS = Object.freeze(`prepareScheduledTask scheduledGoalGet scheduledGoalList newTask importPrd sourceInstructions contractDraftSave contractShow contractValidate contractDiff taskAdvance prepareDagConfirmation prepareDagExecution runDag operationGet operationList operationEventSummary operationWait observeLink dagReport dagDoctor dagRerunPlan dagRerun prepareMutationGate standaloneTaskRerun workerTaskRetry status planList operatorCapabilities`.split(" "));
|
|
9
|
+
const SCHEDULED_BOOKING_READ_TOOLS = new Set(["read", "find", "ls", "grep"]);
|
|
10
|
+
export function isScheduledBookingTurn(text) {
|
|
11
|
+
return /(?:预约|定时|稍后|分钟后|小时后|schedule(?:d)?|\blater\b)/i.test(text);
|
|
12
|
+
}
|
|
13
|
+
/** Keep booking turns on typed workflow actions and local read-only inspection. */
|
|
14
|
+
export function pinScheduledBookingTurnTools(session) {
|
|
15
|
+
const allowed = session.getActiveToolNames().filter(name => CORE_WORKFLOW_TOOLS.includes(name) || SCHEDULED_BOOKING_READ_TOOLS.has(name));
|
|
16
|
+
session.setActiveToolsByName(allowed);
|
|
17
|
+
return allowed;
|
|
18
|
+
}
|
|
19
|
+
export const WORKFLOW_TOOL_GROUPS = [
|
|
20
|
+
{ id: "scheduling", examples: ["prepareScheduledGoal"], description: "高级预约:已有 DAG/Task 的冻结预约", tools: ["prepareScheduledGoal"] },
|
|
21
|
+
{ id: "diagnostics", examples: ["inspect", "doctor", "dagValidate"], description: "DAG 深度诊断、校验、决策、上下文与初始化检查", tools: ["inspect", "doctor", "statsContext", "spineCheck", "dagRunTask", "dagValidate", "dagPromptOverrideConfig", "dagInterruptEligibility", "dagStatus", "dagDecisionInspect", "dagDecisionValidate"] },
|
|
22
|
+
{ id: "governance", examples: ["planCreate", "docsAudit", "closeoutTask"], description: "计划管理、文档审计、交接覆盖与任务收口", tools: ["planCreate", "planComplete", "planCheck", "docsAudit", "handoffCheck", "handoffCoverage", "coverageReport", "stats", "promoteRun", "closeoutTask", "dagCloseoutDraft"] },
|
|
23
|
+
{ id: "knowledge", examples: ["knowledgeQuery"], description: "查询项目知识", tools: ["knowledgeQuery"] },
|
|
24
|
+
{ id: "worker", examples: ["workerSchedulerStatus", "workerPoolDoctor", "workerReportMorning"], description: "Worker 任务池、功能审查、调度诊断与报表", tools: ["workerPoolDoctor", "workerFeatureReview", "workerFeatureDoctor", "workerTaskValidate", "workerBatchPlanReady", "workerReportMorning", "workerReportMetrics", "workerAdmissionShow", "workerSchedulerList", "workerSchedulerStatus", "workerSchedulerLedger", "workerSchedulerDoctor"] },
|
|
25
|
+
];
|
|
26
|
+
export function configuredToolExposure(value) {
|
|
27
|
+
if (value === undefined || value === "")
|
|
28
|
+
return undefined;
|
|
29
|
+
if (value === "full" || value === "progressive")
|
|
30
|
+
return value;
|
|
31
|
+
throw new Error("CONSOLE_CHAT_TOOL_EXPOSURE must be full or progressive");
|
|
32
|
+
}
|
|
33
|
+
/** Fail closed on catalog drift, including duplicate or human-only entries. */
|
|
34
|
+
export function validateWorkflowToolCatalog() {
|
|
35
|
+
const expected = new Set(buildModelCallableToolSchemas().map(t => t.toolId));
|
|
36
|
+
const names = [...CORE_WORKFLOW_TOOLS, ...WORKFLOW_TOOL_GROUPS.flatMap(g => [...g.tools])];
|
|
37
|
+
if (names.length !== new Set(names).size || names.length !== expected.size || names.some(n => !expected.has(n)))
|
|
38
|
+
throw new Error("WORKFLOW_TOOL_CATALOG_DRIFT");
|
|
39
|
+
}
|
|
40
|
+
export const TOOL_DISCOVERY_DESCRIPTION = "Load a workflow capability missing from the visible tools. Choose target by the user's intent from the capability directory, even when no tool name was mentioned. Pass a group ID or exact tool name; omit target if unsure. After loading, call the matching typed tool in the NEXT model response. Discovery alone does not fulfill the request, execute work or grant approval. Prefer discovery over recreating a workflow with shell or file edits.";
|
|
41
|
+
export class WorkflowToolExposure {
|
|
42
|
+
override;
|
|
43
|
+
state;
|
|
44
|
+
session;
|
|
45
|
+
interview = false;
|
|
46
|
+
manager;
|
|
47
|
+
// Failed persistence must not let the next turn silently undo full fallback.
|
|
48
|
+
// The active branch must still contain this ancestor; sibling branches do not inherit it.
|
|
49
|
+
volatileFallback;
|
|
50
|
+
constructor(manager, override) {
|
|
51
|
+
this.override = override;
|
|
52
|
+
this.manager = (manager ?? {});
|
|
53
|
+
this.state = { version: TOOL_CATALOG_VERSION, mode: override ?? "full", groups: [] };
|
|
54
|
+
try {
|
|
55
|
+
this.restore();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
this.state = { version: TOOL_CATALOG_VERSION, mode: "full", groups: [], fallbackReason: "TOOL_EXPOSURE_STATE_READ_FAILED" };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
get mode() { return this.state.mode; }
|
|
62
|
+
get snapshot() { return { ...this.state, groups: [...this.state.groups] }; }
|
|
63
|
+
restore() {
|
|
64
|
+
const entries = this.manager.getBranch?.() ?? [];
|
|
65
|
+
const latest = entries.filter(e => e.type === "custom" && e.customType === TOOL_EXPOSURE_ENTRY).at(-1);
|
|
66
|
+
if (!latest) {
|
|
67
|
+
this.state = { version: TOOL_CATALOG_VERSION, mode: this.override ?? "full", groups: [] };
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const raw = latest.data;
|
|
71
|
+
if (raw?.version !== TOOL_CATALOG_VERSION || !["full", "progressive"].includes(raw.mode ?? "") || !Array.isArray(raw.groups) || raw.groups.some(id => !WORKFLOW_TOOL_GROUPS.some(g => g.id === id))) {
|
|
72
|
+
this.state = { version: TOOL_CATALOG_VERSION, mode: "full", groups: [], fallbackReason: "INVALID_SAVED_TOOL_EXPOSURE" };
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const mode = raw.mode === "full" && raw.fallbackReason ? "full" : (this.override ?? raw.mode);
|
|
76
|
+
this.state = { version: TOOL_CATALOG_VERSION, mode, groups: [...new Set(raw.groups)], ...(mode === "full" && raw.fallbackReason ? { fallbackReason: raw.fallbackReason } : {}) };
|
|
77
|
+
}
|
|
78
|
+
persist() {
|
|
79
|
+
if (!this.manager.appendCustomEntry || !this.manager.getBranch)
|
|
80
|
+
throw new Error("TOOL_EXPOSURE_PERSISTENCE_UNAVAILABLE");
|
|
81
|
+
const previous = this.manager.getBranch().filter(e => e.type === "custom" && e.customType === TOOL_EXPOSURE_ENTRY).at(-1)?.data;
|
|
82
|
+
if (JSON.stringify(previous) !== JSON.stringify(this.state))
|
|
83
|
+
this.manager.appendCustomEntry(TOOL_EXPOSURE_ENTRY, this.snapshot);
|
|
84
|
+
}
|
|
85
|
+
exposed() {
|
|
86
|
+
if (this.state.mode === "full")
|
|
87
|
+
return undefined;
|
|
88
|
+
return [...CORE_WORKFLOW_TOOLS, ...WORKFLOW_TOOL_GROUPS.filter(g => this.state.groups.includes(g.id)).flatMap(g => [...g.tools])];
|
|
89
|
+
}
|
|
90
|
+
apply() {
|
|
91
|
+
if (!this.session)
|
|
92
|
+
throw new Error("TOOL_EXPOSURE_SESSION_UNAVAILABLE");
|
|
93
|
+
const names = computeChatActiveToolNames(this.session, "workflow", this.exposed());
|
|
94
|
+
const allowed = this.interview ? filterActiveInterviewTools(names).allowed : names;
|
|
95
|
+
this.session.setActiveToolsByName(allowed);
|
|
96
|
+
// SDK silently ignores unknown names: verify actual registration, not intent.
|
|
97
|
+
if (this.session.getAllTools) {
|
|
98
|
+
const registered = new Set(this.session.getAllTools().map(t => t.name));
|
|
99
|
+
const actual = new Set(this.session.getActiveToolNames());
|
|
100
|
+
const required = this.interview ? [] : (this.exposed() ?? CORE_WORKFLOW_TOOLS);
|
|
101
|
+
if (required.some(name => !registered.has(name) || !actual.has(name)))
|
|
102
|
+
throw new Error("TOOL_EXPOSURE_REQUIRED_TOOL_MISSING");
|
|
103
|
+
if (allowed.some(n => registered.has(n) && !actual.has(n)) || [...actual].some(n => !allowed.includes(n)))
|
|
104
|
+
throw new Error("TOOL_EXPOSURE_ACTIVATION_MISMATCH");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
fallback(reason) {
|
|
108
|
+
this.state = { version: TOOL_CATALOG_VERSION, mode: "full", groups: [], fallbackReason: reason };
|
|
109
|
+
this.apply(); // A failure here is fatal; never pretend full mode is active.
|
|
110
|
+
try {
|
|
111
|
+
this.persist();
|
|
112
|
+
this.volatileFallback = undefined;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
let anchor;
|
|
116
|
+
try {
|
|
117
|
+
anchor = this.manager.getBranch?.().at(-1);
|
|
118
|
+
}
|
|
119
|
+
catch { /* Conservatively keep full when branch cannot be read. */ }
|
|
120
|
+
this.volatileFallback = { anchor, state: this.snapshot };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
pin(session, interview = false) {
|
|
124
|
+
this.session = session;
|
|
125
|
+
this.interview = interview;
|
|
126
|
+
// Reconstruct from the active branch, including after compaction/reopen.
|
|
127
|
+
try {
|
|
128
|
+
const pending = this.volatileFallback;
|
|
129
|
+
const branch = this.manager.getBranch?.() ?? [];
|
|
130
|
+
if (pending && (!pending.anchor || branch.some(entry => entry === pending.anchor ||
|
|
131
|
+
(pending.anchor?.id !== undefined && entry.id === pending.anchor.id)))) {
|
|
132
|
+
this.state = { ...pending.state, groups: [...pending.state.groups] };
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.volatileFallback = undefined;
|
|
136
|
+
this.restore();
|
|
137
|
+
}
|
|
138
|
+
validateWorkflowToolCatalog();
|
|
139
|
+
this.apply();
|
|
140
|
+
// Full mode also records the session policy when supported.
|
|
141
|
+
if (this.mode === "progressive" || this.manager.appendCustomEntry) {
|
|
142
|
+
this.persist();
|
|
143
|
+
this.volatileFallback = undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
this.fallback(this.state.fallbackReason ?? "TOOL_EXPOSURE_RESTORE_OR_ACTIVATION_FAILED");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
discover(target) {
|
|
151
|
+
if (this.interview)
|
|
152
|
+
return { ok: false, code: "TOOL_DISCOVERY_INTERVIEW_DENIED" };
|
|
153
|
+
const catalog = WORKFLOW_TOOL_GROUPS.map(g => ({ id: g.id, description: g.description, examples: [...g.examples], active: this.mode === "full" || this.state.groups.includes(g.id) }));
|
|
154
|
+
if (!target)
|
|
155
|
+
return { ok: true, ...this.snapshot, catalog };
|
|
156
|
+
if (CORE_WORKFLOW_TOOLS.includes(target)) {
|
|
157
|
+
if (!this.session?.getActiveToolNames().includes(target))
|
|
158
|
+
return { ok: false, code: "TOOL_EXPOSURE_REQUIRED_TOOL_MISSING" };
|
|
159
|
+
return { ok: true, alreadyActive: true, tools: [target] };
|
|
160
|
+
}
|
|
161
|
+
const group = WORKFLOW_TOOL_GROUPS.find(g => g.id === target || g.tools.includes(target));
|
|
162
|
+
if (!group)
|
|
163
|
+
return { ok: false, code: "TOOL_DISCOVERY_NOT_FOUND", catalog };
|
|
164
|
+
if (!this.session)
|
|
165
|
+
return { ok: false, code: "TOOL_EXPOSURE_SESSION_UNAVAILABLE" };
|
|
166
|
+
const registered = new Set(this.session.getAllTools?.().map(t => t.name) ?? []);
|
|
167
|
+
if (group.tools.some(n => !registered.has(n))) {
|
|
168
|
+
this.fallback("TOOL_EXPOSURE_GROUP_UNAVAILABLE");
|
|
169
|
+
return { ok: false, code: "TOOL_EXPOSURE_GROUP_UNAVAILABLE", ...this.snapshot };
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
this.state = { ...this.state, groups: [...new Set([...this.state.groups, group.id])] };
|
|
173
|
+
this.apply();
|
|
174
|
+
this.persist();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
this.fallback("TOOL_EXPOSURE_LOAD_FAILED");
|
|
178
|
+
}
|
|
179
|
+
return { ok: true, ...this.snapshot, group: group.id, tools: [...group.tools], effective: "next-model-response", guidance: "Use the activated typed tools in your next response. Loading has not executed any business action." };
|
|
180
|
+
}
|
|
181
|
+
buildTool(fullTool) {
|
|
182
|
+
return { name: "operatorCapabilities", label: "发现工作流工具", description: TOOL_DISCOVERY_DESCRIPTION,
|
|
183
|
+
// One short navigation line; detailed semantics live in schema description.
|
|
184
|
+
promptSnippet: "工作流能力目录:按用户意图选组;所需能力未显示时先调用 operatorCapabilities(target=组ID),再调用加载后的业务工具完成请求:" + WORKFLOW_TOOL_GROUPS.map(g => `${g.id}: ${g.description} (${g.examples.join(", ")})`).join("; "),
|
|
185
|
+
parameters: Type.Object({ target: Type.Optional(Type.String({ maxLength: 80, description: "Group ID or exact tool name; omit to list groups." })) }, { additionalProperties: false }),
|
|
186
|
+
execute: async (_id, params, signal) => {
|
|
187
|
+
if (signal?.aborted)
|
|
188
|
+
throw new Error("Tool discovery aborted");
|
|
189
|
+
if (this.mode === "full" && !this.interview && !params.target && fullTool)
|
|
190
|
+
return fullTool.execute(_id, {});
|
|
191
|
+
const result = this.discover(params.target);
|
|
192
|
+
return { isError: result.ok === false, content: [{ type: "text", text: JSON.stringify(result) }], details: result };
|
|
193
|
+
} };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -37,6 +37,7 @@ import { authorizeChatSessionTool, DEFAULT_CONSOLE_CHAT_SESSION_MODE, chatActive
|
|
|
37
37
|
import { buildAgentCustomTool, ExploreOrchestrator, } from "./subagents/index.js";
|
|
38
38
|
import { createOperatorChatResourceLoader, } from "./resource-loader.js";
|
|
39
39
|
import { applySkillAutoInvocationPreferences, loadResourcePreferences, } from "./resource-preferences-store.js";
|
|
40
|
+
import { buildPiModeLoopIsolationOptions } from "./pi-mode-loop-isolation.js";
|
|
40
41
|
import { applyProcessRetryOverride, } from "../../../shared/pi-retry-settings.js";
|
|
41
42
|
import { applyProcessCompactionOverride, createCompactionController, createContextPressureExtension, createSiftBridgeState, housekeepSiftStash, isConsoleSiftEnabled, parseCompactionPolicy, parseSiftBridgeConfig, projectPromptPressure, deriveEffectiveWindow, projectSafeTelemetry, emptyContextPressureCounters, } from "../../../shared/pi-context-pressure/index.js";
|
|
42
43
|
import { incrementCounter as incrementPressureCounter } from "../../../shared/pi-context-pressure/telemetry.js";
|
|
@@ -49,6 +50,8 @@ import { buildPersistentTerminalTools } from "./terminal-tools.js";
|
|
|
49
50
|
import { buildScmTools } from "./scm-tools.js";
|
|
50
51
|
import { buildGoalContinuationContext, } from "./goals.js";
|
|
51
52
|
import { injectWizardLocalHeaders, } from "./sdd-data-alignment.js";
|
|
53
|
+
import { buildScheduledGoalTools } from "./pi-runtime/custom-tools/scheduled-goal-tools.js";
|
|
54
|
+
import { WorkflowToolExposure, configuredToolExposure, isScheduledBookingTurn, pinScheduledBookingTurnTools, } from "./pi-runtime/progressive-tools.js";
|
|
52
55
|
import { buildCustomOperatorTools } from "./pi-runtime/custom-tools/operator-tools.js";
|
|
53
56
|
import { buildBrowserTools } from "./pi-runtime/custom-tools/browser-tools.js";
|
|
54
57
|
import { buildAskUserQuestionTool } from "./pi-runtime/custom-tools/ask-user-question.js";
|
|
@@ -80,13 +83,14 @@ import { createDefaultPiSdkBindings, safeGetAgentDir, } from "./pi-runtime/sdk-b
|
|
|
80
83
|
*/
|
|
81
84
|
export const OPERATOR_CHAT_SYSTEM_PROMPT_BASE = [
|
|
82
85
|
"You are General Operator Chat.",
|
|
83
|
-
"
|
|
86
|
+
"For workflow requests use operator tools; discover missing capabilities via operatorCapabilities(target). Obey AGENTS.md. Deny apply_patch/full-tools/shell/coding-chat; probe with safe-read/safe-grep. Browser_*: visible Browser panel only (static http(s), no workspace edits/scripts).",
|
|
84
87
|
"No harness and asked to init: `loop-agent init instructions --repo-root .`, then `loop-agent init --repo-root . --profile full --merge`; verify with doctor, inspect and docs audit. Updates: `loop-agent init upgrade --repo-root . --json` until stable; `loop-agent init check-update` is read-only.",
|
|
88
|
+
"Scheduled intent takes precedence over task content: prepareScheduledTask first; no curl/HTTP probes or tests; never perform the scheduled work now. Only a planned goalId means booked; never self-confirm.",
|
|
85
89
|
"Long DAGs: prepareDagExecution → runDag → operationId. Never foreground-Bash task advance, tail/head pipes or nohup/Start-Process/start; do not end on accepted/queued/running or an operationId — NOT completion. Supervise operationGet/status/dagReport/dagDoctor/operationWait at 60s → 90s → 120s → 180s; reset to 60s on state change; 30-60s re-checks when stall suspected. operationWait MUST use afterSeq=previous nextSeq (recommendedNextCall). Heartbeats: not meaningful progress. Run id ≠ task id.",
|
|
86
|
-
"Failure: terminal runSummary
|
|
87
|
-
"
|
|
88
|
-
"Continue until success, user stop, or no safe recovery remains
|
|
89
|
-
"
|
|
90
|
+
"Failure: dagReport(runId) → terminal runSummary. With progress wait; else dagRerunPlan(fromNode=primaryFailure.nodeId); eligible → dagRerun: reruns the failed subgraph, keeps prior successes. Ineligible: Worker → workerTaskRetry; standalone → standaloneTaskRerun after contract adopt (regenerates the whole task, not node continue). No dagResume or re-advance of run-failed tasks. Pre-run identical state twice: stop and report; never loop identical bash/read/grep. Approve only bounded writeSet inside allowedPaths, outside forbiddenPaths, with structured verification.",
|
|
91
|
+
"Each bounded round: summarize the previous round's error in one line, fix its root cause — not the symptom — then rerun the failed node with a fresh plan. Never resubmit an unchanged fix; after two rounds failing the same way, stop and report evidence and remaining routes.",
|
|
92
|
+
"Continue until success, user stop, or no safe recovery remains. Never create a new task for a transient failure or replace repair-pi with direct edits.",
|
|
93
|
+
"Report start, meaningful phase changes, failures, prolonged quiet and completion: what finished and what is next. Never narrate unchanged operationWait polls or heartbeats.",
|
|
90
94
|
].join("\n");
|
|
91
95
|
/**
|
|
92
96
|
* Built-in SDK tool names registered for every Chat session (ADR 0011).
|
|
@@ -387,6 +391,7 @@ export class ConsolePiRuntime {
|
|
|
387
391
|
/** Non-sensitive models.json metadata used to avoid reparsing unchanged config. */
|
|
388
392
|
modelConfigFiles = new Map();
|
|
389
393
|
sessionManagers = new Map();
|
|
394
|
+
toolExposures = new Map();
|
|
390
395
|
/** Per-session SDK service scope (resourceLoader / settingsManager) used by the live runtime snapshot. */
|
|
391
396
|
serviceScopes = new Map();
|
|
392
397
|
/** Snapshot revision: bumped on create / reopen / model / thinking / reload. */
|
|
@@ -418,11 +423,17 @@ export class ConsolePiRuntime {
|
|
|
418
423
|
exploreOrchestrator;
|
|
419
424
|
bindings;
|
|
420
425
|
browserAutomation;
|
|
426
|
+
ownsBrowserAutomation;
|
|
421
427
|
constructor(options) {
|
|
422
428
|
this.options = options;
|
|
423
429
|
this.loader = createOperatorChatResourceLoader();
|
|
424
430
|
this.bindings = options.bindings ?? createDefaultPiSdkBindings();
|
|
425
|
-
this.
|
|
431
|
+
this.ownsBrowserAutomation = options.browserAutomation === undefined;
|
|
432
|
+
this.browserAutomation = options.browserAutomation ?? new BrowserAutomationService({
|
|
433
|
+
profileDir: path.join(this.options.sessionDir, "..", "browser-profile"),
|
|
434
|
+
downloadsDir: path.join(this.options.sessionDir, "..", "browser-downloads"),
|
|
435
|
+
screenshotsDir: path.join(this.options.sessionDir, "..", "browser-screenshots"),
|
|
436
|
+
});
|
|
426
437
|
this.exploreOrchestrator = new ExploreOrchestrator({
|
|
427
438
|
cwd: this.options.cwd,
|
|
428
439
|
bindings: this.bindings,
|
|
@@ -483,7 +494,7 @@ export class ConsolePiRuntime {
|
|
|
483
494
|
this.sessionModes.set(sessionId, sessionMode);
|
|
484
495
|
return sessionMode;
|
|
485
496
|
}
|
|
486
|
-
async buildResourceLoaderOptions(base = {}, sessionId) {
|
|
497
|
+
async buildResourceLoaderOptions(base = {}, sessionId, sessionMode = DEFAULT_CONSOLE_CHAT_SESSION_MODE) {
|
|
487
498
|
const inheritedFactories = Array.isArray(base.extensionFactories)
|
|
488
499
|
? base.extensionFactories
|
|
489
500
|
: [];
|
|
@@ -514,6 +525,7 @@ export class ConsolePiRuntime {
|
|
|
514
525
|
pressureExtension,
|
|
515
526
|
wizardLocalHeadersExtension,
|
|
516
527
|
].filter((item) => Boolean(item));
|
|
528
|
+
const piMode = sessionMode === "pi";
|
|
517
529
|
return {
|
|
518
530
|
...base,
|
|
519
531
|
...(extraFactories.length > 0
|
|
@@ -521,18 +533,23 @@ export class ConsolePiRuntime {
|
|
|
521
533
|
extensionFactories: [...inheritedFactories, ...extraFactories],
|
|
522
534
|
}
|
|
523
535
|
: {}),
|
|
524
|
-
|
|
536
|
+
...buildPiModeLoopIsolationOptions({
|
|
537
|
+
enabled: piMode,
|
|
538
|
+
cwd: this.options.cwd,
|
|
525
539
|
// Synchronous override surface: use last-known prefs via a sync
|
|
526
540
|
// fail-closed path. Prefer async cache filled by ensurePrefs.
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
541
|
+
readPreferences: () => this.cachedSkillPrefs,
|
|
542
|
+
skillsOverride: (current) => {
|
|
543
|
+
const prefs = this.cachedSkillPrefs ?? {
|
|
544
|
+
schemaVersion: 1,
|
|
545
|
+
skills: {},
|
|
546
|
+
};
|
|
547
|
+
return {
|
|
548
|
+
skills: applySkillAutoInvocationPreferences(current.skills, prefs),
|
|
549
|
+
diagnostics: current.diagnostics,
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
}),
|
|
536
553
|
};
|
|
537
554
|
}
|
|
538
555
|
/** Last successfully loaded skill prefs (sync override + snapshot projection). */
|
|
@@ -852,6 +869,9 @@ export class ConsolePiRuntime {
|
|
|
852
869
|
const aborted = () => Boolean(init.abortIfDisposed && this.disposedSessions.has(init.sessionId));
|
|
853
870
|
if (aborted())
|
|
854
871
|
return undefined;
|
|
872
|
+
if (init.sessionMode === "workflow" && this.options.actionContext) {
|
|
873
|
+
this.toolExposures.set(init.sessionId, new WorkflowToolExposure(init.sessionManager, this.options.toolExposure ?? configuredToolExposure(process.env.CONSOLE_CHAT_TOOL_EXPOSURE)));
|
|
874
|
+
}
|
|
855
875
|
const agentDir = await safeGetAgentDir(this.bindings);
|
|
856
876
|
// Capture before services parse models.json. A concurrent edit can therefore
|
|
857
877
|
// cause at most one harmless extra refresh, never a missed next-turn refresh.
|
|
@@ -865,7 +885,7 @@ export class ConsolePiRuntime {
|
|
|
865
885
|
const { services } = await this.bindings.createServices({
|
|
866
886
|
cwd: this.options.cwd,
|
|
867
887
|
agentDir,
|
|
868
|
-
resourceLoaderOptions: await this.buildResourceLoaderOptions({ appendSystemPrompt }, init.sessionId),
|
|
888
|
+
resourceLoaderOptions: await this.buildResourceLoaderOptions({ appendSystemPrompt }, init.sessionId, init.sessionMode),
|
|
869
889
|
});
|
|
870
890
|
if (aborted())
|
|
871
891
|
return undefined;
|
|
@@ -907,8 +927,19 @@ export class ConsolePiRuntime {
|
|
|
907
927
|
}
|
|
908
928
|
return undefined;
|
|
909
929
|
}
|
|
910
|
-
|
|
911
|
-
|
|
930
|
+
try {
|
|
931
|
+
await bindSessionExtensions(session);
|
|
932
|
+
this.pinSessionTools(init.sessionId, session);
|
|
933
|
+
}
|
|
934
|
+
catch (error) {
|
|
935
|
+
try {
|
|
936
|
+
session.dispose();
|
|
937
|
+
}
|
|
938
|
+
finally {
|
|
939
|
+
this.toolExposures.delete(init.sessionId);
|
|
940
|
+
}
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
912
943
|
this.applySessionRecoveryOverrides(init.sessionId, services, session);
|
|
913
944
|
return {
|
|
914
945
|
session,
|
|
@@ -957,6 +988,7 @@ export class ConsolePiRuntime {
|
|
|
957
988
|
}
|
|
958
989
|
this.sessions.delete(sessionId);
|
|
959
990
|
this.sessionManagers.delete(sessionId);
|
|
991
|
+
this.toolExposures.delete(sessionId);
|
|
960
992
|
this.modelRuntimes.delete(sessionId);
|
|
961
993
|
this.modelConfigFiles.delete(sessionId);
|
|
962
994
|
this.sessionModels.delete(sessionId);
|
|
@@ -966,6 +998,20 @@ export class ConsolePiRuntime {
|
|
|
966
998
|
this.thinkingSelections.delete(sessionId);
|
|
967
999
|
}
|
|
968
1000
|
}
|
|
1001
|
+
pinSessionTools(sessionId, session, interview = false) {
|
|
1002
|
+
const exposure = this.toolExposures.get(sessionId);
|
|
1003
|
+
if (exposure) {
|
|
1004
|
+
exposure.pin(session, interview);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
if (interview) {
|
|
1008
|
+
session.setActiveToolsByName(filterActiveInterviewTools(chatActiveToolBaseline(this.sessionModeOf(sessionId))).allowed);
|
|
1009
|
+
}
|
|
1010
|
+
else
|
|
1011
|
+
pinOperatorChatTools(session, this.sessionModeOf(sessionId));
|
|
1012
|
+
}
|
|
1013
|
+
/** Read-only rollout diagnostics; no model parameters or host identities. */
|
|
1014
|
+
getToolExposure(sessionId) { return this.toolExposures.get(sessionId)?.snapshot; }
|
|
969
1015
|
/** Cached session-less custom tool definitions (operator + explore). */
|
|
970
1016
|
async loadCustomTools(sessionId) {
|
|
971
1017
|
const sessionMode = this.sessionModeOf(sessionId);
|
|
@@ -976,6 +1022,8 @@ export class ConsolePiRuntime {
|
|
|
976
1022
|
: Promise.resolve([]),
|
|
977
1023
|
this.loadExploreCustomTools(),
|
|
978
1024
|
]);
|
|
1025
|
+
const scheduledGoalTools = loadWorkflowTools && this.options.actionContext
|
|
1026
|
+
? await buildScheduledGoalTools({ sessionId, actionContext: this.options.actionContext }) : [];
|
|
979
1027
|
const browserTools = await buildBrowserTools({
|
|
980
1028
|
automation: this.browserAutomation,
|
|
981
1029
|
});
|
|
@@ -1029,8 +1077,14 @@ export class ConsolePiRuntime {
|
|
|
1029
1077
|
process.stderr.write(`[console] chat Agent tool build failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
1030
1078
|
return undefined;
|
|
1031
1079
|
});
|
|
1080
|
+
const exposure = this.toolExposures.get(sessionId);
|
|
1081
|
+
const fullCapabilities = operatorCustomTools.find(tool => tool.name === "operatorCapabilities");
|
|
1082
|
+
// Always session-bound: a branch can restore progressive after full fallback.
|
|
1083
|
+
const discovery = exposure?.buildTool(fullCapabilities);
|
|
1032
1084
|
return [
|
|
1033
|
-
...operatorCustomTools,
|
|
1085
|
+
...operatorCustomTools.filter(tool => !discovery || tool.name !== "operatorCapabilities"),
|
|
1086
|
+
...(discovery ? [discovery] : []),
|
|
1087
|
+
...scheduledGoalTools,
|
|
1034
1088
|
...exploreCustomTools,
|
|
1035
1089
|
...(questionTool ? [questionTool] : []),
|
|
1036
1090
|
...(todoTool ? [todoTool] : []),
|
|
@@ -1120,6 +1174,7 @@ export class ConsolePiRuntime {
|
|
|
1120
1174
|
const entry = input.summary
|
|
1121
1175
|
? manager.branchWithSummary?.(input.branchFromEntryId, input.summary)
|
|
1122
1176
|
: (manager.branch(input.branchFromEntryId), undefined);
|
|
1177
|
+
this.pinSessionTools(input.sessionId, session);
|
|
1123
1178
|
return {
|
|
1124
1179
|
branchedFromEntryId: input.branchFromEntryId,
|
|
1125
1180
|
...(entry?.id ? { branchSummaryEntryId: entry.id } : {}),
|
|
@@ -1131,6 +1186,9 @@ export class ConsolePiRuntime {
|
|
|
1131
1186
|
throw new Error(`chat session not active: ${sessionId}`);
|
|
1132
1187
|
manager.branch(this.mainlineLeaves.get(sessionId));
|
|
1133
1188
|
this.mainlineLeaves.delete(sessionId);
|
|
1189
|
+
const session = this.sessions.get(sessionId);
|
|
1190
|
+
if (session)
|
|
1191
|
+
this.pinSessionTools(sessionId, session);
|
|
1134
1192
|
}
|
|
1135
1193
|
getBranchContext(sessionId) {
|
|
1136
1194
|
const manager = this.sessionManagers.get(sessionId);
|
|
@@ -1390,7 +1448,7 @@ export class ConsolePiRuntime {
|
|
|
1390
1448
|
if (services)
|
|
1391
1449
|
this.applySessionRecoveryOverrides(sessionId, services, session);
|
|
1392
1450
|
await this.refreshModelConfiguration(sessionId, "force");
|
|
1393
|
-
|
|
1451
|
+
this.pinSessionTools(sessionId, session);
|
|
1394
1452
|
}
|
|
1395
1453
|
catch (error) {
|
|
1396
1454
|
return {
|
|
@@ -1532,7 +1590,14 @@ export class ConsolePiRuntime {
|
|
|
1532
1590
|
extensions: inventories.extensions,
|
|
1533
1591
|
packages: inventories.packages,
|
|
1534
1592
|
mcp: inventories.mcp,
|
|
1535
|
-
diagnostics:
|
|
1593
|
+
diagnostics: [
|
|
1594
|
+
...inventories.diagnostics,
|
|
1595
|
+
...(this.toolExposures.has(sessionId) ? [{
|
|
1596
|
+
type: "info",
|
|
1597
|
+
code: "WORKFLOW_TOOL_EXPOSURE",
|
|
1598
|
+
message: JSON.stringify(this.toolExposures.get(sessionId).snapshot),
|
|
1599
|
+
}] : []),
|
|
1600
|
+
],
|
|
1536
1601
|
hasBash: true,
|
|
1537
1602
|
});
|
|
1538
1603
|
}
|
|
@@ -1849,11 +1914,10 @@ export class ConsolePiRuntime {
|
|
|
1849
1914
|
// explicitly restores the normal Operator Chat set (baseline + extension
|
|
1850
1915
|
// tools, ADR 0012).
|
|
1851
1916
|
const sessionMode = this.sessionModeOf(sessionId);
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
session.setActiveToolsByName(activeTools);
|
|
1917
|
+
this.pinSessionTools(sessionId, session, options?.mode === "requirement-interview" && sessionMode === "workflow");
|
|
1918
|
+
if (sessionMode === "workflow" && isScheduledBookingTurn(text)) {
|
|
1919
|
+
pinScheduledBookingTurnTools(session);
|
|
1920
|
+
}
|
|
1857
1921
|
// Final pre-prompt fence: abort after tool pin must still skip session.prompt.
|
|
1858
1922
|
if (options?.signal?.aborted) {
|
|
1859
1923
|
return;
|
|
@@ -2186,6 +2250,7 @@ export class ConsolePiRuntime {
|
|
|
2186
2250
|
}
|
|
2187
2251
|
this.sessions.delete(sessionId);
|
|
2188
2252
|
this.sessionManagers.delete(sessionId);
|
|
2253
|
+
this.toolExposures.delete(sessionId);
|
|
2189
2254
|
this.mainlineLeaves.delete(sessionId);
|
|
2190
2255
|
this.modelRuntimes.delete(sessionId);
|
|
2191
2256
|
this.modelConfigFiles.delete(sessionId);
|
|
@@ -2202,6 +2267,8 @@ export class ConsolePiRuntime {
|
|
|
2202
2267
|
for (const id of [...this.sessions.keys()])
|
|
2203
2268
|
this.dispose(id);
|
|
2204
2269
|
void this.exploreOrchestrator.abortAll();
|
|
2270
|
+
if (this.ownsBrowserAutomation)
|
|
2271
|
+
void this.browserAutomation.dispose();
|
|
2205
2272
|
}
|
|
2206
2273
|
}
|
|
2207
2274
|
function mapAssistantMessageEvent(type, sessionId, message) {
|
|
@@ -134,6 +134,16 @@ export function projectStoredMessagesWithEventOrder(messages, events) {
|
|
|
134
134
|
if (event.kind !== expectedKind)
|
|
135
135
|
continue;
|
|
136
136
|
const data = event.data;
|
|
137
|
+
// Exact alignment wins: since 2026-09-13 message_end frames carry the
|
|
138
|
+
// durable assistant message id, so restore matches by id, never by
|
|
139
|
+
// text (duplicate assistant texts would mis-align within a turn).
|
|
140
|
+
if (typeof data.messageId === "string" && data.messageId) {
|
|
141
|
+
if (data.messageId === message.id) {
|
|
142
|
+
assistantCursor = index + 1;
|
|
143
|
+
return event;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
137
147
|
const eventText = typeof data.text === "string"
|
|
138
148
|
? data.text
|
|
139
149
|
: typeof data.message === "string"
|
|
@@ -271,6 +281,15 @@ function turnEventToStorePartial(event) {
|
|
|
271
281
|
text: String(event.text ?? ""),
|
|
272
282
|
...(content ? { content } : {}),
|
|
273
283
|
...(timing ? { timing } : {}),
|
|
284
|
+
// Durable assistant message id (2026-09-13): the session
|
|
285
|
+
// store persists message_end BEFORE framing, so a reconnecting
|
|
286
|
+
// client and the restore projection can align on this id
|
|
287
|
+
// instead of matching text within a time window.
|
|
288
|
+
...(typeof event.messageId ===
|
|
289
|
+
"string" &&
|
|
290
|
+
event.messageId
|
|
291
|
+
? { messageId: event.messageId }
|
|
292
|
+
: {}),
|
|
274
293
|
},
|
|
275
294
|
};
|
|
276
295
|
}
|
|
@@ -4279,7 +4298,7 @@ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
|
|
|
4279
4298
|
return true;
|
|
4280
4299
|
}
|
|
4281
4300
|
}
|
|
4282
|
-
//
|
|
4301
|
+
// Shared real-browser API for the Browser panel and chat browser tools.
|
|
4283
4302
|
if (await handleBrowserRequest(req, res, {
|
|
4284
4303
|
automation: deps.browserAutomation,
|
|
4285
4304
|
requireWorkspace: () => workspaceScope
|