@tea-agent/loop-agent 0.44.0-next.1 → 0.44.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +379 -416
- package/dist/application/evaluation/budget.js +21 -0
- package/dist/application/evaluation/corpus-hash.js +10 -15
- package/dist/application/evaluation/corpus.js +2 -1
- package/dist/application/evaluation/frontend-browser-acceptance.js +77 -0
- package/dist/application/evaluation/frontend-corpus-browser-acceptance.js +175 -0
- package/dist/application/evaluation/frontend-gateway-evidence.js +226 -0
- package/dist/application/evaluation/frontend-pair-registration.js +143 -0
- package/dist/application/evaluation/frontend-paired-summary.js +150 -0
- package/dist/application/evaluation/frontend-run-observation.js +144 -0
- package/dist/application/evaluation/frontend-shared-evidence.js +105 -0
- package/dist/application/evaluation/types.js +45 -12
- package/dist/application/task-lifecycle/observe.js +26 -1
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +11 -0
- package/dist/cli/program.js +4 -0
- package/dist/commands/task-advance.js +3 -5
- package/dist/commands/task-source-prepare.js +13 -1
- package/dist/executors/dag-pi/guards.js +4 -0
- package/dist/executors/dag-pi/model-config.js +30 -0
- package/dist/executors/dag-pi/plan/fact-adoption.js +55 -0
- package/dist/{workflows/dag/frontend-plan-completeness.js → executors/dag-pi/plan/facts.js} +22 -28
- package/dist/executors/dag-pi/plan/finalize-tools.js +259 -0
- package/dist/executors/dag-pi/plan/ledger-contract.js +55 -0
- package/dist/executors/dag-pi/plan/record-tools.js +861 -0
- package/dist/executors/dag-pi/plan/schema.js +129 -0
- package/dist/executors/dag-pi/plan/session.js +1 -0
- package/dist/executors/dag-pi/plan/tool-helpers.js +16 -0
- package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
- package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
- package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
- package/dist/executors/dag-pi-executor.js +398 -3384
- package/dist/executors/pi-executor.js +6 -1
- package/dist/executors/pi-sdk-executor.js +112 -19
- package/dist/executors/shell-executor.js +88 -12
- package/dist/executors/shell-presets.js +1 -1
- package/dist/executors/shell-write-guard.js +26 -3
- package/dist/infrastructure/console/operation-store.js +48 -14
- package/dist/infrastructure/evaluation/corpus-store.js +37 -12
- package/dist/shared/file-identity.js +23 -0
- package/dist/shared/git-workspace-status.js +482 -0
- package/dist/shared/operator/capabilities.js +10 -5
- package/dist/shared/path-safety.js +2 -2
- package/dist/shared/wizard-local-telemetry.js +51 -0
- package/dist/task/config-types.js +22 -8
- package/dist/task/contract/project.js +13 -0
- package/dist/task/contract/schema.js +2 -7
- package/dist/task/frontend-preflight.js +1 -1
- package/dist/task/source-prepare/parse-intent.js +124 -5
- package/dist/task/source-prepare/prepare.js +6 -1
- package/dist/task/source-prepare/semantic-intake.js +18 -3
- package/dist/task/source-prepare/source-execution.js +65 -0
- package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
- package/dist/task/source-prepare/source-provider-budget.js +290 -0
- package/dist/worker/console/chat/assistant-content.js +5 -34
- package/dist/worker/console/chat/chat-event-store.js +62 -10
- package/dist/worker/console/chat/context-insights.js +723 -0
- package/dist/worker/console/chat/conversation-nodes.js +383 -0
- package/dist/worker/console/chat/db-connections.js +854 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
- package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
- package/dist/worker/console/chat/pi-runtime.js +445 -36
- package/dist/worker/console/chat/provider-error.js +0 -12
- package/dist/worker/console/chat/repo-browser.js +52 -4
- package/dist/worker/console/chat/routes.js +577 -72
- package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
- package/dist/worker/console/chat/session-store.js +147 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/timeline-message-order.js +22 -0
- package/dist/worker/console/chat/tools.js +34 -0
- package/dist/worker/console/chat/turn-process.js +237 -66
- package/dist/worker/console/chat/user-turn-outline.js +19 -0
- package/dist/worker/console/console-update-runtime.js +2 -2
- package/dist/worker/console/dag-confirmation.js +3 -0
- package/dist/worker/console/dag-execution-receipt.js +3 -0
- package/dist/worker/console/operator-actions.js +32 -3
- package/dist/worker/console/operator-user-error.js +28 -1
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/prd-intake-bridge.js +5 -12
- package/dist/worker/console/recovery-cta.js +14 -14
- package/dist/worker/console/server.js +5 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
- package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
- package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
- package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
- package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -1
- package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
- package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
- package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
- package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
- package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
- package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
- package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
- package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
- package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
- package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
- package/dist/worker/console/workspace-context.js +2 -0
- package/dist/worker/console/workspace-initialization.js +3 -3
- package/dist/worker/observability/read-model.js +23 -2
- package/dist/worker/observe/node-context-insights.js +479 -0
- package/dist/worker/observe/static/console-theme.css +174 -0
- package/dist/worker/observe/static/console-theme.js +36 -0
- package/dist/worker/observe/static/operator-chrome.css +85 -0
- package/dist/worker/observe/static/operator-chrome.js +51 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
- package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
- package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
- package/dist/workflows/dag/budget-enforcement.js +31 -2
- package/dist/workflows/dag/frontend-closeout.js +3 -1
- package/dist/workflows/dag/frontend-contract-facts.js +11 -8
- package/dist/workflows/dag/frontend-design-policy.js +6 -6
- package/dist/workflows/dag/frontend-durable-tools.js +90 -70
- package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
- package/dist/workflows/dag/frontend-plan-canary.js +66 -16
- package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
- package/dist/workflows/dag/frontend-plan-progress.js +92 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
- package/dist/workflows/dag/frontend-recovery-run.js +51 -46
- package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
- package/dist/workflows/dag/frontend-review-context.js +29 -1
- package/dist/workflows/dag/frontend-review-scopes.js +92 -2
- package/dist/workflows/dag/frontend-risk.js +8 -3
- package/dist/workflows/dag/frontend-root-observation.js +261 -0
- package/dist/workflows/dag/frontend-session-budget.js +52 -39
- package/dist/workflows/dag/frontend-session-context.js +10 -0
- package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
- package/dist/workflows/dag/frontend-verification-trace.js +6 -0
- package/dist/workflows/dag/frontend-writer-admission.js +3 -1
- package/dist/workflows/dag/init-hybrid.js +108 -37
- package/dist/workflows/dag/node-execution.js +11 -11
- package/dist/workflows/dag/output-protocol.js +1 -0
- package/dist/workflows/dag/rerun-feedback.js +35 -4
- package/dist/workflows/dag/rerun-task.js +62 -66
- package/dist/workflows/dag/runner.js +58 -12
- package/dist/workflows/dag/types.js +15 -8
- package/dist/workflows/loop/benchmark.js +11 -8
- package/docs/README.md +1 -1
- package/docs/architecture/runtime-boundaries.md +1 -1
- package/docs/governance/harness-methodology-verification.md +11 -0
- package/docs/templates/backend-test-dag.json +5 -4
- package/docs/templates/frontend-implementation-contract.schema.json +31 -11
- package/package.json +6 -4
- package/skills/frontend-design-review/SKILL.md +8 -11
- package/skills/frontend-design-review/references/review-checklist.md +3 -4
- package/skills/frontend-review/SKILL.md +5 -1
- package/skills/loop-agent/references/command-reference.md +8 -0
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
- package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
import { access } from "node:fs/promises";
|
|
17
17
|
import { parseAssistantContent } from "./assistant-content.js";
|
|
18
|
-
import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
|
|
18
|
+
import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, OPERATOR_CHAT_SQL_TOOL_CAPABILITIES, } from "./tools.js";
|
|
19
19
|
import { InvalidChatSessionModeError, SESSION_MODE_ACTION_DENIED_CODE, SESSION_MODE_ACTION_DENIED_MESSAGE, SESSION_MODE_IMMUTABLE_CODE, UnknownChatSessionModeError, isWorkflowSessionMode, parseCreateSessionMode, readStoredSessionMode, shouldAcceptOperatorSnapshot, } from "./session-mode.js";
|
|
20
20
|
import { buildModelCallableToolSchemas } from "./tool-adapter.js";
|
|
21
21
|
import { cleanChatTitle, acceptedOperationIdFromResult, isBlankChatSession } from "./session-store.js";
|
|
22
|
-
import {
|
|
22
|
+
import { projectUserTurnOutline } from "./user-turn-outline.js";
|
|
23
|
+
import { hashChatTurnPayload, projectChatToolEventPayload, projectUnknownChatEventPayload, projectCompactSnapshot, } from "./chat-event-store.js";
|
|
23
24
|
import { projectDeferredTurnSummary, projectDeferredTurnSummaries, } from "./deferred-turn.js";
|
|
24
25
|
import { THINKING_LEVELS, RuntimeSnapshotUnavailableError, } from "./pi-runtime.js";
|
|
25
26
|
import { ResourcePreferencesError, setSkillAutoInvocationPreference, } from "./resource-preferences-store.js";
|
|
26
27
|
import { ComposerDraftStore } from "./composer-draft-store.js";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import { listRepoDirectory, matchRepoHtmlPreviewRoute, readRepoHtmlAsset, readRepoPreview, RepoBrowserError, } from "./repo-browser.js";
|
|
28
|
+
import { scrubSecrets } from "./explore-tools.js";
|
|
29
|
+
import { listChatFilePalette, listRepoDirectory, matchRepoHtmlPreviewRoute, readRepoHtmlAsset, readRepoPreview, RepoBrowserError, } from "./repo-browser.js";
|
|
30
30
|
import { projectRuntimeContext } from "./runtime-context.js";
|
|
31
31
|
import { patchModelsConfig, readModelsConfig, readPackageInventory, } from "./pi-console-config.js";
|
|
32
32
|
import { ChatInterviewAdapter, projectChatInterviewState, } from "./interview-adapter.js";
|
|
@@ -36,6 +36,7 @@ import { dispatchOperatorAction } from "../operator-actions.js";
|
|
|
36
36
|
import { makeHumanGateCard, } from "./human-gate-card.js";
|
|
37
37
|
import { ContractApplyReceiptStore } from "./contract-apply-receipt-store.js";
|
|
38
38
|
import { projectTaskContext } from "./context-panel.js";
|
|
39
|
+
import { deriveContextInsights, estimateTokensFromText, } from "./context-insights.js";
|
|
39
40
|
import { deriveChatSessionTelemetry, deriveChatTurnTelemetry } from "./session-stats.js";
|
|
40
41
|
import { contractApplyPayloadHash, issueHumanGateToken, verifyHumanGateToken, } from "../human-gate-token.js";
|
|
41
42
|
import { sendJson } from "../routes.js";
|
|
@@ -49,6 +50,7 @@ import { TerminalSessionRegistry } from "./terminal-sessions.js";
|
|
|
49
50
|
import { handleTerminalRequest } from "./terminal-routes.js";
|
|
50
51
|
import { handleScmRequest } from "./scm-routes.js";
|
|
51
52
|
import { handleBrowserRequest } from "./browser-routes.js";
|
|
53
|
+
import { DbConnectionError } from "./db-connections.js";
|
|
52
54
|
function projectBrowserChatSession(record) {
|
|
53
55
|
const { telemetrySessionId, ...projected } = record;
|
|
54
56
|
void telemetrySessionId;
|
|
@@ -64,6 +66,14 @@ function reopenInitFromRecord(record) {
|
|
|
64
66
|
sessionMode: record.sessionMode,
|
|
65
67
|
};
|
|
66
68
|
}
|
|
69
|
+
function workspaceProjectionFromRecord(record) {
|
|
70
|
+
return {
|
|
71
|
+
sessionMode: record.sessionMode,
|
|
72
|
+
...(record.modelProvider ? { modelProvider: record.modelProvider } : {}),
|
|
73
|
+
...(record.modelId ? { modelId: record.modelId } : {}),
|
|
74
|
+
...(record.thinkingLevel ? { thinkingLevel: record.thinkingLevel } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
67
77
|
async function readChatSessionMode(deps, sessionId) {
|
|
68
78
|
let record;
|
|
69
79
|
try {
|
|
@@ -196,6 +206,16 @@ export function projectStoredMessagesWithEventOrder(messages, events) {
|
|
|
196
206
|
...(finish
|
|
197
207
|
? { finishedSeq: finish.seq, finishedAt: finish.at }
|
|
198
208
|
: {}),
|
|
209
|
+
...(start && typeof start.data.step === "number"
|
|
210
|
+
? { step: start.data.step }
|
|
211
|
+
: {}),
|
|
212
|
+
...(start &&
|
|
213
|
+
typeof start.data.parentToolCallId ===
|
|
214
|
+
"string"
|
|
215
|
+
? {
|
|
216
|
+
parentToolCallId: start.data.parentToolCallId,
|
|
217
|
+
}
|
|
218
|
+
: {}),
|
|
199
219
|
};
|
|
200
220
|
});
|
|
201
221
|
}
|
|
@@ -212,6 +232,43 @@ function turnSummary(turn) {
|
|
|
212
232
|
abortRequestedAt: turn.abortRequestedAt ?? null,
|
|
213
233
|
};
|
|
214
234
|
}
|
|
235
|
+
function turnStateSummary(turn) {
|
|
236
|
+
return {
|
|
237
|
+
turnId: turn.turnId,
|
|
238
|
+
state: turn.state === "queued" ? "running" : turn.state,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function turnStatesForIds(events, sessionId, turnIds) {
|
|
242
|
+
const projected = new Map();
|
|
243
|
+
for (const turnId of turnIds) {
|
|
244
|
+
if (!turnId || projected.has(turnId))
|
|
245
|
+
continue;
|
|
246
|
+
const turn = events.getTurn(sessionId, turnId);
|
|
247
|
+
if (turn)
|
|
248
|
+
projected.set(turnId, turnStateSummary(turn));
|
|
249
|
+
}
|
|
250
|
+
return [...projected.values()];
|
|
251
|
+
}
|
|
252
|
+
const MAX_STATE_QUERY_TURN_IDS = 8;
|
|
253
|
+
const MAX_STATE_QUERY_TURN_ID_CHARS = 240;
|
|
254
|
+
function requestedStateTurnIds(req) {
|
|
255
|
+
const result = [];
|
|
256
|
+
const seen = new Set();
|
|
257
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
258
|
+
for (const raw of url.searchParams.getAll("turnId")) {
|
|
259
|
+
const turnId = raw.trim();
|
|
260
|
+
if (!turnId ||
|
|
261
|
+
turnId.length > MAX_STATE_QUERY_TURN_ID_CHARS ||
|
|
262
|
+
seen.has(turnId)) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
seen.add(turnId);
|
|
266
|
+
result.push(turnId);
|
|
267
|
+
if (result.length >= MAX_STATE_QUERY_TURN_IDS)
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
215
272
|
function isTerminalTurnState(state) {
|
|
216
273
|
return state === "settled" || state === "aborted" || state === "failed";
|
|
217
274
|
}
|
|
@@ -242,10 +299,99 @@ function writeChatSse(res, event) {
|
|
|
242
299
|
/** Map a runtime ChatTurnEvent to the persisted event-store partial.
|
|
243
300
|
*
|
|
244
301
|
* Returns undefined for heartbeat (not persisted — keep-alive only). */
|
|
245
|
-
function turnEventToStorePartial(event) {
|
|
302
|
+
export function turnEventToStorePartial(event) {
|
|
246
303
|
switch (event.type) {
|
|
247
304
|
case "agent_start":
|
|
248
305
|
return { kind: "agent_start", data: {} };
|
|
306
|
+
case "step_start":
|
|
307
|
+
case "step_end":
|
|
308
|
+
return {
|
|
309
|
+
kind: event.type,
|
|
310
|
+
data: {
|
|
311
|
+
step: Number(event.step ?? 1),
|
|
312
|
+
...(event.type === "step_end"
|
|
313
|
+
? { outcome: String(event.outcome ?? "completed") }
|
|
314
|
+
: {}),
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
case "model_retry":
|
|
318
|
+
{
|
|
319
|
+
const retryState = event.state === "completed" || event.state === "failed"
|
|
320
|
+
? event.state
|
|
321
|
+
: "scheduled";
|
|
322
|
+
return {
|
|
323
|
+
kind: "model_retry",
|
|
324
|
+
data: {
|
|
325
|
+
step: Number(event.step ?? 1),
|
|
326
|
+
attempt: Number(event.attempt ?? 1),
|
|
327
|
+
...(typeof event.maxAttempts === "number"
|
|
328
|
+
? { maxAttempts: event.maxAttempts }
|
|
329
|
+
: {}),
|
|
330
|
+
...(typeof event.delayMs === "number" ? { delayMs: event.delayMs } : {}),
|
|
331
|
+
...(typeof event.errorMessage === "string"
|
|
332
|
+
? { errorMessage: event.errorMessage }
|
|
333
|
+
: {}),
|
|
334
|
+
state: retryState,
|
|
335
|
+
...(typeof event.success === "boolean" ? { success: event.success } : {}),
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
case "turn_max_tokens":
|
|
340
|
+
return {
|
|
341
|
+
kind: "turn_max_tokens",
|
|
342
|
+
data: { step: Number(event.step ?? 1) },
|
|
343
|
+
};
|
|
344
|
+
case "turn_error":
|
|
345
|
+
return {
|
|
346
|
+
kind: "turn_error",
|
|
347
|
+
data: {
|
|
348
|
+
...(typeof event.step === "number" ? { step: event.step } : {}),
|
|
349
|
+
...(typeof event.code === "string" ? { code: event.code } : {}),
|
|
350
|
+
message: String(event.message ?? "模型请求失败"),
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
case "request_context":
|
|
354
|
+
return {
|
|
355
|
+
kind: "request_context",
|
|
356
|
+
data: {
|
|
357
|
+
requestId: String(event.requestId ?? ""),
|
|
358
|
+
step: Number(event.step ?? 1),
|
|
359
|
+
...(event.model && typeof event.model === "object"
|
|
360
|
+
? { model: event.model }
|
|
361
|
+
: {}),
|
|
362
|
+
...(typeof event.thinkingLevel === "string"
|
|
363
|
+
? { thinkingLevel: event.thinkingLevel }
|
|
364
|
+
: {}),
|
|
365
|
+
systemPromptHash: String(event.systemPromptHash ?? ""),
|
|
366
|
+
systemPromptChars: Number(event.systemPromptChars ?? 0),
|
|
367
|
+
activeTools: Array.isArray(event.activeTools) ? event.activeTools : [],
|
|
368
|
+
inputChars: Number(event.inputChars ?? 0),
|
|
369
|
+
imageCount: Number(event.imageCount ?? 0),
|
|
370
|
+
sources: Array.isArray(event.sources) ? event.sources : [],
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
case "command":
|
|
374
|
+
return {
|
|
375
|
+
kind: "command",
|
|
376
|
+
data: {
|
|
377
|
+
commandId: String(event.commandId ?? ""),
|
|
378
|
+
name: String(event.name ?? "unknown"),
|
|
379
|
+
...(typeof event.argsPreview === "string"
|
|
380
|
+
? { argsPreview: event.argsPreview }
|
|
381
|
+
: {}),
|
|
382
|
+
...(typeof event.action === "string" ? { action: event.action } : {}),
|
|
383
|
+
outcome: event.outcome === "failed" ? "failed" : "completed",
|
|
384
|
+
...(typeof event.message === "string" ? { message: event.message } : {}),
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
case "unknown":
|
|
388
|
+
return {
|
|
389
|
+
kind: "unknown",
|
|
390
|
+
data: projectUnknownChatEventPayload({
|
|
391
|
+
eventType: String(event.eventType ?? "unknown"),
|
|
392
|
+
value: event.value,
|
|
393
|
+
}),
|
|
394
|
+
};
|
|
249
395
|
case "message_update":
|
|
250
396
|
case "message_end": {
|
|
251
397
|
const content = parseAssistantContent(event.content);
|
|
@@ -290,6 +436,10 @@ function turnEventToStorePartial(event) {
|
|
|
290
436
|
event.messageId
|
|
291
437
|
? { messageId: event.messageId }
|
|
292
438
|
: {}),
|
|
439
|
+
...(typeof event.step === "number" ? { step: event.step } : {}),
|
|
440
|
+
...(typeof event.stopReason === "string"
|
|
441
|
+
? { stopReason: event.stopReason }
|
|
442
|
+
: {}),
|
|
293
443
|
},
|
|
294
444
|
};
|
|
295
445
|
}
|
|
@@ -303,6 +453,24 @@ function turnEventToStorePartial(event) {
|
|
|
303
453
|
toolCallId: String(event.toolCallId ?? ""),
|
|
304
454
|
toolName: String(event.toolName ?? ""),
|
|
305
455
|
value: event.args,
|
|
456
|
+
...(typeof event.step === "number" ? { step: event.step } : {}),
|
|
457
|
+
...(typeof event.parentToolCallId === "string"
|
|
458
|
+
? { parentToolCallId: event.parentToolCallId }
|
|
459
|
+
: {}),
|
|
460
|
+
}),
|
|
461
|
+
};
|
|
462
|
+
case "tool_update":
|
|
463
|
+
return {
|
|
464
|
+
kind: "tool_update",
|
|
465
|
+
data: projectChatToolEventPayload({
|
|
466
|
+
kind: "tool_update",
|
|
467
|
+
toolCallId: String(event.toolCallId ?? ""),
|
|
468
|
+
toolName: String(event.toolName ?? ""),
|
|
469
|
+
value: event.partialResult,
|
|
470
|
+
...(typeof event.step === "number" ? { step: event.step } : {}),
|
|
471
|
+
...(typeof event.parentToolCallId === "string"
|
|
472
|
+
? { parentToolCallId: event.parentToolCallId }
|
|
473
|
+
: {}),
|
|
306
474
|
}),
|
|
307
475
|
};
|
|
308
476
|
case "tool_result": {
|
|
@@ -317,6 +485,10 @@ function turnEventToStorePartial(event) {
|
|
|
317
485
|
toolName: String(event.toolName ?? ""),
|
|
318
486
|
value: event.result,
|
|
319
487
|
isError: Boolean(event.isError),
|
|
488
|
+
...(typeof event.step === "number" ? { step: event.step } : {}),
|
|
489
|
+
...(typeof event.parentToolCallId === "string"
|
|
490
|
+
? { parentToolCallId: event.parentToolCallId }
|
|
491
|
+
: {}),
|
|
320
492
|
...(resultOperationId ? { operationId: resultOperationId } : {}),
|
|
321
493
|
}),
|
|
322
494
|
};
|
|
@@ -606,6 +778,7 @@ export async function handleChatCapabilities(_req, res, deps) {
|
|
|
606
778
|
deniedTools: OPERATOR_CHAT_DENIED_TOOLS,
|
|
607
779
|
deniedOperatorActions: OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS,
|
|
608
780
|
tools: toolSchemas,
|
|
781
|
+
builtinCustomTools: OPERATOR_CHAT_SQL_TOOL_CAPABILITIES,
|
|
609
782
|
builtinSubagents: [
|
|
610
783
|
{
|
|
611
784
|
toolId: "Agent",
|
|
@@ -803,25 +976,47 @@ export async function handleForkChatSession(req, res, deps, sourceSessionId) {
|
|
|
803
976
|
});
|
|
804
977
|
return;
|
|
805
978
|
}
|
|
979
|
+
let forkedHandle;
|
|
980
|
+
let forkPersisted = false;
|
|
806
981
|
try {
|
|
982
|
+
const body = await readJsonBody(req);
|
|
983
|
+
const throughMessageId = typeof body.throughMessageId === "string" && body.throughMessageId.trim()
|
|
984
|
+
? body.throughMessageId.trim()
|
|
985
|
+
: undefined;
|
|
986
|
+
const throughUserMessageIndex = throughMessageId
|
|
987
|
+
? source.messages
|
|
988
|
+
.filter((message) => message.role === "user")
|
|
989
|
+
.findIndex((message) => message.id === throughMessageId)
|
|
990
|
+
: undefined;
|
|
991
|
+
if (throughMessageId && throughUserMessageIndex === -1) {
|
|
992
|
+
throw new Error("FORK_MESSAGE_NOT_FOUND");
|
|
993
|
+
}
|
|
807
994
|
const handle = await deps.runtime.forkSession({
|
|
808
995
|
sourceSessionFile: source.sessionFile ?? "",
|
|
809
996
|
sessionMode: source.sessionMode,
|
|
810
997
|
...(source.modelProvider && source.modelId
|
|
811
998
|
? { model: { provider: source.modelProvider, modelId: source.modelId } }
|
|
812
999
|
: {}),
|
|
1000
|
+
...(throughUserMessageIndex !== undefined
|
|
1001
|
+
? { throughUserMessageIndex }
|
|
1002
|
+
: {}),
|
|
813
1003
|
});
|
|
1004
|
+
forkedHandle = handle;
|
|
814
1005
|
await deps.store.forkFrom({
|
|
815
1006
|
sourceSessionId,
|
|
816
1007
|
targetSessionId: handle.sessionId,
|
|
817
1008
|
targetSessionFile: handle.sessionFile,
|
|
1009
|
+
...(throughMessageId ? { throughMessageId } : {}),
|
|
818
1010
|
});
|
|
1011
|
+
forkPersisted = true;
|
|
819
1012
|
await deps.attachSession?.(handle.sessionId).catch(() => undefined);
|
|
820
|
-
const copied =
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1013
|
+
const copied = throughMessageId
|
|
1014
|
+
? { copiedCount: 0 }
|
|
1015
|
+
: deps.events.forkContextRefs({
|
|
1016
|
+
sourceSessionId,
|
|
1017
|
+
targetSessionId: handle.sessionId,
|
|
1018
|
+
targetTurnId: `${handle.sessionId}:fork`,
|
|
1019
|
+
});
|
|
825
1020
|
await deps.operationLinker.recoverSession(handle.sessionId);
|
|
826
1021
|
sendJson(res, 201, {
|
|
827
1022
|
ok: true,
|
|
@@ -835,6 +1030,12 @@ export async function handleForkChatSession(req, res, deps, sourceSessionId) {
|
|
|
835
1030
|
});
|
|
836
1031
|
}
|
|
837
1032
|
catch (error) {
|
|
1033
|
+
if (forkedHandle && !forkPersisted) {
|
|
1034
|
+
deps.runtime.dispose(forkedHandle.sessionId);
|
|
1035
|
+
await deps.store
|
|
1036
|
+
.discardUnownedSession(forkedHandle.sessionId, forkedHandle.sessionFile)
|
|
1037
|
+
.catch(() => undefined);
|
|
1038
|
+
}
|
|
838
1039
|
sendJson(res, 409, {
|
|
839
1040
|
ok: false,
|
|
840
1041
|
error: {
|
|
@@ -981,12 +1182,21 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
|
|
|
981
1182
|
const lastEvent = snapshot.at(-1);
|
|
982
1183
|
const activeTurn = deps.events.getActiveTurn(sessionId);
|
|
983
1184
|
const pendingQuestion = await deps.questionRegistry?.get(sessionId);
|
|
1185
|
+
const sddSessionIdentity = deps.sddDataAlignment && (await deps.sddDataAlignment.isEligible())
|
|
1186
|
+
? { telemetrySessionId: record.telemetrySessionId ?? null }
|
|
1187
|
+
: undefined;
|
|
1188
|
+
const projectedMessages = projectStoredMessagesWithEventOrder(messagePage.messages, snapshot);
|
|
1189
|
+
const turnStates = turnStatesForIds(deps.events, sessionId, [
|
|
1190
|
+
...projectedMessages.map((message) => message.turnId),
|
|
1191
|
+
...snapshot.map((event) => event.turnId),
|
|
1192
|
+
]);
|
|
984
1193
|
sendJson(res, 200, {
|
|
985
1194
|
ok: true,
|
|
986
1195
|
eventEpoch: deps.events.epoch(sessionId),
|
|
987
1196
|
session: {
|
|
988
1197
|
...projectBrowserChatSession(record),
|
|
989
|
-
messages:
|
|
1198
|
+
messages: projectedMessages,
|
|
1199
|
+
turnStates,
|
|
990
1200
|
composerDraft,
|
|
991
1201
|
activeTurnId: activeTurn?.turnId,
|
|
992
1202
|
turnPhase: activeTurn
|
|
@@ -1006,6 +1216,10 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
|
|
|
1006
1216
|
sessionStats: telemetry.stats,
|
|
1007
1217
|
usage: telemetry.usage,
|
|
1008
1218
|
turnTelemetry: deriveChatTurnTelemetry(snapshot),
|
|
1219
|
+
userTurnOutline: projectUserTurnOutline(record.messages ?? []),
|
|
1220
|
+
...(sddSessionIdentity
|
|
1221
|
+
? { sddDataAlignment: sddSessionIdentity }
|
|
1222
|
+
: {}),
|
|
1009
1223
|
contextPressure: typeof deps.runtime.getContextPressureTelemetry === "function"
|
|
1010
1224
|
? deps.runtime.getContextPressureTelemetry(sessionId)
|
|
1011
1225
|
: undefined,
|
|
@@ -1021,6 +1235,16 @@ export async function handleGetChatSession(_req, res, deps, sessionId) {
|
|
|
1021
1235
|
turnId: event.turnId,
|
|
1022
1236
|
data: event.data,
|
|
1023
1237
|
})),
|
|
1238
|
+
conversationEvents: snapshot.filter((event) => [
|
|
1239
|
+
"step_start",
|
|
1240
|
+
"step_end",
|
|
1241
|
+
"model_retry",
|
|
1242
|
+
"turn_error",
|
|
1243
|
+
"turn_max_tokens",
|
|
1244
|
+
"request_context",
|
|
1245
|
+
"command",
|
|
1246
|
+
"unknown",
|
|
1247
|
+
].includes(event.kind)),
|
|
1024
1248
|
lastEventId: lastEvent?.eventId ?? null,
|
|
1025
1249
|
lastEventSeq: lastEvent?.seq ?? 0,
|
|
1026
1250
|
});
|
|
@@ -1271,7 +1495,14 @@ function emitUserMessageEvent(deps, sessionId, turnId, userMessage, clientReques
|
|
|
1271
1495
|
data: {
|
|
1272
1496
|
...(clientRequestId ? { clientRequestId } : {}),
|
|
1273
1497
|
...(steering ? { steering: true } : {}),
|
|
1274
|
-
message: {
|
|
1498
|
+
message: {
|
|
1499
|
+
id: userMessage.id,
|
|
1500
|
+
text: userMessage.text,
|
|
1501
|
+
at: userMessage.at,
|
|
1502
|
+
...(userMessage.attachments?.length
|
|
1503
|
+
? { attachments: userMessage.attachments }
|
|
1504
|
+
: {}),
|
|
1505
|
+
},
|
|
1275
1506
|
},
|
|
1276
1507
|
});
|
|
1277
1508
|
}
|
|
@@ -1279,7 +1510,7 @@ function emitUserMessageEvent(deps, sessionId, turnId, userMessage, clientReques
|
|
|
1279
1510
|
* get_state equivalent): exposes the active turn + the latest turn's durable
|
|
1280
1511
|
* outcome WITHOUT the full message payload, so the UI can poll it on an
|
|
1281
1512
|
* interval / visibility / online without paying for session detail. */
|
|
1282
|
-
export async function handleGetChatSessionState(
|
|
1513
|
+
export async function handleGetChatSessionState(req, res, deps, sessionId) {
|
|
1283
1514
|
const record = await deps.store.get(sessionId);
|
|
1284
1515
|
if (!record) {
|
|
1285
1516
|
sendJson(res, 404, {
|
|
@@ -1310,9 +1541,15 @@ export async function handleGetChatSessionState(_req, res, deps, sessionId) {
|
|
|
1310
1541
|
: runtimeBusy
|
|
1311
1542
|
? "running"
|
|
1312
1543
|
: "idle";
|
|
1544
|
+
const turnStates = turnStatesForIds(deps.events, sessionId, [
|
|
1545
|
+
active?.turnId,
|
|
1546
|
+
latest?.turnId,
|
|
1547
|
+
...requestedStateTurnIds(req),
|
|
1548
|
+
]);
|
|
1313
1549
|
sendJson(res, 200, {
|
|
1314
1550
|
ok: true,
|
|
1315
1551
|
state: {
|
|
1552
|
+
turnStates,
|
|
1316
1553
|
// Legacy flat fields retained for older clients.
|
|
1317
1554
|
activeTurnId: active?.turnId ?? null,
|
|
1318
1555
|
activeTurnState: active?.state ?? null,
|
|
@@ -2669,6 +2906,7 @@ export async function handleChatEventsStream(req, res, deps, sessionId) {
|
|
|
2669
2906
|
kind: "stream_ready",
|
|
2670
2907
|
sessionId,
|
|
2671
2908
|
epoch: deps.events.epoch(sessionId),
|
|
2909
|
+
lastSeq: deps.events.latestSeq(sessionId),
|
|
2672
2910
|
latestEventId: retainedTail?.eventId ?? null,
|
|
2673
2911
|
});
|
|
2674
2912
|
let closed = false;
|
|
@@ -2890,28 +3128,8 @@ async function handlePiConfig(req, res, deps, resource) {
|
|
|
2890
3128
|
async function handleChatFiles(res, deps, sessionId, query) {
|
|
2891
3129
|
if (!(await requireChatSession(res, deps, sessionId)))
|
|
2892
3130
|
return;
|
|
2893
|
-
const
|
|
2894
|
-
|
|
2895
|
-
skipSensitive: true,
|
|
2896
|
-
maxFiles: 5000,
|
|
2897
|
-
includeDirs: true,
|
|
2898
|
-
});
|
|
2899
|
-
const entries = walked
|
|
2900
|
-
.filter((entry) => !isSensitivePath(entry.repoRelative) &&
|
|
2901
|
-
(!q || entry.repoRelative.toLowerCase().includes(q)))
|
|
2902
|
-
.sort((a, b) => {
|
|
2903
|
-
const aDir = a.repoRelative.endsWith("/");
|
|
2904
|
-
const bDir = b.repoRelative.endsWith("/");
|
|
2905
|
-
if (aDir !== bDir)
|
|
2906
|
-
return aDir ? -1 : 1;
|
|
2907
|
-
return a.repoRelative.localeCompare(b.repoRelative);
|
|
2908
|
-
})
|
|
2909
|
-
.slice(0, 50)
|
|
2910
|
-
.map((entry) => ({
|
|
2911
|
-
path: entry.repoRelative,
|
|
2912
|
-
isDir: entry.repoRelative.endsWith("/"),
|
|
2913
|
-
}));
|
|
2914
|
-
sendJson(res, 200, { ok: true, files: entries });
|
|
3131
|
+
const files = await listChatFilePalette(deps.appData.repoRoot, query);
|
|
3132
|
+
sendJson(res, 200, { ok: true, files });
|
|
2915
3133
|
}
|
|
2916
3134
|
export async function handleChatCommands(res, deps, sessionId) {
|
|
2917
3135
|
try {
|
|
@@ -2973,7 +3191,32 @@ async function handleSddBingo(req, res, deps, sessionId) {
|
|
|
2973
3191
|
throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "请求体只允许 skillName 字段", 400);
|
|
2974
3192
|
}
|
|
2975
3193
|
const result = await deps.sddDataAlignment.report(sessionId, body.skillName);
|
|
2976
|
-
|
|
3194
|
+
const turnId = deps.events.latestTurnId(sessionId) ?? `${sessionId}:operator-action`;
|
|
3195
|
+
deps.events.append(sessionId, turnId, {
|
|
3196
|
+
kind: "operator-action",
|
|
3197
|
+
data: {
|
|
3198
|
+
schemaVersion: 1,
|
|
3199
|
+
action: "sdd-bingo",
|
|
3200
|
+
skillName: result.skillName,
|
|
3201
|
+
telemetrySessionId: result.telemetrySessionId,
|
|
3202
|
+
},
|
|
3203
|
+
});
|
|
3204
|
+
deps.events.append(sessionId, `${sessionId}:command:${result.telemetrySessionId}`, {
|
|
3205
|
+
kind: "command",
|
|
3206
|
+
data: {
|
|
3207
|
+
commandId: `sdd-bingo:${result.telemetrySessionId}`,
|
|
3208
|
+
name: "sdd-bingo",
|
|
3209
|
+
...(result.skillName ? { argsPreview: result.skillName } : {}),
|
|
3210
|
+
action: "sdd-bingo",
|
|
3211
|
+
outcome: "completed",
|
|
3212
|
+
message: `Session: ${result.telemetrySessionId}`,
|
|
3213
|
+
},
|
|
3214
|
+
});
|
|
3215
|
+
sendJson(res, 200, {
|
|
3216
|
+
ok: true,
|
|
3217
|
+
skillName: result.skillName,
|
|
3218
|
+
telemetrySessionId: result.telemetrySessionId,
|
|
3219
|
+
});
|
|
2977
3220
|
}
|
|
2978
3221
|
catch (error) {
|
|
2979
3222
|
if (error instanceof SddDataAlignmentError) {
|
|
@@ -2990,20 +3233,25 @@ async function handleSddBingo(req, res, deps, sessionId) {
|
|
|
2990
3233
|
}
|
|
2991
3234
|
}
|
|
2992
3235
|
export async function handleRuntimeContext(res, deps, sessionId) {
|
|
2993
|
-
const
|
|
2994
|
-
if (!
|
|
2995
|
-
sendJson(res,
|
|
3236
|
+
const record = await deps.store.get(sessionId);
|
|
3237
|
+
if (!record) {
|
|
3238
|
+
sendJson(res, 404, {
|
|
3239
|
+
ok: false,
|
|
3240
|
+
error: {
|
|
3241
|
+
code: "PI_SESSION_NOT_FOUND",
|
|
3242
|
+
message: `chat session not found: ${sessionId}`,
|
|
3243
|
+
},
|
|
3244
|
+
});
|
|
2996
3245
|
return;
|
|
2997
3246
|
}
|
|
2998
|
-
//
|
|
2999
|
-
//
|
|
3000
|
-
//
|
|
3001
|
-
// length-limited projection. RF-02: a critical projection failure is 503
|
|
3002
|
-
// PI_RUNTIME_UNAVAILABLE — never a 500 leak or a fake-empty context.
|
|
3247
|
+
// Live Agent is preferred. Cold inspect uses workspace ResourceLoader /
|
|
3248
|
+
// ModelRuntime without SessionManager.open(jsonl); prompt still reopens.
|
|
3249
|
+
// RF-02: a critical projection failure is 503 PI_RUNTIME_UNAVAILABLE.
|
|
3003
3250
|
let snapshot;
|
|
3004
3251
|
try {
|
|
3005
3252
|
snapshot = await deps.runtime.getRuntimeSnapshot(sessionId, {
|
|
3006
3253
|
includeRaw: true,
|
|
3254
|
+
workspaceProjection: workspaceProjectionFromRecord(record),
|
|
3007
3255
|
});
|
|
3008
3256
|
}
|
|
3009
3257
|
catch (error) {
|
|
@@ -3053,19 +3301,94 @@ export async function handleRuntimeContext(res, deps, sessionId) {
|
|
|
3053
3301
|
}),
|
|
3054
3302
|
});
|
|
3055
3303
|
}
|
|
3304
|
+
/**
|
|
3305
|
+
* GET /sessions/:id/context-insights: dsh-context parity dashboard projection.
|
|
3306
|
+
* Folds the persisted event ring (works for idle/restored sessions) and, when
|
|
3307
|
+
* the Pi session is live, anchors the estimate with the runtime snapshot's
|
|
3308
|
+
* system-prompt size + active tool count. Read-only; never fabricates payload
|
|
3309
|
+
* text beyond the bounded previews already stored on the ring.
|
|
3310
|
+
*/
|
|
3311
|
+
export async function handleContextInsights(res, deps, sessionId) {
|
|
3312
|
+
const record = await deps.store.get(sessionId);
|
|
3313
|
+
if (!record) {
|
|
3314
|
+
sendJson(res, 404, {
|
|
3315
|
+
ok: false,
|
|
3316
|
+
error: { code: "NOT_FOUND", message: `chat session not found: ${sessionId}` },
|
|
3317
|
+
});
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
await deps.operationLinker.recoverSession(sessionId);
|
|
3321
|
+
const snapshot = deps.events.snapshot(sessionId);
|
|
3322
|
+
let systemPromptChars;
|
|
3323
|
+
let systemPromptPreview;
|
|
3324
|
+
let activeTools;
|
|
3325
|
+
let skillInjections;
|
|
3326
|
+
try {
|
|
3327
|
+
const runtimeSnapshot = await deps.runtime.getRuntimeSnapshot(sessionId, {
|
|
3328
|
+
includeRaw: true,
|
|
3329
|
+
workspaceProjection: workspaceProjectionFromRecord(record),
|
|
3330
|
+
});
|
|
3331
|
+
if (runtimeSnapshot) {
|
|
3332
|
+
systemPromptChars = runtimeSnapshot.systemPrompt.characters;
|
|
3333
|
+
const raw = runtimeSnapshot.systemPrompt.raw ??
|
|
3334
|
+
runtimeSnapshot.systemPrompt.text;
|
|
3335
|
+
if (typeof raw === "string" && raw) {
|
|
3336
|
+
systemPromptPreview = raw.slice(0, 120_000);
|
|
3337
|
+
}
|
|
3338
|
+
activeTools = runtimeSnapshot.activeTools;
|
|
3339
|
+
skillInjections = runtimeSnapshot.skills
|
|
3340
|
+
.slice(0, 50)
|
|
3341
|
+
.map((skill) => ({
|
|
3342
|
+
name: skill.name,
|
|
3343
|
+
tokens: estimateTokensFromText(skill.description ?? skill.name),
|
|
3344
|
+
}));
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
catch {
|
|
3348
|
+
// Insights are derived from the durable ring; a live-snapshot miss only
|
|
3349
|
+
// drops the anchor figures / browser static categories, never the dashboard.
|
|
3350
|
+
}
|
|
3351
|
+
// Tool schemas: capabilities registry projection for whitelisted operator
|
|
3352
|
+
// actions, plus Pi builtins active on this session.
|
|
3353
|
+
const schemaTools = buildModelCallableToolSchemas().map((tool) => ({
|
|
3354
|
+
id: tool.toolId,
|
|
3355
|
+
description: tool.description,
|
|
3356
|
+
}));
|
|
3357
|
+
const activeSet = new Set((activeTools ?? []).map((t) => t.toLowerCase()));
|
|
3358
|
+
const builtinTools = (activeTools ?? [])
|
|
3359
|
+
.filter((name) => !schemaTools.some((t) => t.id === name))
|
|
3360
|
+
.map((name) => ({ id: name, description: "" }));
|
|
3361
|
+
const insights = deriveContextInsights(snapshot, {
|
|
3362
|
+
sessionId,
|
|
3363
|
+
...(systemPromptChars !== undefined ? { systemPromptChars } : {}),
|
|
3364
|
+
...(systemPromptPreview !== undefined
|
|
3365
|
+
? { systemPromptPreview }
|
|
3366
|
+
: {}),
|
|
3367
|
+
tools: [...schemaTools, ...builtinTools],
|
|
3368
|
+
...(skillInjections ? { skillInjections } : {}),
|
|
3369
|
+
});
|
|
3370
|
+
sendJson(res, 200, { ok: true, insights });
|
|
3371
|
+
}
|
|
3056
3372
|
// Exported for the direct handler-boundary tests (trace binding for
|
|
3057
3373
|
// vt-route-model-errors); router dispatch behavior is unchanged.
|
|
3058
3374
|
export async function handleModels(req, res, deps, sessionId) {
|
|
3059
|
-
// AC-01/AC-06: models (and model/thinking mutation) requests reuse the same
|
|
3060
|
-
// activation path. A missing/reopen-failed runtime is a STRUCTURED error —
|
|
3061
|
-
// never a fabricated empty model list.
|
|
3062
|
-
const ensured = await ensureActiveSession(deps, sessionId);
|
|
3063
|
-
if (!ensured.ok) {
|
|
3064
|
-
sendJson(res, ensured.status, { ok: false, error: { code: ensured.code, message: ensured.message } });
|
|
3065
|
-
return;
|
|
3066
|
-
}
|
|
3067
3375
|
try {
|
|
3068
3376
|
if ((req.method ?? "GET").toUpperCase() === "GET") {
|
|
3377
|
+
// Catalog is workspace-scoped (models.json / ModelRuntime). Do not wait
|
|
3378
|
+
// for Pi conversation reopen and do not start it here — prompt / reload
|
|
3379
|
+
// / model mutation still reopen, and concurrent reopen would stall this
|
|
3380
|
+
// cheap list on the single Node thread.
|
|
3381
|
+
const record = await deps.store.get(sessionId);
|
|
3382
|
+
if (!record) {
|
|
3383
|
+
sendJson(res, 404, {
|
|
3384
|
+
ok: false,
|
|
3385
|
+
error: {
|
|
3386
|
+
code: "PI_SESSION_NOT_FOUND",
|
|
3387
|
+
message: `chat session not found: ${sessionId}`,
|
|
3388
|
+
},
|
|
3389
|
+
});
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3069
3392
|
let models;
|
|
3070
3393
|
try {
|
|
3071
3394
|
models = await deps.runtime.listModels(sessionId);
|
|
@@ -3094,6 +3417,11 @@ export async function handleModels(req, res, deps, sessionId) {
|
|
|
3094
3417
|
});
|
|
3095
3418
|
return;
|
|
3096
3419
|
}
|
|
3420
|
+
const ensured = await ensureActiveSession(deps, sessionId);
|
|
3421
|
+
if (!ensured.ok) {
|
|
3422
|
+
sendJson(res, ensured.status, { ok: false, error: { code: ensured.code, message: ensured.message } });
|
|
3423
|
+
return;
|
|
3424
|
+
}
|
|
3097
3425
|
const gate = gateMutation(req, deps);
|
|
3098
3426
|
if (!gate.ok) {
|
|
3099
3427
|
sendJson(res, gate.status, { ok: false, error: { code: gate.code, message: gate.message } });
|
|
@@ -3149,18 +3477,24 @@ async function requireChatSession(res, deps, sessionId) {
|
|
|
3149
3477
|
});
|
|
3150
3478
|
return false;
|
|
3151
3479
|
}
|
|
3152
|
-
|
|
3153
|
-
* Unified session activation (design §2.1 / AC-01): models, runtime snapshot,
|
|
3154
|
-
* reload and model/thinking mutation requests all flow through this path.
|
|
3155
|
-
*
|
|
3156
|
-
* - runtime already holds the session → ok;
|
|
3157
|
-
* - only a persisted record exists → reopen from record.sessionFile;
|
|
3158
|
-
* - no record / missing session file → 404 PI_SESSION_NOT_FOUND;
|
|
3159
|
-
* - SDK reopen failure → 503 PI_RUNTIME_UNAVAILABLE.
|
|
3160
|
-
*
|
|
3161
|
-
* Never fabricates a fresh session or an empty model list.
|
|
3162
|
-
*/
|
|
3480
|
+
const ensureActiveSessionInFlight = new WeakMap();
|
|
3163
3481
|
export async function ensureActiveSession(deps, sessionId) {
|
|
3482
|
+
let bySession = ensureActiveSessionInFlight.get(deps);
|
|
3483
|
+
if (!bySession) {
|
|
3484
|
+
bySession = new Map();
|
|
3485
|
+
ensureActiveSessionInFlight.set(deps, bySession);
|
|
3486
|
+
}
|
|
3487
|
+
const existing = bySession.get(sessionId);
|
|
3488
|
+
if (existing)
|
|
3489
|
+
return existing;
|
|
3490
|
+
const pending = runEnsureActiveSession(deps, sessionId).finally(() => {
|
|
3491
|
+
if (bySession.get(sessionId) === pending)
|
|
3492
|
+
bySession.delete(sessionId);
|
|
3493
|
+
});
|
|
3494
|
+
bySession.set(sessionId, pending);
|
|
3495
|
+
return pending;
|
|
3496
|
+
}
|
|
3497
|
+
async function runEnsureActiveSession(deps, sessionId) {
|
|
3164
3498
|
// Wait for create-path materialization before treating the session as missing.
|
|
3165
3499
|
if (typeof deps.runtime.ensureSessionReady === "function") {
|
|
3166
3500
|
try {
|
|
@@ -3406,19 +3740,28 @@ export async function handleReloadSession(req, res, deps, sessionId) {
|
|
|
3406
3740
|
});
|
|
3407
3741
|
}
|
|
3408
3742
|
/**
|
|
3409
|
-
* GET /sessions/:id/runtime-snapshot (AC-01):
|
|
3743
|
+
* GET /sessions/:id/runtime-snapshot (AC-01): live Agent when present,
|
|
3744
|
+
* otherwise workspace ResourceLoader projection (no JSONL reopen).
|
|
3410
3745
|
*/
|
|
3411
3746
|
export async function handleRuntimeSnapshot(_res, res, deps, sessionId) {
|
|
3412
|
-
const
|
|
3413
|
-
if (!
|
|
3414
|
-
sendJson(res,
|
|
3747
|
+
const record = await deps.store.get(sessionId);
|
|
3748
|
+
if (!record) {
|
|
3749
|
+
sendJson(res, 404, {
|
|
3750
|
+
ok: false,
|
|
3751
|
+
error: {
|
|
3752
|
+
code: "PI_SESSION_NOT_FOUND",
|
|
3753
|
+
message: `chat session not found: ${sessionId}`,
|
|
3754
|
+
},
|
|
3755
|
+
});
|
|
3415
3756
|
return;
|
|
3416
3757
|
}
|
|
3417
3758
|
// RF-02: a critical snapshot projection failure is 503 PI_RUNTIME_UNAVAILABLE
|
|
3418
3759
|
// — never a 200 fake-empty snapshot.
|
|
3419
3760
|
let snapshot;
|
|
3420
3761
|
try {
|
|
3421
|
-
snapshot = await deps.runtime.getRuntimeSnapshot(sessionId
|
|
3762
|
+
snapshot = await deps.runtime.getRuntimeSnapshot(sessionId, {
|
|
3763
|
+
workspaceProjection: workspaceProjectionFromRecord(record),
|
|
3764
|
+
});
|
|
3422
3765
|
}
|
|
3423
3766
|
catch (error) {
|
|
3424
3767
|
if (sendRuntimeUnavailable(res, error))
|
|
@@ -3901,7 +4244,7 @@ async function handleDagHumanGate(req, res, deps, sessionId, step) {
|
|
|
3901
4244
|
: step === "confirm"
|
|
3902
4245
|
? "confirmDagConfirmation"
|
|
3903
4246
|
: "runDag";
|
|
3904
|
-
const result = await dispatchOperatorAction(ctx, {
|
|
4247
|
+
const result = await dispatchOperatorAction({ ...ctx, chatSessionRef: sessionId }, {
|
|
3905
4248
|
action,
|
|
3906
4249
|
actionParams: body,
|
|
3907
4250
|
clientRequestId: typeof body.clientRequestId === "string"
|
|
@@ -4241,7 +4584,14 @@ export async function handleChatMessages(res, deps, sessionId, url) {
|
|
|
4241
4584
|
limit: Number.isFinite(limit) ? limit : 30,
|
|
4242
4585
|
beforeId: url.searchParams.get("beforeId") ?? undefined,
|
|
4243
4586
|
});
|
|
4244
|
-
|
|
4587
|
+
const snapshot = deps.events.snapshot(sessionId);
|
|
4588
|
+
const messages = projectStoredMessagesWithEventOrder(page.messages, snapshot);
|
|
4589
|
+
sendJson(res, 200, {
|
|
4590
|
+
ok: true,
|
|
4591
|
+
...page,
|
|
4592
|
+
messages,
|
|
4593
|
+
turnStates: turnStatesForIds(deps.events, sessionId, messages.map((message) => message.turnId)),
|
|
4594
|
+
});
|
|
4245
4595
|
}
|
|
4246
4596
|
catch (error) {
|
|
4247
4597
|
sendJson(res, 404, {
|
|
@@ -4253,6 +4603,23 @@ export async function handleChatMessages(res, deps, sessionId, url) {
|
|
|
4253
4603
|
});
|
|
4254
4604
|
}
|
|
4255
4605
|
}
|
|
4606
|
+
export async function handleChatAttachment(res, deps, sessionId, attachmentId) {
|
|
4607
|
+
const attachment = await deps.store.readAttachment(sessionId, attachmentId);
|
|
4608
|
+
if (!attachment) {
|
|
4609
|
+
sendJson(res, 404, {
|
|
4610
|
+
ok: false,
|
|
4611
|
+
error: { code: "NOT_FOUND", message: "chat attachment not found" },
|
|
4612
|
+
});
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
res.writeHead(200, {
|
|
4616
|
+
"content-type": attachment.metadata.mimeType,
|
|
4617
|
+
"content-length": String(attachment.bytes.length),
|
|
4618
|
+
"cache-control": "private, max-age=31536000, immutable",
|
|
4619
|
+
"x-content-type-options": "nosniff",
|
|
4620
|
+
});
|
|
4621
|
+
res.end(attachment.bytes);
|
|
4622
|
+
}
|
|
4256
4623
|
/**
|
|
4257
4624
|
* Dispatch a chat HTTP request. Returns true if handled.
|
|
4258
4625
|
* Mounted under /api/operator/v1/chat/*.
|
|
@@ -4279,6 +4646,24 @@ export async function handleChatRequest(req, res, deps, pathname, workspaceScope
|
|
|
4279
4646
|
throw error;
|
|
4280
4647
|
}
|
|
4281
4648
|
}
|
|
4649
|
+
function databaseConnectionInput(body, engine) {
|
|
4650
|
+
return {
|
|
4651
|
+
name: typeof body.name === "string" ? body.name : "",
|
|
4652
|
+
engine,
|
|
4653
|
+
...(typeof body.path === "string" ? { path: body.path } : {}),
|
|
4654
|
+
...(body.mode === "parameters" || body.mode === "url-env" ? { mode: body.mode } : {}),
|
|
4655
|
+
...(typeof body.urlEnv === "string" ? { urlEnv: body.urlEnv } : {}),
|
|
4656
|
+
...(typeof body.host === "string" ? { host: body.host } : {}),
|
|
4657
|
+
...(typeof body.port === "number" ? { port: body.port } : {}),
|
|
4658
|
+
...(typeof body.database === "string" ? { database: body.database } : {}),
|
|
4659
|
+
...(typeof body.username === "string" ? { username: body.username } : {}),
|
|
4660
|
+
...(body.passwordMode === "runtime" || body.passwordMode === "environment" || body.passwordMode === "encrypted" ? { passwordMode: body.passwordMode } : {}),
|
|
4661
|
+
...(typeof body.passwordRef === "string" ? { passwordRef: body.passwordRef } : {}),
|
|
4662
|
+
...(typeof body.passwordEnv === "string" ? { passwordEnv: body.passwordEnv } : {}),
|
|
4663
|
+
...(typeof body.password === "string" ? { password: body.password } : {}),
|
|
4664
|
+
...(typeof body.ssl === "boolean" ? { ssl: body.ssl } : {}),
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4282
4667
|
async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
|
|
4283
4668
|
// Terminal management API (model + Console UI shared PTY registry).
|
|
4284
4669
|
if (await handleTerminalRequest(req, res, {
|
|
@@ -4358,6 +4743,117 @@ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
|
|
|
4358
4743
|
await handleChatCapabilities(req, res, deps);
|
|
4359
4744
|
return true;
|
|
4360
4745
|
}
|
|
4746
|
+
if (method === "GET" && pathname === "/api/operator/v1/chat/database-connections/export") {
|
|
4747
|
+
sendJson(res, 200, { ok: true, document: deps.runtime.exportDatabaseConnections() });
|
|
4748
|
+
return true;
|
|
4749
|
+
}
|
|
4750
|
+
if (method === "POST" && pathname === "/api/operator/v1/chat/database-connections/import") {
|
|
4751
|
+
const gate = gateMutation(req, deps);
|
|
4752
|
+
if (!gate.ok) {
|
|
4753
|
+
sendJson(res, gate.status, { ok: false, error: { code: gate.code, message: gate.message } });
|
|
4754
|
+
return true;
|
|
4755
|
+
}
|
|
4756
|
+
try {
|
|
4757
|
+
const body = await readJsonBody(req);
|
|
4758
|
+
const conflict = body.conflict === "overwrite" ? "overwrite" : "skip";
|
|
4759
|
+
const result = await deps.runtime.importDatabaseConnections(body.document, conflict);
|
|
4760
|
+
sendJson(res, 200, { ok: true, result });
|
|
4761
|
+
}
|
|
4762
|
+
catch (error) {
|
|
4763
|
+
const code = error instanceof DbConnectionError ? error.code : "DB_PERSISTENCE_ERROR";
|
|
4764
|
+
const status = code === "DB_PERSISTENCE_ERROR" ? 500 : 400;
|
|
4765
|
+
sendJson(res, status, { ok: false, error: { code, message: error instanceof Error ? error.message : String(error) } });
|
|
4766
|
+
}
|
|
4767
|
+
return true;
|
|
4768
|
+
}
|
|
4769
|
+
if (pathname === "/api/operator/v1/chat/database-connections" &&
|
|
4770
|
+
(method === "GET" || method === "POST")) {
|
|
4771
|
+
if (method === "GET") {
|
|
4772
|
+
sendJson(res, 200, { ok: true, connections: deps.runtime.listDatabaseConnections() });
|
|
4773
|
+
return true;
|
|
4774
|
+
}
|
|
4775
|
+
const gate = gateMutation(req, deps);
|
|
4776
|
+
if (!gate.ok) {
|
|
4777
|
+
sendJson(res, gate.status, { ok: false, error: { code: gate.code, message: gate.message } });
|
|
4778
|
+
return true;
|
|
4779
|
+
}
|
|
4780
|
+
try {
|
|
4781
|
+
const body = await readJsonBody(req);
|
|
4782
|
+
const engine = body.engine;
|
|
4783
|
+
if (engine !== "sqlite" && engine !== "postgres" && engine !== "mysql") {
|
|
4784
|
+
throw new DbConnectionError("DB_INVALID_CONFIG", "engine must be sqlite, postgres, or mysql");
|
|
4785
|
+
}
|
|
4786
|
+
const connection = await deps.runtime.addDatabaseConnection(databaseConnectionInput(body, engine));
|
|
4787
|
+
sendJson(res, 201, { ok: true, connection });
|
|
4788
|
+
}
|
|
4789
|
+
catch (error) {
|
|
4790
|
+
const code = error instanceof DbConnectionError ? error.code : "DB_PERSISTENCE_ERROR";
|
|
4791
|
+
const status = code === "DB_PERSISTENCE_ERROR" ? 500 : 400;
|
|
4792
|
+
sendJson(res, status, { ok: false, error: { code, message: error instanceof Error ? error.message : String(error) } });
|
|
4793
|
+
}
|
|
4794
|
+
return true;
|
|
4795
|
+
}
|
|
4796
|
+
const databaseBrowseRoute = pathname.match(/^\/api\/operator\/v1\/chat\/database-connections\/([^/]+)\/browse$/);
|
|
4797
|
+
if (databaseBrowseRoute && method === "GET") {
|
|
4798
|
+
const name = decodeURIComponent(databaseBrowseRoute[1]);
|
|
4799
|
+
try {
|
|
4800
|
+
const table = url.searchParams.get("table")?.trim();
|
|
4801
|
+
const database = url.searchParams.get("database")?.trim() || undefined;
|
|
4802
|
+
if (!table) {
|
|
4803
|
+
const databases = await deps.runtime.listDatabaseDatabases(name);
|
|
4804
|
+
const connection = deps.runtime.listDatabaseConnections().find((item) => item.name === name);
|
|
4805
|
+
const shouldLoadTables = connection?.engine === "sqlite" || Boolean(database || connection?.database);
|
|
4806
|
+
sendJson(res, 200, { ok: true, connection: name, database, databases, tables: shouldLoadTables ? await deps.runtime.listDatabaseTables(name, database) : [] });
|
|
4807
|
+
}
|
|
4808
|
+
else {
|
|
4809
|
+
const page = Number(url.searchParams.get("page") ?? "1");
|
|
4810
|
+
const pageSize = Number(url.searchParams.get("pageSize") ?? "50");
|
|
4811
|
+
sendJson(res, 200, { ok: true, result: await deps.runtime.browseDatabaseTable(name, table, page, pageSize, database) });
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
catch (error) {
|
|
4815
|
+
const code = error instanceof DbConnectionError ? error.code : "DB_ENGINE_ERROR";
|
|
4816
|
+
const status = code === "DB_CONNECTION_NOT_FOUND" ? 404 : 400;
|
|
4817
|
+
sendJson(res, status, { ok: false, error: { code, message: error instanceof Error ? error.message : String(error) } });
|
|
4818
|
+
}
|
|
4819
|
+
return true;
|
|
4820
|
+
}
|
|
4821
|
+
const databaseConnectionRoute = pathname.match(/^\/api\/operator\/v1\/chat\/database-connections\/([^/]+)(?:\/(test))?$/);
|
|
4822
|
+
if (databaseConnectionRoute && (method === "DELETE" || method === "POST" || method === "PATCH")) {
|
|
4823
|
+
const gate = gateMutation(req, deps);
|
|
4824
|
+
if (!gate.ok) {
|
|
4825
|
+
sendJson(res, gate.status, { ok: false, error: { code: gate.code, message: gate.message } });
|
|
4826
|
+
return true;
|
|
4827
|
+
}
|
|
4828
|
+
const name = decodeURIComponent(databaseConnectionRoute[1]);
|
|
4829
|
+
try {
|
|
4830
|
+
if (method === "POST" && databaseConnectionRoute[2] === "test") {
|
|
4831
|
+
sendJson(res, 200, { ok: true, result: await deps.runtime.testDatabaseConnection(name) });
|
|
4832
|
+
}
|
|
4833
|
+
else if (method === "PATCH" && !databaseConnectionRoute[2]) {
|
|
4834
|
+
const body = await readJsonBody(req);
|
|
4835
|
+
const engine = body.engine;
|
|
4836
|
+
if (engine !== "sqlite" && engine !== "postgres" && engine !== "mysql") {
|
|
4837
|
+
throw new DbConnectionError("DB_INVALID_CONFIG", "engine must be sqlite, postgres, or mysql");
|
|
4838
|
+
}
|
|
4839
|
+
const connection = await deps.runtime.updateDatabaseConnection(name, databaseConnectionInput(body, engine));
|
|
4840
|
+
sendJson(res, 200, { ok: true, connection });
|
|
4841
|
+
}
|
|
4842
|
+
else if (method === "DELETE" && !databaseConnectionRoute[2]) {
|
|
4843
|
+
await deps.runtime.removeDatabaseConnection(name);
|
|
4844
|
+
sendJson(res, 200, { ok: true });
|
|
4845
|
+
}
|
|
4846
|
+
else {
|
|
4847
|
+
sendJson(res, 405, { ok: false, error: { code: "METHOD_NOT_ALLOWED", message: "method not allowed" } });
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
catch (error) {
|
|
4851
|
+
const code = error instanceof DbConnectionError ? error.code : "DB_ENGINE_ERROR";
|
|
4852
|
+
const status = code === "DB_CONNECTION_NOT_FOUND" ? 404 : code === "DB_PERSISTENCE_ERROR" ? 500 : 400;
|
|
4853
|
+
sendJson(res, status, { ok: false, error: { code, message: error instanceof Error ? error.message : String(error) } });
|
|
4854
|
+
}
|
|
4855
|
+
return true;
|
|
4856
|
+
}
|
|
4361
4857
|
const htmlPreviewRoute = matchRepoHtmlPreviewRoute(pathname);
|
|
4362
4858
|
if (method === "GET" && htmlPreviewRoute) {
|
|
4363
4859
|
if (!htmlPreviewRoute.rawPath)
|
|
@@ -4443,7 +4939,7 @@ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
|
|
|
4443
4939
|
await handleSddBingo(req, res, deps, decodeURIComponent(sddBingo[1]));
|
|
4444
4940
|
return true;
|
|
4445
4941
|
}
|
|
4446
|
-
const m4Route = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/(draft|files|runtime-context|runtime-snapshot|models|model|reload|commands)$/);
|
|
4942
|
+
const m4Route = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/(draft|files|runtime-context|runtime-snapshot|context-insights|models|model|reload|commands)$/);
|
|
4447
4943
|
if (m4Route) {
|
|
4448
4944
|
const id = decodeURIComponent(m4Route[1]);
|
|
4449
4945
|
const route = m4Route[2];
|
|
@@ -4463,6 +4959,10 @@ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
|
|
|
4463
4959
|
await handleRuntimeSnapshot(req, res, deps, id);
|
|
4464
4960
|
return true;
|
|
4465
4961
|
}
|
|
4962
|
+
if (route === "context-insights" && method === "GET") {
|
|
4963
|
+
await handleContextInsights(res, deps, id);
|
|
4964
|
+
return true;
|
|
4965
|
+
}
|
|
4466
4966
|
if (route === "models" && method === "GET") {
|
|
4467
4967
|
await handleModels(req, res, deps, id);
|
|
4468
4968
|
return true;
|
|
@@ -4557,6 +5057,11 @@ async function dispatchChatRequest(req, res, deps, pathname, workspaceScope) {
|
|
|
4557
5057
|
await handleChatMessages(res, deps, decodeURIComponent(sessionMessages[1]), url);
|
|
4558
5058
|
return true;
|
|
4559
5059
|
}
|
|
5060
|
+
const sessionAttachment = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/attachments\/([^/]+)$/);
|
|
5061
|
+
if (method === "GET" && sessionAttachment) {
|
|
5062
|
+
await handleChatAttachment(res, deps, decodeURIComponent(sessionAttachment[1]), decodeURIComponent(sessionAttachment[2]));
|
|
5063
|
+
return true;
|
|
5064
|
+
}
|
|
4560
5065
|
const sessionPrompt = pathname.match(/^\/api\/operator\/v1\/chat\/sessions\/([^/]+)\/prompt$/);
|
|
4561
5066
|
if (method === "POST" && sessionPrompt) {
|
|
4562
5067
|
await handleChatPrompt(req, res, deps, decodeURIComponent(sessionPrompt[1]));
|