@rryando/arcs 5.1.0 → 5.2.1
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/README.md +30 -17
- package/dist/cli/arcs-orchestrate.d.ts +1 -1
- package/dist/cli/arcs-orchestrate.d.ts.map +1 -1
- package/dist/cli/commands/bundle.js +39 -0
- package/dist/cli/commands/bundle.js.map +1 -1
- package/dist/cli/commands/proposal-doc.js +76 -58
- package/dist/cli/commands/proposal-doc.js.map +1 -1
- package/dist/cli/commands/web.js +2 -2
- package/dist/cli/commands/web.js.map +1 -1
- package/dist/cli/config.d.ts +37 -9
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +64 -5
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/instructions.d.ts +3 -3
- package/dist/cli/instructions.d.ts.map +1 -1
- package/dist/cli/instructions.js +10 -2
- package/dist/cli/instructions.js.map +1 -1
- package/dist/cli/orchestrator-shared-blocks.d.ts +1 -1
- package/dist/cli/orchestrator-shared-blocks.d.ts.map +1 -1
- package/dist/cli/orchestrator-shared-blocks.js +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +281 -47
- package/dist/cli/setup.js.map +1 -1
- package/dist/utils/json-schemas.d.ts +32 -9
- package/dist/utils/json-schemas.d.ts.map +1 -1
- package/dist/utils/json-schemas.js +8 -1
- package/dist/utils/json-schemas.js.map +1 -1
- package/dist/utils/project-resolver.d.ts.map +1 -1
- package/dist/utils/project-resolver.js +9 -2
- package/dist/utils/project-resolver.js.map +1 -1
- package/dist/utils/run-transcript.d.ts +138 -0
- package/dist/utils/run-transcript.d.ts.map +1 -0
- package/dist/utils/run-transcript.js +146 -0
- package/dist/utils/run-transcript.js.map +1 -0
- package/dist/utils/storage-utils.d.ts +1 -1
- package/dist/utils/storage-utils.d.ts.map +1 -1
- package/dist/utils/storage-utils.js +1 -1
- package/dist/utils/storage-utils.js.map +1 -1
- package/dist/web-client/assets/{GraphCanvas-BYZE4sO9.js → GraphCanvas-BgWF1rB_.js} +1 -1
- package/dist/web-client/assets/{MarkdownEditor-BKx6M1cw.js → MarkdownEditor-5buYPQ1d.js} +1 -1
- package/dist/web-client/assets/{abnfDiagram-VRR7QNED-D4kt0l2y.js → abnfDiagram-VRR7QNED-AjQ08-zY.js} +1 -1
- package/dist/web-client/assets/architecture-TIHT7OUA-a3lTAuFS.js +1 -0
- package/dist/web-client/assets/{architectureDiagram-ZJ3FMSHR-bu29SJFS.js → architectureDiagram-ZJ3FMSHR-CtW2pYEp.js} +1 -1
- package/dist/web-client/assets/{blockDiagram-677ZJIJ3-hsu7mXKw.js → blockDiagram-677ZJIJ3-DE6HRcpA.js} +1 -1
- package/dist/web-client/assets/{c4Diagram-LMCZKHZV-DMllRlT_.js → c4Diagram-LMCZKHZV-Rn6iQdUl.js} +1 -1
- package/dist/web-client/assets/channel-Rxk7gzWl.js +1 -0
- package/dist/web-client/assets/{chunk-32BRIVSS-BSzwj5eM.js → chunk-32BRIVSS-C4-ay_Qd.js} +1 -1
- package/dist/web-client/assets/{chunk-52WLFC77-RDdZn6yY.js → chunk-52WLFC77-Bib0DU0H.js} +1 -1
- package/dist/web-client/assets/{chunk-C7G6YPKG-DQr--txT.js → chunk-C7G6YPKG-BLphnESE.js} +1 -1
- package/dist/web-client/assets/{chunk-EX3LRPZG-Bb8nc4z4.js → chunk-EX3LRPZG-CzvkrPNM.js} +1 -1
- package/dist/web-client/assets/{chunk-FWX5IMBZ-QvBpOcOg.js → chunk-FWX5IMBZ-BFTR_OLz.js} +2 -2
- package/dist/web-client/assets/{chunk-HOUHSVGY-B0mX_yjc.js → chunk-HOUHSVGY-CZS3ExUR.js} +1 -1
- package/dist/web-client/assets/{chunk-ICXQ74PX-BmJSQqOH.js → chunk-ICXQ74PX-iWFpNZgv.js} +1 -1
- package/dist/web-client/assets/{chunk-MOJQB5TN-BrEI4GGn.js → chunk-MOJQB5TN-opEQCwVq.js} +1 -1
- package/dist/web-client/assets/{chunk-OGEWGWER-CrqDPOWP.js → chunk-OGEWGWER-NCfmjyhi.js} +1 -1
- package/dist/web-client/assets/{chunk-PUDLZKDR-DfPKldpG.js → chunk-PUDLZKDR-D3_A--Z2.js} +1 -1
- package/dist/web-client/assets/{chunk-Q4XR5HBZ-ekATI9aV.js → chunk-Q4XR5HBZ-D6s-qZLi.js} +1 -1
- package/dist/web-client/assets/{chunk-V7JOEXUC-C5APsP1t.js → chunk-V7JOEXUC-DTqLR-bZ.js} +1 -1
- package/dist/web-client/assets/{chunk-VAUOI2AC-BhfSWJZI.js → chunk-VAUOI2AC-Gsd0Itap.js} +1 -1
- package/dist/web-client/assets/{chunk-VR4S4FIN-BGa-44J7.js → chunk-VR4S4FIN-Cdo_MeDQ.js} +1 -1
- package/dist/web-client/assets/{chunk-WYO6CB5R-By1K0guW.js → chunk-WYO6CB5R-n7FhYrli.js} +1 -1
- package/dist/web-client/assets/{chunk-ZGVPDNZ5-Vnzpc76F.js → chunk-ZGVPDNZ5-DU1Jcue0.js} +1 -1
- package/dist/web-client/assets/classDiagram-OUVF2IWQ-BG5uYDvC.js +1 -0
- package/dist/web-client/assets/classDiagram-v2-EOCWNBFH-BG5uYDvC.js +1 -0
- package/dist/web-client/assets/{cynefin-VYW2F7L2-C6MOMOOz.js → cynefin-VYW2F7L2-D-gD9LCO.js} +1 -1
- package/dist/web-client/assets/{cynefinDiagram-TSTJHNR4-Cseyu79b.js → cynefinDiagram-TSTJHNR4-BFGkcrNl.js} +1 -1
- package/dist/web-client/assets/{dagre-VKFMJZFB-CQJlIuNh.js → dagre-VKFMJZFB-BUaC6fks.js} +1 -1
- package/dist/web-client/assets/{diagram-FQU43EPY-DpqDxhq4.js → diagram-FQU43EPY-Ckgu02XC.js} +1 -1
- package/dist/web-client/assets/{diagram-G47NLZAW-C11fYYaF.js → diagram-G47NLZAW-BickHhmE.js} +1 -1
- package/dist/web-client/assets/{diagram-NH7WQ7WH-BiG-uRAF.js → diagram-NH7WQ7WH-CoqgJpcU.js} +1 -1
- package/dist/web-client/assets/{diagram-OA4YK3LP-CIXWWjq-.js → diagram-OA4YK3LP-CTSZWqCe.js} +1 -1
- package/dist/web-client/assets/{diagram-WEI45ONY-C3OgIWu9.js → diagram-WEI45ONY-D7xd1naU.js} +1 -1
- package/dist/web-client/assets/{ebnfDiagram-CCIWWBDH-BHF_NA3_.js → ebnfDiagram-CCIWWBDH-FESKYpKQ.js} +1 -1
- package/dist/web-client/assets/{erDiagram-Q63AITRT-CUOJCrLy.js → erDiagram-Q63AITRT-C-879ftU.js} +1 -1
- package/dist/web-client/assets/eventmodeling-45OFAUF4-C3kRZCei.js +1 -0
- package/dist/web-client/assets/flowDiagram-23GEKE2U-QKWFycdH.js +1 -0
- package/dist/web-client/assets/{ganttDiagram-NO4QXBWP-D_4BMJ-g.js → ganttDiagram-NO4QXBWP-mIX5nPpv.js} +1 -1
- package/dist/web-client/assets/{gitGraph-TEB2WS4Q-CBuaZBId.js → gitGraph-TEB2WS4Q-DlEDJ1IK.js} +1 -1
- package/dist/web-client/assets/{gitGraphDiagram-IHSO6WYX-BbztsGuO.js → gitGraphDiagram-IHSO6WYX-D2fXD4AO.js} +1 -1
- package/dist/web-client/assets/index-BsYR-C8u.js +61 -0
- package/dist/web-client/assets/index-Cc5DCTOr.css +2 -0
- package/dist/web-client/assets/{info-DKCQHKI2-Bzi0Xjro.js → info-DKCQHKI2-jinbCROt.js} +1 -1
- package/dist/web-client/assets/{infoDiagram-FWYZ7A6U-BkeEKcr0.js → infoDiagram-FWYZ7A6U-CM-A4TL8.js} +1 -1
- package/dist/web-client/assets/{ishikawaDiagram-FXEZZL3T-CVusWu0p.js → ishikawaDiagram-FXEZZL3T-BdFpHwp4.js} +1 -1
- package/dist/web-client/assets/{journeyDiagram-5HDEW3XC-BUxu71zw.js → journeyDiagram-5HDEW3XC-BwDWz25z.js} +1 -1
- package/dist/web-client/assets/{kanban-definition-HUTT4EX6-BE8Hv4Kd.js → kanban-definition-HUTT4EX6-_2QXjSvx.js} +1 -1
- package/dist/web-client/assets/{line-CiAoINJS.js → line-HCziev87.js} +1 -1
- package/dist/web-client/assets/{mermaid-parser.core-BW47khiS.js → mermaid-parser.core-C4139mkz.js} +3 -3
- package/dist/web-client/assets/{mermaid.core-DP--Jl9R.js → mermaid.core-ocHwkZiF.js} +3 -3
- package/dist/web-client/assets/{mindmap-definition-LN4V7U3C-CvWJUqdq.js → mindmap-definition-LN4V7U3C-DHyCmflp.js} +1 -1
- package/dist/web-client/assets/{packet-7NZHBO7P-XtzX9SaQ.js → packet-7NZHBO7P-Bk0w_irl.js} +1 -1
- package/dist/web-client/assets/{pegDiagram-2B236MQR-XNo0K1ct.js → pegDiagram-2B236MQR-D_GrmL93.js} +1 -1
- package/dist/web-client/assets/{pie-RZYD4A2V-B1eUd9yt.js → pie-RZYD4A2V-B6VIAbyj.js} +1 -1
- package/dist/web-client/assets/{pieDiagram-ENE6RG2P-CRSb5z-4.js → pieDiagram-ENE6RG2P-CiR2Y8y2.js} +1 -1
- package/dist/web-client/assets/{quadrantDiagram-ABIIQ3AL-DziMaQxE.js → quadrantDiagram-ABIIQ3AL-CLlicKmV.js} +1 -1
- package/dist/web-client/assets/{radar-I7S5WNFK-BpRqZH2g.js → radar-I7S5WNFK-B53XVRSZ.js} +1 -1
- package/dist/web-client/assets/{railroad-3IZDKUUU-FDIHW04k.js → railroad-3IZDKUUU-Dn8TN2M-.js} +1 -1
- package/dist/web-client/assets/railroad-abnf-AHOZXSZD-UrOKy6yx.js +1 -0
- package/dist/web-client/assets/railroad-ebnf-EBAXGLYW-Bx73sfMZ.js +1 -0
- package/dist/web-client/assets/railroad-peg-LSFZ7HO6-C4QEje-y.js +1 -0
- package/dist/web-client/assets/{railroadDiagram-RFXS5EU6-Cs7EVBaz.js → railroadDiagram-RFXS5EU6-C6kZwOio.js} +1 -1
- package/dist/web-client/assets/{requirementDiagram-TGXJPOKE-B9_Lj1ga.js → requirementDiagram-TGXJPOKE-D2b1MeP8.js} +1 -1
- package/dist/web-client/assets/{sankeyDiagram-HTMAVEWB-DuTJTiy0.js → sankeyDiagram-HTMAVEWB-pS_Ufq1E.js} +1 -1
- package/dist/web-client/assets/{sequenceDiagram-DBY2YBRQ-vOCU5UoE.js → sequenceDiagram-DBY2YBRQ-BrsKXNzu.js} +1 -1
- package/dist/web-client/assets/{stateDiagram-2N3HPSRC-rxAnfzWn.js → stateDiagram-2N3HPSRC-BuQKu3i0.js} +1 -1
- package/dist/web-client/assets/stateDiagram-v2-6OUMAXLB-BhqgaxDu.js +1 -0
- package/dist/web-client/assets/{swimlanes-5IMT3BWC-gdVZUnPe.js → swimlanes-5IMT3BWC-CzoImfTA.js} +1 -1
- package/dist/web-client/assets/swimlanesDiagram-G3AALYLV-CHOEI9hI.js +8 -0
- package/dist/web-client/assets/{timeline-definition-FHXFAJF6-Do68JyGy.js → timeline-definition-FHXFAJF6-DvezW7Fr.js} +1 -1
- package/dist/web-client/assets/{treeView-QDETBFTQ-CdZkmg80.js → treeView-QDETBFTQ-xu58R4kS.js} +1 -1
- package/dist/web-client/assets/{treemap-6X3UGDF4-ub54fmpW.js → treemap-6X3UGDF4-DMYBAOW1.js} +1 -1
- package/dist/web-client/assets/{vennDiagram-L72KCM5P-D-r8NBEp.js → vennDiagram-L72KCM5P-l1CbC7Ig.js} +1 -1
- package/dist/web-client/assets/{wardley-OPB4EBWU-CTleH1-J.js → wardley-OPB4EBWU-G3zmnnM0.js} +1 -1
- package/dist/web-client/assets/{wardleyDiagram-EHGQE667-BU8Kw39F.js → wardleyDiagram-EHGQE667-Dl2I79Vx.js} +1 -1
- package/dist/web-client/assets/{xychartDiagram-FW5EYKEG-CfNnag6i.js → xychartDiagram-FW5EYKEG-BWfge21N.js} +1 -1
- package/dist/web-client/index.html +2 -2
- package/dist/web-server/app.d.ts.map +1 -1
- package/dist/web-server/app.js +13 -7
- package/dist/web-server/app.js.map +1 -1
- package/dist/web-server/ask-prompt.d.ts +70 -0
- package/dist/web-server/ask-prompt.d.ts.map +1 -0
- package/dist/web-server/ask-prompt.js +238 -0
- package/dist/web-server/ask-prompt.js.map +1 -0
- package/dist/web-server/index.d.ts.map +1 -1
- package/dist/web-server/index.js +56 -3
- package/dist/web-server/index.js.map +1 -1
- package/dist/web-server/respond.d.ts.map +1 -1
- package/dist/web-server/respond.js +6 -1
- package/dist/web-server/respond.js.map +1 -1
- package/dist/web-server/routes/ask.d.ts +29 -0
- package/dist/web-server/routes/ask.d.ts.map +1 -0
- package/dist/web-server/routes/ask.js +698 -0
- package/dist/web-server/routes/ask.js.map +1 -0
- package/dist/web-server/routes/changes.d.ts +35 -0
- package/dist/web-server/routes/changes.d.ts.map +1 -0
- package/dist/web-server/routes/changes.js +93 -0
- package/dist/web-server/routes/changes.js.map +1 -0
- package/dist/web-server/routes/projects.d.ts.map +1 -1
- package/dist/web-server/routes/projects.js +10 -2
- package/dist/web-server/routes/projects.js.map +1 -1
- package/dist/web-server/routes/proposal-docs.d.ts +18 -0
- package/dist/web-server/routes/proposal-docs.d.ts.map +1 -0
- package/dist/web-server/routes/proposal-docs.js +208 -0
- package/dist/web-server/routes/proposal-docs.js.map +1 -0
- package/dist/web-server/routes/runners.d.ts +15 -0
- package/dist/web-server/routes/runners.d.ts.map +1 -0
- package/dist/web-server/routes/runners.js +66 -0
- package/dist/web-server/routes/runners.js.map +1 -0
- package/dist/web-server/routes/workspace.d.ts +2 -2
- package/dist/web-server/routes/workspace.js +4 -4
- package/dist/web-server/run-diff.d.ts +188 -0
- package/dist/web-server/run-diff.d.ts.map +1 -0
- package/dist/web-server/run-diff.js +739 -0
- package/dist/web-server/run-diff.js.map +1 -0
- package/dist/web-server/run-driver.d.ts +102 -2
- package/dist/web-server/run-driver.d.ts.map +1 -1
- package/dist/web-server/run-driver.js +495 -1
- package/dist/web-server/run-driver.js.map +1 -1
- package/dist/web-server/run-event-log.d.ts +13 -6
- package/dist/web-server/run-event-log.d.ts.map +1 -1
- package/dist/web-server/run-event-log.js +70 -25
- package/dist/web-server/run-event-log.js.map +1 -1
- package/dist/web-server/run-store.d.ts +201 -0
- package/dist/web-server/run-store.d.ts.map +1 -0
- package/dist/web-server/run-store.js +304 -0
- package/dist/web-server/run-store.js.map +1 -0
- package/dist/web-server/watcher.d.ts.map +1 -1
- package/dist/web-server/watcher.js +4 -0
- package/dist/web-server/watcher.js.map +1 -1
- package/opencode/arcs/manifest.json +8 -8
- package/opencode/arcs/prompts/arcs-orchestrate-caveman.txt +1 -1
- package/opencode/arcs/prompts/arcs-orchestrate.txt +1 -1
- package/opencode/arcs/skills/writing-proposals/SKILL.md +6 -3
- package/package.json +1 -1
- package/scripts/deploy-claudecode-bundle.mjs +2 -2
- package/scripts/deploy-opencode-bundle.mjs +1 -1
- package/scripts/deploy-pi-bundle.mjs +468 -0
- package/scripts/lint-bundle.mjs +1 -1
- package/dist/shared/session-vocabulary.d.ts +0 -88
- package/dist/shared/session-vocabulary.d.ts.map +0 -1
- package/dist/shared/session-vocabulary.js +0 -114
- package/dist/shared/session-vocabulary.js.map +0 -1
- package/dist/utils/claude-transcript.d.ts +0 -206
- package/dist/utils/claude-transcript.d.ts.map +0 -1
- package/dist/utils/claude-transcript.js +0 -384
- package/dist/utils/claude-transcript.js.map +0 -1
- package/dist/utils/session-store.d.ts +0 -313
- package/dist/utils/session-store.d.ts.map +0 -1
- package/dist/utils/session-store.js +0 -513
- package/dist/utils/session-store.js.map +0 -1
- package/dist/web-client/assets/architecture-TIHT7OUA-CIdGE6VO.js +0 -1
- package/dist/web-client/assets/channel-CBg-s-ZD.js +0 -1
- package/dist/web-client/assets/classDiagram-OUVF2IWQ-ciwDjhUR.js +0 -1
- package/dist/web-client/assets/classDiagram-v2-EOCWNBFH-ciwDjhUR.js +0 -1
- package/dist/web-client/assets/eventmodeling-45OFAUF4-DxfboL1J.js +0 -1
- package/dist/web-client/assets/flowDiagram-23GEKE2U-C3NzpTtI.js +0 -1
- package/dist/web-client/assets/index-B1KVIr80.css +0 -2
- package/dist/web-client/assets/index-BWd2fBNL.js +0 -60
- package/dist/web-client/assets/railroad-abnf-AHOZXSZD-BKj6JAhH.js +0 -1
- package/dist/web-client/assets/railroad-ebnf-EBAXGLYW-ChDM1OBv.js +0 -1
- package/dist/web-client/assets/railroad-peg-LSFZ7HO6-DvwA2e0i.js +0 -1
- package/dist/web-client/assets/stateDiagram-v2-6OUMAXLB-B3EvQPEe.js +0 -1
- package/dist/web-client/assets/swimlanesDiagram-G3AALYLV-WcOXuCPg.js +0 -8
- package/dist/web-server/permission-policy.d.ts +0 -46
- package/dist/web-server/permission-policy.d.ts.map +0 -1
- package/dist/web-server/permission-policy.js +0 -96
- package/dist/web-server/permission-policy.js.map +0 -1
- package/dist/web-server/prompt-assembly.d.ts +0 -454
- package/dist/web-server/prompt-assembly.d.ts.map +0 -1
- package/dist/web-server/prompt-assembly.js +0 -1121
- package/dist/web-server/prompt-assembly.js.map +0 -1
- package/dist/web-server/routes/sessions.d.ts +0 -19
- package/dist/web-server/routes/sessions.d.ts.map +0 -1
- package/dist/web-server/routes/sessions.js +0 -1296
- package/dist/web-server/routes/sessions.js.map +0 -1
- package/dist/web-server/session-reconciler.d.ts +0 -162
- package/dist/web-server/session-reconciler.d.ts.map +0 -1
- package/dist/web-server/session-reconciler.js +0 -363
- package/dist/web-server/session-reconciler.js.map +0 -1
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Deploy pi ARCS bundle from repo to pi config.
|
|
4
|
+
//
|
|
5
|
+
// Direction: repo → config ONLY. Never writes config → repo.
|
|
6
|
+
//
|
|
7
|
+
// What gets deployed:
|
|
8
|
+
// 1. Sub-agent prompts → <destination>/agent/agents/<stem>.md (global)
|
|
9
|
+
// <destination>/.pi/agents/<stem>.md (project)
|
|
10
|
+
// 2. Skills tree → <destination>/agent/skills/arcs-<name>/... (global)
|
|
11
|
+
// <destination>/.pi/skills/arcs-<name>/... (project)
|
|
12
|
+
//
|
|
13
|
+
// The compiled agent format targets the pi subagents extension
|
|
14
|
+
// (@tintinweb/pi-subagents) custom agent types: a Claude Code-shaped
|
|
15
|
+
// .md with YAML frontmatter in pi's agent directory. pi's main session is
|
|
16
|
+
// the orchestrator, so there is no settings.json default-agent merge —
|
|
17
|
+
// every agent (including the arcs-* primary prompts) deploys as a spawnable
|
|
18
|
+
// subagent type via the `Agent` tool / `@type` mentions.
|
|
19
|
+
//
|
|
20
|
+
// Frontmatter mapping (ARCS manifest → pi agent type):
|
|
21
|
+
// - `model: inherit` is OMITTED — "inherit parent" is the extension default.
|
|
22
|
+
// A pinned DEPLOY_MODEL_* value emits `model: <value>` instead.
|
|
23
|
+
// - permissions → `tools:` allowlist: edit→write/edit, bash→bash,
|
|
24
|
+
// mcp→ext:mcp, base read-only set read/grep/find/ls. Read-only
|
|
25
|
+
// specialists (code-reviewer, tech-architect, graph-explorer) get no
|
|
26
|
+
// mutation tools.
|
|
27
|
+
// - `task: allow` (primaries only) → `allowed_subagents: all`, mirroring
|
|
28
|
+
// the Task tool those prompts were written against.
|
|
29
|
+
//
|
|
30
|
+
// Env vars:
|
|
31
|
+
// DEPLOY_BUNDLE_ROOT — override bundle root (default: opencode/arcs)
|
|
32
|
+
// DEPLOY_CONFIG_ROOT — override config root (default: ~/.pi)
|
|
33
|
+
// DEPLOY_PROJECT_ROOT — override project root (default: repository root)
|
|
34
|
+
// DEPLOY_SCOPE — `global` or `project` (default: `global`)
|
|
35
|
+
// DEPLOY_DRY_RUN — "false" to actually write; anything else = dry-run (default: dry-run)
|
|
36
|
+
// DEPLOY_MODEL_HEAVY/STANDARD/LIGHT — 3-tier model overrides (default: "inherit")
|
|
37
|
+
//
|
|
38
|
+
// Outputs JSON to stdout: DeployResult. Exit code 0 on success, 1 on error.
|
|
39
|
+
// codegraph/rtk are never wired: neither tool supports a pi install target.
|
|
40
|
+
|
|
41
|
+
import { createHash } from "node:crypto";
|
|
42
|
+
import {
|
|
43
|
+
existsSync,
|
|
44
|
+
mkdirSync,
|
|
45
|
+
readdirSync,
|
|
46
|
+
readFileSync,
|
|
47
|
+
rmSync,
|
|
48
|
+
statSync,
|
|
49
|
+
writeFileSync,
|
|
50
|
+
} from "node:fs";
|
|
51
|
+
import { homedir } from "node:os";
|
|
52
|
+
import { dirname, relative, resolve } from "node:path";
|
|
53
|
+
import { fileURLToPath } from "node:url";
|
|
54
|
+
import {
|
|
55
|
+
isActiveAgentForMode,
|
|
56
|
+
isRetiredAgentForMode,
|
|
57
|
+
validateRetirementReplacements,
|
|
58
|
+
} from "./lib/bundle-helpers.mjs";
|
|
59
|
+
|
|
60
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
61
|
+
const repoRoot = resolve(scriptDir, "..");
|
|
62
|
+
const defaultBundleRoot = resolve(repoRoot, "opencode/arcs");
|
|
63
|
+
const defaultConfigRoot = resolve(homedir(), ".pi");
|
|
64
|
+
const defaultProjectRoot = repoRoot;
|
|
65
|
+
|
|
66
|
+
const bundleRoot = process.env.DEPLOY_BUNDLE_ROOT
|
|
67
|
+
? resolve(repoRoot, process.env.DEPLOY_BUNDLE_ROOT)
|
|
68
|
+
: defaultBundleRoot;
|
|
69
|
+
|
|
70
|
+
let configRoot;
|
|
71
|
+
if (process.env.DEPLOY_CONFIG_ROOT) {
|
|
72
|
+
const rawPath = process.env.DEPLOY_CONFIG_ROOT;
|
|
73
|
+
if (rawPath.startsWith("~/")) {
|
|
74
|
+
configRoot = resolve(homedir(), rawPath.slice(2));
|
|
75
|
+
} else if (rawPath === "~") {
|
|
76
|
+
configRoot = homedir();
|
|
77
|
+
} else {
|
|
78
|
+
configRoot = resolve(homedir(), rawPath);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
configRoot = defaultConfigRoot;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const projectRoot = process.env.DEPLOY_PROJECT_ROOT
|
|
85
|
+
? resolve(repoRoot, process.env.DEPLOY_PROJECT_ROOT)
|
|
86
|
+
: defaultProjectRoot;
|
|
87
|
+
|
|
88
|
+
const scope = process.env.DEPLOY_SCOPE === "project" ? "project" : "global";
|
|
89
|
+
const dryRun = process.env.DEPLOY_DRY_RUN !== "false";
|
|
90
|
+
|
|
91
|
+
// Project deploys write into the project's own pi config dir; global deploys
|
|
92
|
+
// write into ~/.pi (agents/skills land under agent/, the pi agent dir).
|
|
93
|
+
const destination = scope === "project" ? resolve(projectRoot, ".pi") : configRoot;
|
|
94
|
+
|
|
95
|
+
const SKILL_PREFIX = "arcs-";
|
|
96
|
+
|
|
97
|
+
function isPromptPath(value) {
|
|
98
|
+
if (typeof value !== "string" || value.includes("\\")) return false;
|
|
99
|
+
const segments = value.split("/");
|
|
100
|
+
return (
|
|
101
|
+
segments.length >= 2 &&
|
|
102
|
+
segments[0] === "prompts" &&
|
|
103
|
+
segments.slice(1).every((segment) => segment !== "" && segment !== "." && segment !== "..") &&
|
|
104
|
+
segments.at(-1).endsWith(".txt")
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function resolveBundlePromptPath(value) {
|
|
109
|
+
if (!isPromptPath(value)) throw new Error(`Invalid bundle prompt path: ${value}`);
|
|
110
|
+
const promptsRoot = resolve(bundleRoot, "prompts");
|
|
111
|
+
const resolvedPath = resolve(promptsRoot, ...value.split("/").slice(1));
|
|
112
|
+
const relativePath = relative(promptsRoot, resolvedPath);
|
|
113
|
+
if (relativePath.split(/[\\/]/).includes("..")) {
|
|
114
|
+
throw new Error(`Bundle prompt path escapes prompts/: ${value}`);
|
|
115
|
+
}
|
|
116
|
+
return resolvedPath;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Model tier configuration
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Env vars: DEPLOY_MODEL_HEAVY, DEPLOY_MODEL_STANDARD, DEPLOY_MODEL_LIGHT.
|
|
123
|
+
// "inherit" (the default) means the agent inherits the parent pi session's
|
|
124
|
+
// model — the extension default — so no `model:` frontmatter is emitted.
|
|
125
|
+
const tierModels = {
|
|
126
|
+
heavy: process.env.DEPLOY_MODEL_HEAVY || "inherit",
|
|
127
|
+
standard: process.env.DEPLOY_MODEL_STANDARD || "inherit",
|
|
128
|
+
light: process.env.DEPLOY_MODEL_LIGHT || "inherit",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
function readAgentRegistry() {
|
|
132
|
+
const manifestPath = resolve(bundleRoot, "manifest.json");
|
|
133
|
+
if (!existsSync(manifestPath)) throw new Error(`Agent registry not found at ${manifestPath}`);
|
|
134
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
135
|
+
if (!Array.isArray(manifest.agents))
|
|
136
|
+
throw new Error("Invalid agent registry: agents must be an array");
|
|
137
|
+
|
|
138
|
+
const ids = new Set();
|
|
139
|
+
const sources = new Set();
|
|
140
|
+
const destinations = new Set();
|
|
141
|
+
for (const agent of manifest.agents) {
|
|
142
|
+
const valid =
|
|
143
|
+
agent &&
|
|
144
|
+
typeof agent.id === "string" &&
|
|
145
|
+
/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agent.id) &&
|
|
146
|
+
["active", "retired"].includes(agent.status) &&
|
|
147
|
+
["primary", "subagent"].includes(agent.kind) &&
|
|
148
|
+
["heavy", "standard", "light"].includes(agent.tier) &&
|
|
149
|
+
Array.isArray(agent.modes) &&
|
|
150
|
+
agent.modes.length > 0 &&
|
|
151
|
+
agent.modes.every((mode) => ["opencode", "claudecode", "pi"].includes(mode)) &&
|
|
152
|
+
isPromptPath(agent.source) &&
|
|
153
|
+
isPromptPath(agent.destination) &&
|
|
154
|
+
typeof agent.description === "string" &&
|
|
155
|
+
agent.description.length > 0 &&
|
|
156
|
+
agent.permissions &&
|
|
157
|
+
["allow", "deny"].includes(agent.permissions.edit) &&
|
|
158
|
+
["allow", "deny"].includes(agent.permissions.bash) &&
|
|
159
|
+
["allow", "deny"].includes(agent.permissions.webfetch) &&
|
|
160
|
+
["allow", "deny"].includes(agent.permissions.mcp) &&
|
|
161
|
+
["allow", "deny"].includes(agent.permissions.task);
|
|
162
|
+
if (!valid) throw new Error(`Invalid agent registry record: ${JSON.stringify(agent)}`);
|
|
163
|
+
if (ids.has(agent.id)) throw new Error(`Duplicate agent registry id: ${agent.id}`);
|
|
164
|
+
if (sources.has(agent.source))
|
|
165
|
+
throw new Error(`Duplicate agent registry source: ${agent.source}`);
|
|
166
|
+
if (destinations.has(agent.destination))
|
|
167
|
+
throw new Error(`Duplicate agent registry destination: ${agent.destination}`);
|
|
168
|
+
ids.add(agent.id);
|
|
169
|
+
sources.add(agent.source);
|
|
170
|
+
destinations.add(agent.destination);
|
|
171
|
+
}
|
|
172
|
+
validateRetirementReplacements(manifest.agents);
|
|
173
|
+
return manifest.agents;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// pi destination layout
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
function agentConfigRelative(stem) {
|
|
181
|
+
return scope === "project" ? `agents/${stem}.md` : `agent/agents/${stem}.md`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function skillConfigRelative(skillName, rel) {
|
|
185
|
+
const skillsRoot = scope === "project" ? "skills" : "agent/skills";
|
|
186
|
+
return `${skillsRoot}/${SKILL_PREFIX}${skillName}/${rel}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function manifestConfigRelative() {
|
|
190
|
+
return ".arcs-bundle.json";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// 1. Compile sub-agent prompts
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
function piTools(agent) {
|
|
198
|
+
const tools = ["read", "grep", "find", "ls"];
|
|
199
|
+
if (agent.permissions.edit === "allow") tools.push("write", "edit");
|
|
200
|
+
if (agent.permissions.bash === "allow") tools.push("bash");
|
|
201
|
+
if (agent.permissions.mcp === "allow") tools.push("ext:mcp");
|
|
202
|
+
return tools.join(", ");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function buildAgentSources() {
|
|
206
|
+
const promptsDir = resolve(bundleRoot, "prompts");
|
|
207
|
+
if (!existsSync(promptsDir)) {
|
|
208
|
+
throw new Error(`Prompts directory not found at ${promptsDir}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const registry = readAgentRegistry();
|
|
212
|
+
const registeredSources = new Set(registry.map((agent) => agent.source));
|
|
213
|
+
const promptFiles = readdirSync(promptsDir).filter((file) => file.endsWith(".txt"));
|
|
214
|
+
for (const file of promptFiles) {
|
|
215
|
+
const source = `prompts/${file}`;
|
|
216
|
+
if (!registeredSources.has(source)) throw new Error(`Unregistered prompt file: ${source}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const sources = [];
|
|
220
|
+
|
|
221
|
+
for (const agent of registry.filter((record) => isActiveAgentForMode(record, "pi"))) {
|
|
222
|
+
const stem = agent.id;
|
|
223
|
+
const sourcePath = resolveBundlePromptPath(agent.source);
|
|
224
|
+
if (!existsSync(sourcePath))
|
|
225
|
+
throw new Error(`Registered agent prompt not found: ${agent.source}`);
|
|
226
|
+
const promptContent = readFileSync(sourcePath, "utf-8");
|
|
227
|
+
const model = tierModels[agent.tier];
|
|
228
|
+
|
|
229
|
+
const frontmatter = ["---", `name: ${stem}`, `description: ${agent.description}`];
|
|
230
|
+
|
|
231
|
+
// "inherit" is the extension default — omit it so the agent inherits the
|
|
232
|
+
// parent session's model (matches Claude Code `model: inherit`).
|
|
233
|
+
if (model !== "inherit") {
|
|
234
|
+
frontmatter.push(`model: ${model}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
frontmatter.push(`tools: ${piTools(agent)}`);
|
|
238
|
+
|
|
239
|
+
// primaries were authored with a Task tool (task: allow); on pi that is
|
|
240
|
+
// ownership-scoped nested delegation.
|
|
241
|
+
if (agent.permissions.task === "allow") {
|
|
242
|
+
frontmatter.push("allowed_subagents: all");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const compiledContent = [...frontmatter, "---", "", promptContent].join("\n");
|
|
246
|
+
|
|
247
|
+
sources.push({
|
|
248
|
+
stem,
|
|
249
|
+
compiledContent,
|
|
250
|
+
configRelative: agentConfigRelative(stem),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return sources;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
// 2. Walk skills tree
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
function buildSkillSources() {
|
|
262
|
+
const skillsDir = resolve(bundleRoot, "skills");
|
|
263
|
+
if (!existsSync(skillsDir)) return { skillNames: [], files: [] };
|
|
264
|
+
|
|
265
|
+
const skillNames = readdirSync(skillsDir, { withFileTypes: true })
|
|
266
|
+
.filter((e) => e.isDirectory())
|
|
267
|
+
.map((e) => e.name);
|
|
268
|
+
|
|
269
|
+
const files = [];
|
|
270
|
+
for (const skillName of skillNames) {
|
|
271
|
+
const skillRoot = resolve(skillsDir, skillName);
|
|
272
|
+
for (const absPath of walkFiles(skillRoot)) {
|
|
273
|
+
const rel = relative(skillRoot, absPath).split(/[\\/]/).join("/");
|
|
274
|
+
files.push({
|
|
275
|
+
skillName,
|
|
276
|
+
absSource: absPath,
|
|
277
|
+
configRelative: skillConfigRelative(skillName, rel),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return { skillNames, files };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function walkFiles(dir) {
|
|
286
|
+
const out = [];
|
|
287
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
288
|
+
const full = resolve(dir, entry.name);
|
|
289
|
+
if (entry.isDirectory()) {
|
|
290
|
+
out.push(...walkFiles(full));
|
|
291
|
+
} else if (entry.isFile()) {
|
|
292
|
+
out.push(full);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function sha256(content) {
|
|
299
|
+
return createHash("sha256").update(content).digest("hex");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function readInstalledManifest() {
|
|
303
|
+
const manifestPath = resolve(destination, manifestConfigRelative());
|
|
304
|
+
if (!existsSync(manifestPath)) return null;
|
|
305
|
+
try {
|
|
306
|
+
const value = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
307
|
+
return value?.bundleId === "arcs-pi-bundle" ? value : null;
|
|
308
|
+
} catch {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function ensureParentDir(filePath) {
|
|
314
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Main
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
|
|
321
|
+
function main() {
|
|
322
|
+
const registry = readAgentRegistry();
|
|
323
|
+
const previousManifest = readInstalledManifest();
|
|
324
|
+
const agentSources = buildAgentSources();
|
|
325
|
+
const skillSources = buildSkillSources();
|
|
326
|
+
|
|
327
|
+
// Pass 1: Classify add/change/unchanged for every planned write
|
|
328
|
+
const filesAdded = [];
|
|
329
|
+
const filesChanged = [];
|
|
330
|
+
const filesUnchanged = [];
|
|
331
|
+
|
|
332
|
+
for (const { compiledContent, configRelative } of agentSources) {
|
|
333
|
+
const abs = resolve(destination, configRelative);
|
|
334
|
+
if (!existsSync(abs)) {
|
|
335
|
+
filesAdded.push(configRelative);
|
|
336
|
+
} else if (readFileSync(abs, "utf-8") !== compiledContent) {
|
|
337
|
+
filesChanged.push(configRelative);
|
|
338
|
+
} else {
|
|
339
|
+
filesUnchanged.push(configRelative);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
for (const { absSource, configRelative } of skillSources.files) {
|
|
344
|
+
const abs = resolve(destination, configRelative);
|
|
345
|
+
const sourceContent = readFileSync(absSource);
|
|
346
|
+
if (!existsSync(abs)) {
|
|
347
|
+
filesAdded.push(configRelative);
|
|
348
|
+
} else {
|
|
349
|
+
const existing = readFileSync(abs);
|
|
350
|
+
if (Buffer.compare(existing, sourceContent) !== 0) {
|
|
351
|
+
filesChanged.push(configRelative);
|
|
352
|
+
} else {
|
|
353
|
+
filesUnchanged.push(configRelative);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Orphans
|
|
359
|
+
const filesRemoved = [];
|
|
360
|
+
|
|
361
|
+
// Skill orphans — only `arcs-*/` directories that aren't in current source.
|
|
362
|
+
// The prefix gives us a safe namespace; foreign user skills are never touched.
|
|
363
|
+
const skillsTargetDir = resolve(destination, scope === "project" ? "skills" : "agent/skills");
|
|
364
|
+
const activeSkillDirs = new Set(skillSources.skillNames.map((n) => `${SKILL_PREFIX}${n}`));
|
|
365
|
+
const orphanSkillDirs = [];
|
|
366
|
+
if (existsSync(skillsTargetDir)) {
|
|
367
|
+
for (const entry of readdirSync(skillsTargetDir, { withFileTypes: true })) {
|
|
368
|
+
if (entry.isDirectory() && entry.name.startsWith(SKILL_PREFIX)) {
|
|
369
|
+
if (!activeSkillDirs.has(entry.name)) {
|
|
370
|
+
orphanSkillDirs.push(entry.name);
|
|
371
|
+
filesRemoved.push(`${scope === "project" ? "skills" : "agent/skills"}/${entry.name}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const retiredById = new Map(
|
|
378
|
+
registry
|
|
379
|
+
.filter((agent) => isRetiredAgentForMode(agent, "pi"))
|
|
380
|
+
.map((agent) => [agent.id, agent]),
|
|
381
|
+
);
|
|
382
|
+
for (const ownership of previousManifest?.agents ?? []) {
|
|
383
|
+
const retired = retiredById.get(ownership.id);
|
|
384
|
+
if (!retired) continue;
|
|
385
|
+
const expectedDestination = agentConfigRelative(retired.id);
|
|
386
|
+
if (ownership.promptDestination !== expectedDestination) continue;
|
|
387
|
+
const installedPath = resolve(destination, ownership.promptDestination);
|
|
388
|
+
if (existsSync(installedPath) && sha256(readFileSync(installedPath)) === ownership.sourceHash) {
|
|
389
|
+
filesRemoved.push(ownership.promptDestination);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Pass 2: Write if not dry-run
|
|
394
|
+
if (!dryRun) {
|
|
395
|
+
for (const { compiledContent, configRelative } of agentSources) {
|
|
396
|
+
const abs = resolve(destination, configRelative);
|
|
397
|
+
ensureParentDir(abs);
|
|
398
|
+
writeFileSync(abs, compiledContent, "utf-8");
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
for (const { absSource, configRelative } of skillSources.files) {
|
|
402
|
+
const abs = resolve(destination, configRelative);
|
|
403
|
+
ensureParentDir(abs);
|
|
404
|
+
writeFileSync(abs, readFileSync(absSource));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
for (const fileToRemove of filesRemoved) {
|
|
408
|
+
const abs = resolve(destination, fileToRemove);
|
|
409
|
+
if (existsSync(abs)) {
|
|
410
|
+
// Could be a file (agent orphan) or directory (skill orphan)
|
|
411
|
+
const isDir = statSync(abs).isDirectory();
|
|
412
|
+
rmSync(abs, { recursive: isDir, force: true });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const installedManifestPath = resolve(destination, manifestConfigRelative());
|
|
417
|
+
ensureParentDir(installedManifestPath);
|
|
418
|
+
writeFileSync(
|
|
419
|
+
installedManifestPath,
|
|
420
|
+
`${JSON.stringify(
|
|
421
|
+
{
|
|
422
|
+
bundleId: "arcs-pi-bundle",
|
|
423
|
+
installMode: `pi-${scope}`,
|
|
424
|
+
sourceBundleVersion: "deploy-script",
|
|
425
|
+
sourceBundleHash: sha256(readFileSync(resolve(bundleRoot, "manifest.json"))),
|
|
426
|
+
installedAt: new Date().toISOString(),
|
|
427
|
+
tierModels,
|
|
428
|
+
ownedPaths: [],
|
|
429
|
+
agents: agentSources.map((agent) => ({
|
|
430
|
+
id: agent.stem,
|
|
431
|
+
promptDestination: agent.configRelative,
|
|
432
|
+
sourceHash: sha256(agent.compiledContent),
|
|
433
|
+
})),
|
|
434
|
+
},
|
|
435
|
+
null,
|
|
436
|
+
2,
|
|
437
|
+
)}\n`,
|
|
438
|
+
"utf-8",
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// codegraph/rtk: neither exposes a pi install target, so nothing to wire.
|
|
443
|
+
const result = {
|
|
444
|
+
dryRun,
|
|
445
|
+
platform: "pi",
|
|
446
|
+
scope,
|
|
447
|
+
source: bundleRoot,
|
|
448
|
+
destination,
|
|
449
|
+
modelConfig: tierModels,
|
|
450
|
+
filesAdded,
|
|
451
|
+
filesChanged,
|
|
452
|
+
filesRemoved,
|
|
453
|
+
filesUnchanged,
|
|
454
|
+
orphanSkillDirs,
|
|
455
|
+
codegraphWired: false,
|
|
456
|
+
rtkWired: false,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
main();
|
|
464
|
+
} catch (error) {
|
|
465
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
466
|
+
process.stderr.write(`${message}\n`);
|
|
467
|
+
process.exitCode = 1;
|
|
468
|
+
}
|
package/scripts/lint-bundle.mjs
CHANGED
|
@@ -77,7 +77,7 @@ function isAgentRegistryRecord(agent) {
|
|
|
77
77
|
["heavy", "standard", "light"].includes(agent.tier) &&
|
|
78
78
|
Array.isArray(agent.modes) &&
|
|
79
79
|
agent.modes.length > 0 &&
|
|
80
|
-
agent.modes.every((mode) => ["opencode", "claudecode"].includes(mode)) &&
|
|
80
|
+
agent.modes.every((mode) => ["opencode", "claudecode", "pi"].includes(mode)) &&
|
|
81
81
|
isPromptPath(agent.source) &&
|
|
82
82
|
isPromptPath(agent.destination) &&
|
|
83
83
|
typeof agent.description === "string" &&
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session-state vocabulary — the ONE derivation both sides of the wire read.
|
|
3
|
-
*
|
|
4
|
-
* A session carries two state fields: `status`, persisted by the store, and
|
|
5
|
-
* `phase`, derived per response and never persisted. The moment two call sites
|
|
6
|
-
* pick between them independently the product starts disagreeing with itself —
|
|
7
|
-
* a table badged `running` under an `active` chip, or a server refusing the
|
|
8
|
-
* resume the client just offered. Everything user-facing and every server gate
|
|
9
|
-
* goes through `sessionState()` here, so there is nothing to keep in sync.
|
|
10
|
-
*
|
|
11
|
-
* The predicates take the RECORD, never a pre-picked string, for the same
|
|
12
|
-
* reason the badge component does: handing the badge a record protects badge
|
|
13
|
-
* call sites only, and a counter or an affordance gate takes no badge prop.
|
|
14
|
-
*
|
|
15
|
-
* ---------------------------------------------------------------------------
|
|
16
|
-
* THIS MODULE MUST HAVE ZERO IMPORTS. Do not add one — not a type-only import,
|
|
17
|
-
* not a `import type { SessionMeta }` for the parameter shapes below.
|
|
18
|
-
*
|
|
19
|
-
* `web/` imports this file directly by relative path (there is no alias and no
|
|
20
|
-
* third package). Its being a LEAF — not the directory it sits in — is the
|
|
21
|
-
* entire guarantee that no server module graph is dragged into the client
|
|
22
|
-
* bundle. One import of anything under `src/utils/` or `src/web-server/` and
|
|
23
|
-
* the CLI's transitive graph starts resolving inside `vite build`.
|
|
24
|
-
*
|
|
25
|
-
* The corollary of that reach: this file is typechecked under BOTH tsconfigs,
|
|
26
|
-
* and `web/tsconfig.json` sets `noUncheckedIndexedAccess` and
|
|
27
|
-
* `verbatimModuleSyntax` where the root tsconfig sets neither. A root-legal
|
|
28
|
-
* edit here can fail `npm --workspace @arcs/web run typecheck`. Run both.
|
|
29
|
-
* ---------------------------------------------------------------------------
|
|
30
|
-
*/
|
|
31
|
-
/**
|
|
32
|
-
* The two fields a session's state is read from — all this module ever needs of
|
|
33
|
-
* a record, so a store record, a reconciled server view and a wire-shaped
|
|
34
|
-
* client record can all be filtered, gated and badged through one derivation.
|
|
35
|
-
*
|
|
36
|
-
* Typed as plain strings rather than the `SessionStatus`/`SessionPhase` literal
|
|
37
|
-
* unions on purpose. Pinning those here would mean either an import (forbidden
|
|
38
|
-
* above) or a third hand-written copy of the wire vocabulary that the server
|
|
39
|
-
* store and `web/src/api/client.ts` already mirror deliberately. What is shared
|
|
40
|
-
* across the boundary is the DERIVATION, not the wire types; every caller keeps
|
|
41
|
-
* passing its own precisely-typed record, which is assignable to this.
|
|
42
|
-
*/
|
|
43
|
-
export interface SessionStateSource {
|
|
44
|
-
status: string;
|
|
45
|
-
phase?: string;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* The state a session is in, as the UI states it everywhere and as the server
|
|
49
|
-
* decides from: the derived phase — the only signal reflecting whether the
|
|
50
|
-
* session is live right now — falling back to the record's own status for a
|
|
51
|
-
* record that reached a decision site without one (the record echoed by
|
|
52
|
-
* `POST /run` carries no phase, and neither does a pre-phase endpoint).
|
|
53
|
-
*
|
|
54
|
-
* This is the single derivation site. `status` is still readable on the row
|
|
55
|
-
* (the status cell's tooltip), but nothing decides anything from it alone.
|
|
56
|
-
*/
|
|
57
|
-
export declare function sessionState(session: SessionStateSource): string;
|
|
58
|
-
/**
|
|
59
|
-
* Is this a session a human could still reach — as the badge beside it reads?
|
|
60
|
-
*
|
|
61
|
-
* What the sessions header counts as "N live". Takes the record so the count is
|
|
62
|
-
* computed from the same field the badge one row below renders, rather than
|
|
63
|
-
* from whichever of the two a call site happened to reach for.
|
|
64
|
-
*
|
|
65
|
-
* This is the SAFE half of the pair: across the reachable pairs above, `status`
|
|
66
|
-
* and `phase` never disagree about liveness. A record stored `active` whose
|
|
67
|
-
* process is gone derives `idle`, and `idle` is live — the record is not over —
|
|
68
|
-
* so the count is right read either way. That is a property of today's
|
|
69
|
-
* derivation, not a guarantee; reading the record keeps the count correct if it
|
|
70
|
-
* stops holding. `isSessionAttached` below is where the two genuinely disagree.
|
|
71
|
-
*/
|
|
72
|
-
export declare function isSessionLive(session: SessionStateSource): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* Is a process driving this session right now?
|
|
75
|
-
*
|
|
76
|
-
* The gate on affordances that need the session's runtime thread free — today
|
|
77
|
-
* the composer's headless resume and the server route behind it, neither of
|
|
78
|
-
* which can attach to a session a terminal is already driving. Reading the
|
|
79
|
-
* persisted status here is wrong in BOTH directions, and both are reachable
|
|
80
|
-
* pairs: a record stored `active` whose process is gone derives `idle` — no
|
|
81
|
-
* fresh evidence of attachment — and would be refused the resume it is the
|
|
82
|
-
* perfect candidate for (`active`/`idle`), while a live `running` session
|
|
83
|
-
* stored `idle` would be offered one that cannot work (`idle`/`running`).
|
|
84
|
-
* `idle` is the state the whole affordance turns on; it is never `ended`, which
|
|
85
|
-
* only a terminal status produces.
|
|
86
|
-
*/
|
|
87
|
-
export declare function isSessionAttached(session: SessionStateSource): boolean;
|
|
88
|
-
//# sourceMappingURL=session-vocabulary.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session-vocabulary.d.ts","sourceRoot":"","sources":["../../src/shared/session-vocabulary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAEhE;AAwCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAElE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAEtE"}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session-state vocabulary — the ONE derivation both sides of the wire read.
|
|
3
|
-
*
|
|
4
|
-
* A session carries two state fields: `status`, persisted by the store, and
|
|
5
|
-
* `phase`, derived per response and never persisted. The moment two call sites
|
|
6
|
-
* pick between them independently the product starts disagreeing with itself —
|
|
7
|
-
* a table badged `running` under an `active` chip, or a server refusing the
|
|
8
|
-
* resume the client just offered. Everything user-facing and every server gate
|
|
9
|
-
* goes through `sessionState()` here, so there is nothing to keep in sync.
|
|
10
|
-
*
|
|
11
|
-
* The predicates take the RECORD, never a pre-picked string, for the same
|
|
12
|
-
* reason the badge component does: handing the badge a record protects badge
|
|
13
|
-
* call sites only, and a counter or an affordance gate takes no badge prop.
|
|
14
|
-
*
|
|
15
|
-
* ---------------------------------------------------------------------------
|
|
16
|
-
* THIS MODULE MUST HAVE ZERO IMPORTS. Do not add one — not a type-only import,
|
|
17
|
-
* not a `import type { SessionMeta }` for the parameter shapes below.
|
|
18
|
-
*
|
|
19
|
-
* `web/` imports this file directly by relative path (there is no alias and no
|
|
20
|
-
* third package). Its being a LEAF — not the directory it sits in — is the
|
|
21
|
-
* entire guarantee that no server module graph is dragged into the client
|
|
22
|
-
* bundle. One import of anything under `src/utils/` or `src/web-server/` and
|
|
23
|
-
* the CLI's transitive graph starts resolving inside `vite build`.
|
|
24
|
-
*
|
|
25
|
-
* The corollary of that reach: this file is typechecked under BOTH tsconfigs,
|
|
26
|
-
* and `web/tsconfig.json` sets `noUncheckedIndexedAccess` and
|
|
27
|
-
* `verbatimModuleSyntax` where the root tsconfig sets neither. A root-legal
|
|
28
|
-
* edit here can fail `npm --workspace @arcs/web run typecheck`. Run both.
|
|
29
|
-
* ---------------------------------------------------------------------------
|
|
30
|
-
*/
|
|
31
|
-
/**
|
|
32
|
-
* The state a session is in, as the UI states it everywhere and as the server
|
|
33
|
-
* decides from: the derived phase — the only signal reflecting whether the
|
|
34
|
-
* session is live right now — falling back to the record's own status for a
|
|
35
|
-
* record that reached a decision site without one (the record echoed by
|
|
36
|
-
* `POST /run` carries no phase, and neither does a pre-phase endpoint).
|
|
37
|
-
*
|
|
38
|
-
* This is the single derivation site. `status` is still readable on the row
|
|
39
|
-
* (the status cell's tooltip), but nothing decides anything from it alone.
|
|
40
|
-
*/
|
|
41
|
-
export function sessionState(session) {
|
|
42
|
-
return session.phase ?? session.status;
|
|
43
|
-
}
|
|
44
|
-
/** States meaning "this session is not over": the live phases, plus the raw
|
|
45
|
-
* `active`/`idle` a record that arrived without a phase is badged with. */
|
|
46
|
-
const LIVE_STATES = new Set(["running", "idle", "active"]);
|
|
47
|
-
/**
|
|
48
|
-
* States meaning "a process is driving this session right now".
|
|
49
|
-
*
|
|
50
|
-
* Deliberately NARROWER than `LIVE_STATES`, and the reason both sets are
|
|
51
|
-
* private to this module rather than living at their call sites: `idle` is live
|
|
52
|
-
* — the record is not over and the session list counts it — but nothing holds
|
|
53
|
-
* its runtime thread, which is exactly the session a headless resume works on.
|
|
54
|
-
* One shared "live" set would silently conflate "not over" with "attached" and
|
|
55
|
-
* take the resume affordance away from every session that can actually use it.
|
|
56
|
-
*
|
|
57
|
-
* REACHABLE PAIRS. The server's derivation (`deriveSessionPhase` →
|
|
58
|
-
* `reconcilePhase` → `runDeadlinePhase`, all composed in `withPhases`) can only
|
|
59
|
-
* ever produce these (`status`, `phase`) pairs, exhaustively:
|
|
60
|
-
*
|
|
61
|
-
* active/idle · active/running · completed/ended · disconnected/ended ·
|
|
62
|
-
* failed/failed · idle/idle · idle/running
|
|
63
|
-
*
|
|
64
|
-
* So `ended` comes ONLY from a terminal status (`completed`/`disconnected`):
|
|
65
|
-
* the derivation returns it before looking at any liveness evidence, the
|
|
66
|
-
* reconciler early-returns on every non-`running` derivation, and both probes
|
|
67
|
-
* can only demote `running` to `idle`. A record stored `active` whose process
|
|
68
|
-
* is gone therefore derives `idle` — "no fresh evidence of attachment" — and
|
|
69
|
-
* never `ended`. Both sit outside this set, so this predicate answers the same
|
|
70
|
-
* either way; the pair that carries the meaning is `active`/`idle`, because
|
|
71
|
-
* that is what "the process is gone" actually looks like on the wire.
|
|
72
|
-
*
|
|
73
|
-
* `active` itself is a persisted status, reachable only through
|
|
74
|
-
* `sessionState`'s fallback for a record handed to a predicate without a
|
|
75
|
-
* derived phase. Server call sites go through `withPhases` first, so the phase
|
|
76
|
-
* decides in practice; the fallback is the conservative answer for anything
|
|
77
|
-
* that does not — treat as attached and refuse, rather than silently accept.
|
|
78
|
-
*/
|
|
79
|
-
const ATTACHED_STATES = new Set(["running", "active"]);
|
|
80
|
-
/**
|
|
81
|
-
* Is this a session a human could still reach — as the badge beside it reads?
|
|
82
|
-
*
|
|
83
|
-
* What the sessions header counts as "N live". Takes the record so the count is
|
|
84
|
-
* computed from the same field the badge one row below renders, rather than
|
|
85
|
-
* from whichever of the two a call site happened to reach for.
|
|
86
|
-
*
|
|
87
|
-
* This is the SAFE half of the pair: across the reachable pairs above, `status`
|
|
88
|
-
* and `phase` never disagree about liveness. A record stored `active` whose
|
|
89
|
-
* process is gone derives `idle`, and `idle` is live — the record is not over —
|
|
90
|
-
* so the count is right read either way. That is a property of today's
|
|
91
|
-
* derivation, not a guarantee; reading the record keeps the count correct if it
|
|
92
|
-
* stops holding. `isSessionAttached` below is where the two genuinely disagree.
|
|
93
|
-
*/
|
|
94
|
-
export function isSessionLive(session) {
|
|
95
|
-
return LIVE_STATES.has(sessionState(session));
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Is a process driving this session right now?
|
|
99
|
-
*
|
|
100
|
-
* The gate on affordances that need the session's runtime thread free — today
|
|
101
|
-
* the composer's headless resume and the server route behind it, neither of
|
|
102
|
-
* which can attach to a session a terminal is already driving. Reading the
|
|
103
|
-
* persisted status here is wrong in BOTH directions, and both are reachable
|
|
104
|
-
* pairs: a record stored `active` whose process is gone derives `idle` — no
|
|
105
|
-
* fresh evidence of attachment — and would be refused the resume it is the
|
|
106
|
-
* perfect candidate for (`active`/`idle`), while a live `running` session
|
|
107
|
-
* stored `idle` would be offered one that cannot work (`idle`/`running`).
|
|
108
|
-
* `idle` is the state the whole affordance turns on; it is never `ended`, which
|
|
109
|
-
* only a terminal status produces.
|
|
110
|
-
*/
|
|
111
|
-
export function isSessionAttached(session) {
|
|
112
|
-
return ATTACHED_STATES.has(sessionState(session));
|
|
113
|
-
}
|
|
114
|
-
//# sourceMappingURL=session-vocabulary.js.map
|