@tea-agent/loop-agent 0.43.0-next.1 → 0.43.0-next.11
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 +144 -9
- package/dist/application/dag/run-dag.js +5 -0
- package/dist/application/evaluation/budget.js +19 -1
- package/dist/application/task-lifecycle/advance.js +17 -4
- package/dist/application/task-lifecycle/observe.js +43 -29
- package/dist/application/task-lifecycle/plan-transitions.js +5 -4
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +1237 -586
- package/dist/executors/pi-executor.js +22 -1
- package/dist/executors/pi-sdk-executor.js +140 -39
- package/dist/executors/shell-executor.js +8 -4
- package/dist/shared/frontend-execution-policy.js +23 -0
- package/dist/task/config-types.js +4 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
- package/dist/task/source-prepare/ledger-review.js +6 -9
- package/dist/task/source-prepare/semantic-intake.js +16 -26
- package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
- package/dist/task/source-references.js +48 -23
- package/dist/worker/console/chat/assistant-content.js +23 -2
- package/dist/worker/console/chat/browser-policy.js +113 -0
- package/dist/worker/console/chat/browser-routes.js +140 -0
- package/dist/worker/console/chat/explore-tools.js +13 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +86 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
- package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
- package/dist/worker/console/chat/pi-runtime.js +207 -1178
- package/dist/worker/console/chat/routes.js +208 -71
- package/dist/worker/console/chat/scm-routes.js +217 -0
- package/dist/worker/console/chat/scm-service.js +283 -0
- package/dist/worker/console/chat/scm-tools.js +111 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +65 -9
- package/dist/worker/console/chat/session-catalog.js +32 -0
- package/dist/worker/console/chat/session-mode-view.js +49 -0
- package/dist/worker/console/chat/session-mode.js +219 -0
- package/dist/worker/console/chat/session-store.js +22 -6
- package/dist/worker/console/chat/shortcuts.js +6 -0
- package/dist/worker/console/chat/terminal-routes.js +216 -0
- package/dist/worker/console/chat/terminal-sessions.js +348 -0
- package/dist/worker/console/chat/terminal-tools.js +211 -0
- package/dist/worker/console/chat/tool-preview.js +11 -0
- package/dist/worker/console/chat/tools.js +2 -0
- package/dist/worker/console/server.js +2 -28
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B0Q0nClf.js → abnfDiagram-N423BO3Z-CTSK0xbU.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DCPjC19G.js → arc-_2MfctFP.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CP5n9jmG.js → architectureDiagram-T3A2C74G-DsDe2wTg.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CmaWi_Wg.js → blockDiagram-VBNYF7ZC-Dtz_779U.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Ct5tcMfZ.js → c4Diagram-5PPSVZJV-LaULi-gw.js} +1 -1
- package/dist/worker/console/static/assets/channel-CqS4yBWT.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C0osm1Zf.js → chunk-2GRJ4B5K-DuHBqqX8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CDWviED7.js → chunk-2Q5K7J3B-BZgZs1dn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-mtSk1-j7.js → chunk-5RXB4S5H-tzwmlIO_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D5J9PHC7.js → chunk-5VM5RSS4-CeYrLkSn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Begg4WAa.js → chunk-6Q2QTUOP-C92cgilm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-sF1AEy7T.js → chunk-GF5L2VYU-XOXpGC0F.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BqbsWXZb.js → chunk-JWPE2WC7-CGHbJyCH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BKJrPcJ6.js → chunk-KBJHAD2P-Bs2-LTzf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CcwNMRho.js → chunk-RYQCIY6F-rN7up7E4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BEJi3iIs.js → chunk-XXDRQBXY-B_fWjKpR.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DVOxNOq0.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DVOxNOq0.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-DRLgTvVu.js → cose-bilkent-JH36ORCC-DE3SdJvB.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BoAVG3cs.js → cynefin-VYW2F7L2-B-4yuRAX.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bzol06hR.js → cynefinDiagram-MW4NZA55-TiCPFuO8.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UmliJM77.js → dagre-VZM6K2ZE-7crzLMzA.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BWLcJkFo.js → diagram-7IWD3JNH-Cvr-x6Fd.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CgApOm-O.js → diagram-B4RE2ZJO-BzcRHUjH.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Bh3RgTLs.js → diagram-LBJQPF4R-BjkAXilh.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Dsh-D5nE.js → diagram-Q27KOJAE-BkzyATK7.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dkbbpcpb.js → diagram-UB23O5K3-rZuTarYP.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-3EBaA3t3.js → ebnfDiagram-BXEA7PRR-CAYENaN1.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bq2Dc5ok.js → erDiagram-JOGREHBK-DCEam0Uq.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-De7y55ha.js → flowDiagram-UKHOOZJN-e_dH4wVO.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-fLiDbQRh.js → ganttDiagram-PKOTCBZU-CvZa2zOK.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Du617mp1.js → gitGraphDiagram-DS77QQ5N-CbT5TuKp.js} +1 -1
- package/dist/worker/console/static/assets/index-CUPeoE8W.js +468 -0
- package/dist/worker/console/static/assets/index-SAANIK8g.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BwmlF-XP.js → infoDiagram-6WML65LV-BWzHQa_Z.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-zsGtS59u.js → ishikawaDiagram-WSZJBQD7-BTjlovtU.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DTTPTe3f.js → journeyDiagram-NVQOT4AX-DkvQoCc1.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BkXg9aP-.js → kanban-definition-27J2QSJJ-CHSzGi3W.js} +1 -1
- package/dist/worker/console/static/assets/{linear-7U2ue5IE.js → linear-DZMkTCWH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BUuGHmWO.js → mermaid.core-BIMhty7A.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-7q6fX0Sv.js → mindmap-definition-FAOFIHXS-Dx-tyMl3.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkQPJN55.js → pegDiagram-VL7TDLO6-C6G34rbY.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CCal_tfX.js → pieDiagram-7S7Q4E2Y-CHK9XOfm.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js → quadrantDiagram-CIZ2JOQS-CDARkI0w.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CJmww7D-.js → railroadDiagram-AXF67PYL-DiuUe0ZQ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D6ldWJEv.js → requirementDiagram-LRYGKXZP-RE61UR56.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-VGh33I9e.js → sankeyDiagram-W5VNT64P-KDNLHMEG.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CCkDrIjU.js → sequenceDiagram-SI44F4Z6-CeVgXnD9.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-NC8f5Otb.js → sizeCapture-X5ZJPWSS-CXwGyjjG.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-_92ezZdF.js → stateDiagram-OKZ733FA-BjTfozab.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BXoYsyr2.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BdUCGtbP.js → swimlanes-SLNWSIFB-BaXOB8GT.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DfKliS4e.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CdD1H8ia.js → timeline-definition-Z64GVDOM-C3r2hqUU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-Dkdk3oFo.js → vennDiagram-T6HMQDX7-MWKgl1S9.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CKu2uPPY.js → wardleyDiagram-T6FBY63Y-C9iFcZPk.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-ClqAnuG8.js → xychartDiagram-ELKLHX3M-DqnM24Jp.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +10 -0
- package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +122 -10
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +7 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +27 -15
- package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +10 -0
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +44 -24
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +18 -2
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +41 -8
- package/dist/worker/console/static-src/operator-chat/useComposer.js +26 -11
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
- package/dist/worker/console/workspace-context.js +15 -1
- package/dist/worker/observe/node-transparency.js +81 -72
- package/dist/worker/observe/routes.js +20 -1
- package/dist/worker/observe/static/dag-inspector-humanize.js +3 -0
- package/dist/worker/observe/static/dom.js +20 -1
- package/dist/worker/observe/static/format-pool.d.ts +2 -0
- package/dist/worker/observe/static/format-pool.js +6 -0
- package/dist/worker/observe/static/format.js +7 -0
- package/dist/worker/observe/static/inspect-workspace.js +34 -7
- package/dist/worker/observe/static/inspector-submission.js +32 -0
- package/dist/worker/observe/static/kpi.js +1 -0
- package/dist/worker/observe/static/relations.js +2 -0
- package/dist/worker/observe/static/router.js +13 -0
- package/dist/worker/observe/static/run-processing.js +2 -0
- package/dist/worker/observe/static/shell-chrome.js +36 -3
- package/dist/worker/observe/static/state.js +35 -2
- package/dist/worker/observe/static/styles.css +263 -39
- package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
- package/dist/worker/observe/static/task-failure-labels.js +67 -0
- package/dist/worker/observe/static/task-history.js +12 -0
- package/dist/worker/observe/static/views/batch.js +6 -13
- package/dist/worker/observe/static/views/dag-graph.js +50 -3
- package/dist/worker/observe/static/views/dag-inspector.js +742 -268
- package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
- package/dist/worker/observe/static/views/dag.d.ts +6 -0
- package/dist/worker/observe/static/views/dag.js +49 -11
- package/dist/worker/observe/static/views/dags.js +2 -0
- package/dist/worker/observe/static/views/dashboard.js +21 -12
- package/dist/worker/observe/static/views/failures.js +21 -11
- package/dist/worker/observe/static/views/feature.js +11 -29
- package/dist/worker/observe/static/views/pool.js +37 -28
- package/dist/worker/observe/static/views/run.js +48 -5
- package/dist/worker/observe/static/views/session-timeline.js +189 -240
- package/dist/worker/observe/static/views/task.js +81 -62
- package/dist/workflows/dag/budget-enforcement.js +53 -3
- package/dist/workflows/dag/frontend-capacity.js +9 -0
- package/dist/workflows/dag/frontend-closeout.js +1 -1
- package/dist/workflows/dag/frontend-design-policy.js +2 -1
- package/dist/workflows/dag/frontend-durable-tools.js +193 -0
- package/dist/workflows/dag/frontend-execution-groups.js +24 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +13 -0
- package/dist/workflows/dag/frontend-input-projection.js +76 -0
- package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
- package/dist/workflows/dag/frontend-plan-render.js +10 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-recovery-controller.js +7 -7
- package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
- package/dist/workflows/dag/frontend-recovery-run.js +4 -0
- package/dist/workflows/dag/frontend-review-context.js +2 -1
- package/dist/workflows/dag/frontend-review-scopes.js +139 -0
- package/dist/workflows/dag/frontend-session-budget.js +249 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +20 -2
- package/dist/workflows/dag/frontend-shape-facts.js +12 -2
- package/dist/workflows/dag/frontend-shape.js +30 -1
- package/dist/workflows/dag/frontend-test-execution-evidence.js +3 -2
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -0
- package/dist/workflows/dag/frontend-verification-trace.js +1 -1
- package/dist/workflows/dag/init-hybrid.js +65 -167
- package/dist/workflows/dag/node-execution.js +38 -155
- package/dist/workflows/dag/prompt.js +4 -0
- package/dist/workflows/dag/rerun-plan.js +7 -1
- package/dist/workflows/dag/runner.js +26 -1
- package/dist/workflows/dag/types.js +6 -0
- package/docs/operations/README.md +1 -0
- package/docs/templates/frontend-design-contract.md +4 -4
- package/docs/templates/frontend-implementation-contract.schema.json +34 -2
- package/docs/templates/frontend-implementation-dag.json +14 -13
- package/package.json +6 -3
- package/skills/frontend-bounded-implement/SKILL.md +3 -3
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/SKILL.md +2 -1
- package/skills/frontend-contract/references/contract-protocol.md +22 -4
- package/skills/frontend-design-review/SKILL.md +22 -16
- package/skills/frontend-design-review/references/review-checklist.md +7 -7
- package/skills/frontend-plan/SKILL.md +21 -2
- package/skills/frontend-plan/references/decision-contract.md +42 -23
- package/skills/frontend-review/SKILL.md +22 -29
- package/skills/frontend-review/references/review-findings.md +24 -43
- package/skills/frontend-scout/references/scout-evidence.md +4 -0
- package/dist/worker/console/static/assets/channel-DAS07MdS.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +0 -1
- package/dist/worker/console/static/assets/index-C0O48S_P.js +0 -449
- package/dist/worker/console/static/assets/index-CzKf4U8P.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +0 -8
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime inspection: browser-safe snapshot projection, slash-command
|
|
3
|
+
* inventory, and ResourceLoader/package/MCP collection. Does not own
|
|
4
|
+
* session lifecycle, preference-file cache, or prompt/reload locks.
|
|
5
|
+
*/
|
|
6
|
+
import { RUNTIME_CONTEXT_TEXT_MAX, redactRuntimeText, } from "../runtime-context.js";
|
|
7
|
+
import { isAutoInvocationEnabled, skillResourceId, } from "../resource-preferences-store.js";
|
|
8
|
+
/**
|
|
9
|
+
* Browser-safe projection of a live runtime snapshot (design §2.2 / RF-03).
|
|
10
|
+
*
|
|
11
|
+
* Every browser-visible string runs the shared secret-scrub chain
|
|
12
|
+
* (scrubSecrets + ghp_/Bearer redaction via redactRuntimeText) and a fixed
|
|
13
|
+
* per-field length limit: 200 (names/spec/source/origin), 400 (paths/errors/
|
|
14
|
+
* diagnostic messages), 600 (skill descriptions), 80 (package versions);
|
|
15
|
+
* truncation uses the fixed `…[truncated N chars …]` marker. The server-only
|
|
16
|
+
* `raw` prompt and the RAW-based `sha256`/`characters` pass through untouched,
|
|
17
|
+
* and real empty lists stay empty.
|
|
18
|
+
*
|
|
19
|
+
* Pure + deterministic and exported so the route harness reuses the exact
|
|
20
|
+
* production implementation (same code path as getRuntimeSnapshot).
|
|
21
|
+
*/
|
|
22
|
+
export function projectRuntimeSnapshot(input) {
|
|
23
|
+
const field = (value, max) => value === undefined ? undefined : redactRuntimeText(value, max);
|
|
24
|
+
const projectSkill = (skill) => ({
|
|
25
|
+
name: field(skill.name, 200) ?? "",
|
|
26
|
+
description: field(skill.description, 600) ?? "",
|
|
27
|
+
path: field(skill.path, 400) ?? "",
|
|
28
|
+
...(skill.baseDir ? { baseDir: field(skill.baseDir, 400) } : {}),
|
|
29
|
+
scope: skill.scope,
|
|
30
|
+
source: field(skill.source, 200) ?? "",
|
|
31
|
+
resourceId: field(skill.resourceId, 200) ?? "",
|
|
32
|
+
autoInvocationEnabled: skill.autoInvocationEnabled,
|
|
33
|
+
diagnostics: skill.diagnostics.map((diag) => field(diag, 400) ?? ""),
|
|
34
|
+
});
|
|
35
|
+
const projectExtension = (ext) => ({
|
|
36
|
+
name: field(ext.name, 200) ?? "",
|
|
37
|
+
path: field(ext.path, 400) ?? "",
|
|
38
|
+
...(ext.resolvedPath
|
|
39
|
+
? { resolvedPath: field(ext.resolvedPath, 400) }
|
|
40
|
+
: {}),
|
|
41
|
+
source: field(ext.source, 200) ?? "",
|
|
42
|
+
scope: ext.scope,
|
|
43
|
+
...(ext.origin ? { origin: field(ext.origin, 200) } : {}),
|
|
44
|
+
enabled: ext.enabled,
|
|
45
|
+
...(ext.error ? { error: field(ext.error, 400) } : {}),
|
|
46
|
+
});
|
|
47
|
+
const projectPackage = (pkg) => ({
|
|
48
|
+
spec: field(pkg.spec, 200) ?? "",
|
|
49
|
+
scope: pkg.scope,
|
|
50
|
+
enabled: pkg.enabled,
|
|
51
|
+
...(pkg.version ? { version: field(pkg.version, 80) } : {}),
|
|
52
|
+
...(pkg.configuredVersion
|
|
53
|
+
? { configuredVersion: field(pkg.configuredVersion, 80) }
|
|
54
|
+
: {}),
|
|
55
|
+
...(pkg.packageName ? { packageName: field(pkg.packageName, 200) } : {}),
|
|
56
|
+
...(pkg.path ? { path: field(pkg.path, 400) } : {}),
|
|
57
|
+
...(pkg.installedPath ? { installedPath: field(pkg.installedPath, 400) } : {}),
|
|
58
|
+
...(pkg.cwd ? { cwd: field(pkg.cwd, 400) } : {}),
|
|
59
|
+
resolved: pkg.resolved.map((resource) => ({
|
|
60
|
+
type: resource.type,
|
|
61
|
+
name: field(resource.name, 200) ?? "",
|
|
62
|
+
path: field(resource.path, 400) ?? "",
|
|
63
|
+
source: field(resource.source, 200) ?? "",
|
|
64
|
+
scope: resource.scope,
|
|
65
|
+
})),
|
|
66
|
+
diagnostics: pkg.diagnostics.map((diag) => field(diag, 400) ?? ""),
|
|
67
|
+
});
|
|
68
|
+
const projectMcpTool = (tool) => ({
|
|
69
|
+
name: field(tool.name, 200) ?? "",
|
|
70
|
+
description: field(tool.description, 600) ?? "",
|
|
71
|
+
...(tool.server ? { server: field(tool.server, 200) } : {}),
|
|
72
|
+
source: field(tool.source, 200) ?? "",
|
|
73
|
+
scope: tool.scope,
|
|
74
|
+
...(tool.path ? { path: field(tool.path, 400) } : {}),
|
|
75
|
+
active: tool.active,
|
|
76
|
+
});
|
|
77
|
+
const projectMcp = (mcp) => ({
|
|
78
|
+
servers: mcp.servers.map((server) => ({
|
|
79
|
+
name: field(server.name, 200) ?? "",
|
|
80
|
+
scope: server.scope,
|
|
81
|
+
status: server.status,
|
|
82
|
+
enabled: server.enabled,
|
|
83
|
+
...(server.transport ? { transport: field(server.transport, 80) } : {}),
|
|
84
|
+
...(server.command ? { command: field(server.command, 400) } : {}),
|
|
85
|
+
...(server.url ? { url: field(server.url, 400) } : {}),
|
|
86
|
+
...(server.configPath ? { configPath: field(server.configPath, 400) } : {}),
|
|
87
|
+
tools: server.tools.map(projectMcpTool),
|
|
88
|
+
})),
|
|
89
|
+
unassignedTools: mcp.unassignedTools.map(projectMcpTool),
|
|
90
|
+
});
|
|
91
|
+
const projectDiagnostic = (diag) => ({
|
|
92
|
+
type: diag.type,
|
|
93
|
+
...(diag.code ? { code: diag.code } : {}),
|
|
94
|
+
message: field(diag.message, 400) ?? "",
|
|
95
|
+
...(diag.path ? { path: field(diag.path, 400) } : {}),
|
|
96
|
+
...(diag.retryable !== undefined
|
|
97
|
+
? { retryable: diag.retryable }
|
|
98
|
+
: {}),
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
sessionId: input.sessionId,
|
|
102
|
+
revision: input.revision,
|
|
103
|
+
loadedAt: input.loadedAt,
|
|
104
|
+
selection: {
|
|
105
|
+
...(input.selection.model
|
|
106
|
+
? {
|
|
107
|
+
model: {
|
|
108
|
+
provider: input.selection.model.provider,
|
|
109
|
+
id: input.selection.model.id,
|
|
110
|
+
...(input.selection.model.name
|
|
111
|
+
? { name: field(input.selection.model.name, 200) }
|
|
112
|
+
: {}),
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
: {}),
|
|
116
|
+
thinkingSelection: input.selection.thinkingSelection,
|
|
117
|
+
...(input.selection.effectiveThinkingLevel
|
|
118
|
+
? { effectiveThinkingLevel: input.selection.effectiveThinkingLevel }
|
|
119
|
+
: {}),
|
|
120
|
+
},
|
|
121
|
+
models: input.models.map((model) => ({
|
|
122
|
+
provider: model.provider,
|
|
123
|
+
id: model.id,
|
|
124
|
+
...(model.name ? { name: field(model.name, 200) } : {}),
|
|
125
|
+
...(model.hasCredentials !== undefined
|
|
126
|
+
? { hasCredentials: model.hasCredentials }
|
|
127
|
+
: {}),
|
|
128
|
+
})),
|
|
129
|
+
thinkingLevels: input.thinkingLevels.map((option) => ({ ...option })),
|
|
130
|
+
systemPrompt: {
|
|
131
|
+
...(input.systemPrompt.raw !== undefined
|
|
132
|
+
? { raw: input.systemPrompt.raw }
|
|
133
|
+
: {}),
|
|
134
|
+
text: redactRuntimeText(input.systemPrompt.text, RUNTIME_CONTEXT_TEXT_MAX),
|
|
135
|
+
sha256: input.systemPrompt.sha256,
|
|
136
|
+
characters: input.systemPrompt.characters,
|
|
137
|
+
},
|
|
138
|
+
activeTools: [...input.activeTools],
|
|
139
|
+
contextFiles: input.contextFiles.map((file) => ({
|
|
140
|
+
path: field(file.path, 400) ?? "",
|
|
141
|
+
characters: file.characters,
|
|
142
|
+
})),
|
|
143
|
+
skills: input.skills.map(projectSkill),
|
|
144
|
+
extensions: input.extensions.map(projectExtension),
|
|
145
|
+
packages: input.packages.map(projectPackage),
|
|
146
|
+
mcp: projectMcp(input.mcp ?? { servers: [], unassignedTools: [] }),
|
|
147
|
+
diagnostics: input.diagnostics.map(projectDiagnostic),
|
|
148
|
+
hasBash: input.hasBash,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Browser-safe slash command projection for UI-11.
|
|
153
|
+
* Sources: current Session extension commands, prompt templates, skills.
|
|
154
|
+
* Never returns prompt/skill file bodies. Same-name conflict priority:
|
|
155
|
+
* extension > skill > prompt.
|
|
156
|
+
*/
|
|
157
|
+
export function collectSlashCommands(input) {
|
|
158
|
+
const scrub = (value) => {
|
|
159
|
+
const raw = String(value ?? "");
|
|
160
|
+
return raw
|
|
161
|
+
.replace(/\/Users\/[^\s]+/gi, "~")
|
|
162
|
+
.replace(/[A-Za-z]:\\Users\\[^\s]+/gi, "~")
|
|
163
|
+
.slice(0, 240);
|
|
164
|
+
};
|
|
165
|
+
const session = input.session;
|
|
166
|
+
if (!session)
|
|
167
|
+
return { commands: [] };
|
|
168
|
+
const SOURCE_RANK = {
|
|
169
|
+
extension: 0,
|
|
170
|
+
skill: 1,
|
|
171
|
+
prompt: 2,
|
|
172
|
+
};
|
|
173
|
+
const byKey = new Map();
|
|
174
|
+
const sourceWarnings = [];
|
|
175
|
+
const push = (command, label, description, source) => {
|
|
176
|
+
const name = command.startsWith("/") ? command : `/${command}`;
|
|
177
|
+
const key = name.toLowerCase();
|
|
178
|
+
if (!key || key === "/")
|
|
179
|
+
return;
|
|
180
|
+
const next = {
|
|
181
|
+
command: name,
|
|
182
|
+
label: (label || name).slice(0, 80),
|
|
183
|
+
description: scrub(description),
|
|
184
|
+
source,
|
|
185
|
+
};
|
|
186
|
+
const existing = byKey.get(key);
|
|
187
|
+
if (!existing || SOURCE_RANK[source] < SOURCE_RANK[existing.source]) {
|
|
188
|
+
byKey.set(key, next);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
try {
|
|
192
|
+
const cmds = session.extensionRunner?.getRegisteredCommands?.() ?? [];
|
|
193
|
+
for (const cmd of cmds) {
|
|
194
|
+
const name = cmd.invocationName || cmd.name;
|
|
195
|
+
if (!name)
|
|
196
|
+
continue;
|
|
197
|
+
push(String(name), String(cmd.name || name), String(cmd.description || ""), "extension");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
sourceWarnings.push(`extension: ${scrub(error instanceof Error ? error.message : String(error))}`);
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
for (const tpl of session.promptTemplates ?? []) {
|
|
205
|
+
if (!tpl?.name)
|
|
206
|
+
continue;
|
|
207
|
+
push(String(tpl.name), String(tpl.name), String(tpl.description || ""), "prompt");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
sourceWarnings.push(`prompt: ${scrub(error instanceof Error ? error.message : String(error))}`);
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const skills = input.resourceLoader?.getSkills?.()?.skills ?? [];
|
|
215
|
+
for (const skill of skills) {
|
|
216
|
+
if (!skill?.name)
|
|
217
|
+
continue;
|
|
218
|
+
const command = skill.name.startsWith("skill:")
|
|
219
|
+
? `/${skill.name}`
|
|
220
|
+
: `/skill:${skill.name}`;
|
|
221
|
+
push(command, skill.name, String(skill.description || ""), "skill");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
sourceWarnings.push(`skill: ${scrub(error instanceof Error ? error.message : String(error))}`);
|
|
226
|
+
}
|
|
227
|
+
const warning = sourceWarnings.length > 0
|
|
228
|
+
? sourceWarnings.join("; ").slice(0, 160)
|
|
229
|
+
: undefined;
|
|
230
|
+
return {
|
|
231
|
+
commands: [...byKey.values()],
|
|
232
|
+
...(warning ? { warning } : {}),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Collect ResourceLoader / package / MCP inventories for a live session.
|
|
237
|
+
* Preference-file loading stays with ConsolePiRuntime; this function only
|
|
238
|
+
* consumes already-loaded prefs (or a captured load error).
|
|
239
|
+
*/
|
|
240
|
+
export async function collectRuntimeInventories(input) {
|
|
241
|
+
const contextFiles = [];
|
|
242
|
+
const skills = [];
|
|
243
|
+
const extensions = [];
|
|
244
|
+
const diagnostics = [];
|
|
245
|
+
const resolvedResources = [];
|
|
246
|
+
const resourceFailure = (subface, error) => {
|
|
247
|
+
diagnostics.push({
|
|
248
|
+
type: "error",
|
|
249
|
+
code: "RESOURCE_LOAD_FAILED",
|
|
250
|
+
message: `${subface} inventory failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
251
|
+
retryable: true,
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
if (input.skillPrefsError !== undefined) {
|
|
255
|
+
diagnostics.push({
|
|
256
|
+
type: "error",
|
|
257
|
+
code: "PI_RESOURCE_PREFERENCES_MALFORMED",
|
|
258
|
+
message: input.skillPrefsError instanceof Error
|
|
259
|
+
? input.skillPrefsError.message
|
|
260
|
+
: String(input.skillPrefsError),
|
|
261
|
+
retryable: true,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
const resourceLoader = input.resourceLoader;
|
|
265
|
+
if (resourceLoader) {
|
|
266
|
+
try {
|
|
267
|
+
const agentsFiles = resourceLoader.getAgentsFiles?.();
|
|
268
|
+
for (const file of agentsFiles?.agentsFiles ?? []) {
|
|
269
|
+
if (!file.path)
|
|
270
|
+
continue;
|
|
271
|
+
contextFiles.push({
|
|
272
|
+
path: file.path,
|
|
273
|
+
characters: file.content?.length ?? 0,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
resourceFailure("context files", error);
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
const skillsResult = resourceLoader.getSkills?.();
|
|
282
|
+
for (const skill of skillsResult?.skills ?? []) {
|
|
283
|
+
const resourceId = skillResourceId({
|
|
284
|
+
name: skill.name,
|
|
285
|
+
filePath: skill.filePath,
|
|
286
|
+
});
|
|
287
|
+
const prefEnabled = isAutoInvocationEnabled(input.skillPrefs, resourceId);
|
|
288
|
+
const autoInvocationEnabled = prefEnabled && skill.disableModelInvocation !== true;
|
|
289
|
+
skills.push({
|
|
290
|
+
name: skill.name,
|
|
291
|
+
description: skill.description ?? "",
|
|
292
|
+
path: skill.filePath,
|
|
293
|
+
...(skill.baseDir ? { baseDir: skill.baseDir } : {}),
|
|
294
|
+
scope: skill.sourceInfo?.scope ?? "unknown",
|
|
295
|
+
source: skill.sourceInfo?.source ?? "",
|
|
296
|
+
resourceId,
|
|
297
|
+
autoInvocationEnabled,
|
|
298
|
+
diagnostics: [],
|
|
299
|
+
});
|
|
300
|
+
resolvedResources.push({
|
|
301
|
+
type: "skill",
|
|
302
|
+
name: skill.name,
|
|
303
|
+
path: skill.filePath,
|
|
304
|
+
source: skill.sourceInfo?.source ?? "",
|
|
305
|
+
scope: skill.sourceInfo?.scope ?? "unknown",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
for (const diag of skillsResult?.diagnostics ?? []) {
|
|
309
|
+
if (!diag?.message)
|
|
310
|
+
continue;
|
|
311
|
+
diagnostics.push({
|
|
312
|
+
type: diag.type ?? "warning",
|
|
313
|
+
message: diag.message,
|
|
314
|
+
...(diag.path ? { path: diag.path } : {}),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
resourceFailure("skills", error);
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const extResult = resourceLoader.getExtensions?.();
|
|
323
|
+
for (const ext of extResult?.extensions ?? []) {
|
|
324
|
+
const path = ext.resolvedPath ?? ext.path ?? "";
|
|
325
|
+
extensions.push({
|
|
326
|
+
name: ext.sourceInfo?.source ?? ext.path ?? "extension",
|
|
327
|
+
path,
|
|
328
|
+
...(ext.resolvedPath ? { resolvedPath: ext.resolvedPath } : {}),
|
|
329
|
+
source: ext.sourceInfo?.source ?? "",
|
|
330
|
+
scope: ext.sourceInfo?.scope ?? "unknown",
|
|
331
|
+
...(ext.sourceInfo?.origin ? { origin: ext.sourceInfo.origin } : {}),
|
|
332
|
+
enabled: true,
|
|
333
|
+
});
|
|
334
|
+
resolvedResources.push({
|
|
335
|
+
type: "extension",
|
|
336
|
+
name: ext.sourceInfo?.source ?? ext.path ?? "extension",
|
|
337
|
+
path,
|
|
338
|
+
source: ext.sourceInfo?.source ?? "",
|
|
339
|
+
scope: ext.sourceInfo?.scope ?? "unknown",
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
for (const err of extResult?.errors ?? []) {
|
|
343
|
+
if (!err?.error)
|
|
344
|
+
continue;
|
|
345
|
+
diagnostics.push({
|
|
346
|
+
type: "error",
|
|
347
|
+
message: err.error,
|
|
348
|
+
...(err.path ? { path: err.path } : {}),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
resourceFailure("extensions", error);
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
const promptsResult = resourceLoader.getPrompts?.();
|
|
357
|
+
for (const prompt of promptsResult?.prompts ?? []) {
|
|
358
|
+
resolvedResources.push({
|
|
359
|
+
type: "prompt",
|
|
360
|
+
name: prompt.name,
|
|
361
|
+
path: prompt.filePath,
|
|
362
|
+
source: prompt.sourceInfo?.source ?? "",
|
|
363
|
+
scope: prompt.sourceInfo?.scope ?? "unknown",
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
for (const diag of promptsResult?.diagnostics ?? []) {
|
|
367
|
+
if (!diag?.message)
|
|
368
|
+
continue;
|
|
369
|
+
diagnostics.push({
|
|
370
|
+
type: diag.type ?? "warning",
|
|
371
|
+
message: diag.message,
|
|
372
|
+
...(diag.path ? { path: diag.path } : {}),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
resourceFailure("prompts", error);
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
const themesResult = resourceLoader.getThemes?.();
|
|
381
|
+
for (const theme of themesResult?.themes ?? []) {
|
|
382
|
+
if (!theme.path)
|
|
383
|
+
continue;
|
|
384
|
+
resolvedResources.push({
|
|
385
|
+
type: "theme",
|
|
386
|
+
name: theme.name ?? theme.path,
|
|
387
|
+
path: theme.path,
|
|
388
|
+
source: "",
|
|
389
|
+
scope: "unknown",
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
for (const diag of themesResult?.diagnostics ?? []) {
|
|
393
|
+
if (!diag?.message)
|
|
394
|
+
continue;
|
|
395
|
+
diagnostics.push({
|
|
396
|
+
type: diag.type ?? "warning",
|
|
397
|
+
message: diag.message,
|
|
398
|
+
...(diag.path ? { path: diag.path } : {}),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
resourceFailure("themes", error);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const { readPackageInventory } = await import("../pi-console-config.js");
|
|
407
|
+
let packages = [];
|
|
408
|
+
try {
|
|
409
|
+
packages = (await readPackageInventory(input.agentDir, input.cwd, resolvedResources));
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
packages = [];
|
|
413
|
+
diagnostics.push({
|
|
414
|
+
type: "error",
|
|
415
|
+
code: "PACKAGE_INVENTORY_FAILED",
|
|
416
|
+
message: error instanceof Error ? error.message : String(error),
|
|
417
|
+
retryable: true,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
const { readMcpInventory } = await import("../mcp-inventory.js");
|
|
421
|
+
let mcp = { servers: [], unassignedTools: [] };
|
|
422
|
+
try {
|
|
423
|
+
mcp = await readMcpInventory({
|
|
424
|
+
agentDir: input.agentDir,
|
|
425
|
+
cwd: input.cwd,
|
|
426
|
+
tools: (Array.isArray(input.tools) ? input.tools : []),
|
|
427
|
+
activeToolNames: input.activeToolNames,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
diagnostics.push({
|
|
432
|
+
type: "error",
|
|
433
|
+
code: "MCP_INVENTORY_FAILED",
|
|
434
|
+
message: error instanceof Error ? error.message : String(error),
|
|
435
|
+
retryable: true,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
return { contextFiles, skills, extensions, packages, mcp, diagnostics };
|
|
439
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete Pi SDK adapter for Console Chat. Lazy-loads the ESM SDK and
|
|
3
|
+
* implements session-file safety (wx/0600, missing-file fail-closed).
|
|
4
|
+
*/
|
|
5
|
+
import { access, mkdir, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
export async function safeGetAgentDir(bindings) {
|
|
8
|
+
try {
|
|
9
|
+
return await bindings.getAgentDir();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// Fall back to a Console-app-data scoped agent dir so we never accidentally
|
|
13
|
+
// touch the user's real ~/.pi/agent config if getAgentDir throws.
|
|
14
|
+
return path.join(process.cwd(), ".harness", "console", "pi-agent-dir");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Cached SDK module loader. The pi-coding-agent package is ESM-only, so we
|
|
19
|
+
* must use dynamic import() (not require). Cached to avoid repeated imports.
|
|
20
|
+
*/
|
|
21
|
+
let cachedSdk;
|
|
22
|
+
async function loadSdk() {
|
|
23
|
+
if (!cachedSdk) {
|
|
24
|
+
cachedSdk = import("@earendil-works/pi-coding-agent");
|
|
25
|
+
}
|
|
26
|
+
return cachedSdk;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Default production bindings backed by the real Pi SDK (services mode).
|
|
30
|
+
* Dynamically imported so the worker bundle does not hard-fail if the SDK is
|
|
31
|
+
* absent at module load (discovered lazily by pi-readiness instead).
|
|
32
|
+
*/
|
|
33
|
+
export function createDefaultPiSdkBindings() {
|
|
34
|
+
return {
|
|
35
|
+
getAgentDir: async () => {
|
|
36
|
+
const sdk = (await loadSdk());
|
|
37
|
+
return sdk.getAgentDir();
|
|
38
|
+
},
|
|
39
|
+
createServices: async (opts) => {
|
|
40
|
+
const sdk = (await loadSdk());
|
|
41
|
+
// createAgentSessionServices returns the full AgentSessionServices object
|
|
42
|
+
// directly (cwd/agentDir/modelRuntime/settingsManager/resourceLoader).
|
|
43
|
+
// Wrap it so our type contract is satisfied.
|
|
44
|
+
const result = await sdk.createAgentSessionServices({
|
|
45
|
+
cwd: opts.cwd,
|
|
46
|
+
agentDir: opts.agentDir,
|
|
47
|
+
resourceLoaderOptions: opts.resourceLoaderOptions,
|
|
48
|
+
});
|
|
49
|
+
return { services: result };
|
|
50
|
+
},
|
|
51
|
+
createSessionFromServices: async (opts) => {
|
|
52
|
+
const sdk = (await loadSdk());
|
|
53
|
+
return sdk.createAgentSessionFromServices({
|
|
54
|
+
services: opts.services,
|
|
55
|
+
sessionManager: opts.sessionManager,
|
|
56
|
+
...(opts.tools ? { tools: opts.tools } : {}),
|
|
57
|
+
...(opts.noTools ? { noTools: opts.noTools } : {}),
|
|
58
|
+
...(opts.excludeTools ? { excludeTools: opts.excludeTools } : {}),
|
|
59
|
+
...(opts.customTools ? { customTools: opts.customTools } : {}),
|
|
60
|
+
...(opts.model ? { model: opts.model } : {}),
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
createSessionManager: async (opts) => {
|
|
64
|
+
const sdk = (await loadSdk());
|
|
65
|
+
return sdk.SessionManager.create(opts.cwd, opts.sessionDir, {
|
|
66
|
+
id: opts.sessionId,
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
materializeSessionShell: async (opts) => {
|
|
70
|
+
const sdk = (await loadSdk());
|
|
71
|
+
const manager = opts.sessionManager;
|
|
72
|
+
if (typeof manager.setSessionFile !== "function") {
|
|
73
|
+
throw new Error("Pi SessionManager cannot materialize a session file");
|
|
74
|
+
}
|
|
75
|
+
let created = false;
|
|
76
|
+
try {
|
|
77
|
+
await mkdir(path.dirname(opts.sessionFile), { recursive: true });
|
|
78
|
+
// Match Pi's documented session header. Rebinding through the public
|
|
79
|
+
// SessionManager API marks it flushed, so Pi appends the first user
|
|
80
|
+
// entry instead of trying to create the already-present file later.
|
|
81
|
+
await writeFile(opts.sessionFile, `${JSON.stringify({
|
|
82
|
+
type: "session",
|
|
83
|
+
version: typeof sdk.CURRENT_SESSION_VERSION === "number"
|
|
84
|
+
? sdk.CURRENT_SESSION_VERSION
|
|
85
|
+
: 3,
|
|
86
|
+
id: opts.sessionId,
|
|
87
|
+
timestamp: new Date().toISOString(),
|
|
88
|
+
cwd: opts.cwd,
|
|
89
|
+
})}\n`, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
90
|
+
created = true;
|
|
91
|
+
manager.setSessionFile(opts.sessionFile);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (created)
|
|
95
|
+
await rm(opts.sessionFile, { force: true }).catch(() => undefined);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
createInMemorySessionManager: async (opts) => {
|
|
100
|
+
const sdk = (await loadSdk());
|
|
101
|
+
return sdk.SessionManager.inMemory(opts.cwd);
|
|
102
|
+
},
|
|
103
|
+
forkSessionManager: async (opts) => {
|
|
104
|
+
const sdk = (await loadSdk());
|
|
105
|
+
return sdk.SessionManager.forkFrom(opts.sourcePath, opts.targetCwd, opts.targetSessionDir, { id: opts.targetSessionId });
|
|
106
|
+
},
|
|
107
|
+
openSessionManager: async (opts) => {
|
|
108
|
+
const sdk = (await loadSdk());
|
|
109
|
+
try {
|
|
110
|
+
// Pi SessionManager.open(missingPath) does NOT throw — it creates a
|
|
111
|
+
// fresh in-memory session with a random UUID. That would reopen the
|
|
112
|
+
// wrong conversation under the durable Console sessionId. Fail closed
|
|
113
|
+
// before open when the persisted file is absent.
|
|
114
|
+
await access(opts.sessionFile);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const code = error && typeof error === "object" && "code" in error
|
|
118
|
+
? String(error.code ?? "")
|
|
119
|
+
: "";
|
|
120
|
+
if (code === "ENOENT") {
|
|
121
|
+
throw Object.assign(new Error(`SESSION_FILE_MISSING: ${opts.sessionFile}`), { code: "SESSION_FILE_MISSING", cause: error });
|
|
122
|
+
}
|
|
123
|
+
// File exists but is unreadable (permissions / I/O) — caller maps
|
|
124
|
+
// undefined to 503 rather than silently creating a fresh session.
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return sdk.SessionManager.open(opts.sessionFile);
|
|
128
|
+
},
|
|
129
|
+
resolveModel: (opts) => {
|
|
130
|
+
const rt = opts.modelRuntime;
|
|
131
|
+
if (typeof rt?.getModel !== "function")
|
|
132
|
+
return undefined;
|
|
133
|
+
try {
|
|
134
|
+
return rt.getModel(opts.provider, opts.modelId);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
listAvailableModels: (opts) => {
|
|
141
|
+
const rt = opts.modelRuntime;
|
|
142
|
+
const available = rt?.snapshot?.available ?? [];
|
|
143
|
+
const configured = rt?.snapshot?.configuredProviders;
|
|
144
|
+
const isConfigured = (provider) => configured instanceof Set ? configured.has(provider) : false;
|
|
145
|
+
return available.map((m) => ({
|
|
146
|
+
id: m.id,
|
|
147
|
+
name: m.name,
|
|
148
|
+
provider: m.provider,
|
|
149
|
+
hasCredentials: isConfigured(m.provider),
|
|
150
|
+
}));
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|