chainlesschain 0.165.3 → 0.165.5
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 +151 -29
- package/package.json +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BiHNUlbU.js → AIOps-CEeYTeDB.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-D-UFjDRQ.js → ActionButton-BeRUQ3V2.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DazVsDgW.js → Analytics-DrV718XF.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DXh9UfNJ.js → AppLayout-Cd2Frt7Q.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CZ6tsceY.js → Artifacts-CUJMlORU.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-RyxnBr_h.js → Audit-CXJcfT6p.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BWS51Q6X.js → BackgroundAgents-DOv6Z7H9.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BRtPcUul.js → Backup-BwdDyBI0.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CGd4vZYY.js → BaseInput-BmLn4kYH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-h8ijVwCg.js → Chat-COg8pAW2.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-NyF6dPl8.js → Checkbox-BCP4uoU8.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Ci4OOxr5.js → Codegen-pRt6Y9JI.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CwTkc9BM.js → Col-DLaprKhS.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DW_tM82m.js → Community-DyATsz5f.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DEvHRV4S.js → Compact-BXAaupZK.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BbjBEEfD.js → Compliance-DpcJBELr.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-10IXBaBa.js → Cowork-DG4EFWyd.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BZkbpd5I.js → Cron-F6Oy5n2P.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CFQ8L_6v.js → Crosschain-BOdgtalX.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BDF6AkXe.js → DID-DT1dSkjH.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CoOdWMi4.js → Dashboard-NBUIqWzC.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DEsGmu8Y.js → Dropdown-CSpHeMYB.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BS89SWlK.js → EmailListRenderer-CEG0xISr.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C1FpCHiI.js → FamilyGuardDashboard-BTZ0BZna.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BzvpVmLA.js → Federation-BuuCTn2V.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BeC259PQ.js → FormItemContext-KAVceevk.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-azgG3bmV.js → GenericCardRenderer-DnHpcQvW.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BmSSowc5.js → Git-C_B_0HwB.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CTBCSyQZ.js → Governance-DPSZuzuu.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DA5xyk4a.js → Inference-BV-06V7q.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-D1gPHNig.js → KnowledgeGraph-wpDuupn6.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BPmLf1AR.js → Logs-Bseca1_I.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-SVLOg49H.js → MarkdownRenderer-B4RLp2zs.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-DtUFcR0E.js → Marketplace-d3kM4DDL.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DS_kqBv-.js → McpTools-DQ_xZ23i.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Fvh7XL7V.js → Memory-CRIeYO4L.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C7itAP45.js → MobileBridge-BsllIa9l.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-BUuQy30X.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B57MiNLK.js → Mtc-BOFDIzrC.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-BXfCUcM7.js → MtcAudit-BQg-vyvG.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-yaJ-ZcF6.js → Multisig-Bo1jrPdN.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CoaUCKrO.js → NLProgramming-Bmod8alw.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BKLM_65R.js → Notes-CGGFL8w-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DoDtK9uZ.js → NotificationSettings-BsZvCJxC.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BJ28TGr-.js → OrderTableRenderer-C1-Quo_h.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-MuGsdoIb.js → Organization-BjOeDYOi.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BLUN9J4R.js → Overflow-DwVV3l5A.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DWuoBUPd.js → P2P-Cu3DQT9W.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CUi37YPj.js → PdhVaultBrowser-Cg0gR7dH.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYQg2pcr.js → Permissions-CsAXxRN5.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CAxoV2xu.js → PersonalDataHub-C-nNmnYp.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-B33wGvIO.js → Pipeline-BOnW7nNc.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-A8yXTORP.js → Privacy-ClLSyGKt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D49I2I1O.js → ProjectInit-DuLvjjq7.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Ci73jJDw.js → ProjectSettings-DQYYbDD9.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CF-yD6Cn.js → Projects-Drw8MBGK.js} +1 -1
- package/src/assets/web-panel/assets/Providers-ckqHQe6K.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Brza0ykJ.js → QrScannerModal-DW5BXk-G.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DFydhjxL.js → QuickAsk-DDLnhNTp.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DLF8_NhA.js → Recommend-PJQ2C91U.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-dyuStW3x.js → RemoteSession-s6wmda34.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-5F3tA6yw.js → Reputation-DC2sb-I3.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Ca2A57S5.js → Row-hPGk1eGj.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B0bxBkq_.js → RssFeed-RIvfZql0.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CFNvHalZ.js → Search-DFg1G1eR.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DSoH3Hav.js → Security-D6H5p8Dp.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHpfEmBQ.js → Services-nxYiFV_w.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Dsh-QNmn.js → Skeleton-BmgFNAdB.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BhT1NkSU.js → Skills-CsaaD2av.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DY9snPeJ.js → Sla-yM5y9yvK.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CWaX0_Cx.js → SpeechSettings-DPy1XMYh.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B690y_TC.js → SyncSettings-CFVhEyyr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-2N-w3zWd.js → Tasks-DdO02Inn.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BjwQOQYC.js → Templates-Eo2d8Llu.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CUqHErxY.js → Tenant-ChESOXgy.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-C13rXvNy.js → Terminal-BJ7oDxTI.js} +2 -2
- package/src/assets/web-panel/assets/TimelineRenderer-hWX0fUvz.js +1 -0
- package/src/assets/web-panel/assets/{Tokens-BXHLGXYC.js → Tokens-DVBZQnd-.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-1e5i65o3.js → Trigger-D89cQfVq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-FLGqNHs5.js → Trust-BIVSb4-V.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BbaR_LJB.js → UkeySign-B58gWopl.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-NOp4RIov.js → VideoEditing-DhHI7bmG.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B7DW7bG4.js → Wallet-C4FheWS_.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-BptSUWix.js → WebAuthn-DnPRq-Bx.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CAJnDOXS.js → WorkflowEditor-BerRPjpJ.js} +1 -1
- package/src/assets/web-panel/assets/{chat-I8LgG1-Z.js → chat-BzZ52iKr.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CWe7SOVD.js → colors-BI_E4BAn.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cx3u3CA3.js → compact-item-d50mr3yk.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CgD5ArwY.js → createContext-C6FsQ1IH.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CagEcIex.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BfQWiXN8.js → hasIn-DLZEMgLP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuEQSorC.js → index-9H_M2jju.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiI-xPRH.js → index-A5CDWP7y.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAVy98u7.js → index-AHr7ryWf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUAR7RY-.js → index-B81mo1Et.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlIyt1qF.js → index-BAE3hwev.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dcdm_Z2c.js → index-BB9SqVSt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWZ3REg6.js → index-BRGx3w6T.js} +3 -3
- package/src/assets/web-panel/assets/{index-B6yChzt1.js → index-BTpIOTfR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeizoQVZ.js → index-BZjjdQ62.js} +1 -1
- package/src/assets/web-panel/assets/{index-e8BM1yY1.js → index-BcSUl9fA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNYFQxAW.js → index-BeM4hfht.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGMYTN5W.js → index-Bmr_vxXD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BREnPKGo.js → index-BoNZlzhQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1cX4Rxu.js → index-C3k8kevG.js} +1 -1
- package/src/assets/web-panel/assets/{index-3iKP9OTJ.js → index-CLoTQr0W.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1UjHdgZ.js → index-CO8DAF7U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy_cCJlr.js → index-CRN5TRTQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0nDKhWv.js → index-CUaEHfQ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DslTuqro.js → index-CVzuawCX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAUnl4Jv.js → index-CdKt9fOd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMu16vPC.js → index-CgkoH3_g.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-rIKccl.js → index-CjuKZBqJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bx7BNIRH.js → index-Co7s9LsB.js} +1 -1
- package/src/assets/web-panel/assets/{index-Doc0Ftpd.js → index-D-aBumtY.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd79NQAx.js → index-DE0V6cp-.js} +1 -1
- package/src/assets/web-panel/assets/{index-V-kIHzAV.js → index-DY4j5zkF.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi24S7Dw.js → index-Dec0Kf_Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCJEY2T0.js → index-DoFtCVZ7.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0ztiqtB.js → index-DqkbHHg8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcX4FSPf.js → index-DsscwxPE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdRkCwm4.js → index-GXE0D8Nx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmgtY58H.js → index-IPkwSAW_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQDKYbnv.js → index-KNGgO4Wm.js} +1 -1
- package/src/assets/web-panel/assets/{index-87gMxqhN.js → index-Pvf30m8Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmrdUwzU.js → index-Q5Tn735s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFb-XIKy.js → index-TWG-_D1M.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsQcURkA.js → index-Y2-q7QLm.js} +1 -1
- package/src/assets/web-panel/assets/index-pzT9PpOU.js +1 -0
- package/src/assets/web-panel/assets/index-z1kFadaX.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-DiBszVdG.js → initDefaultProps-D-4Hzgej.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BGIFFozv.js → motion-D4Wl4xU7.js} +1 -1
- package/src/assets/web-panel/assets/{move-D5W7mp7B.js → move-Cd5eQvL0.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-JFIlxJ7F.js → mtc-parser-B4ilslCW.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CQ3o6LIU.js → omit-Ckarx-tf.js} +1 -1
- package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
- package/src/assets/web-panel/assets/{pickAttrs-R8MIlXo3.js → pickAttrs-QJ0bpCmc.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-sW7ylSky.js → placementArrow-CKHm-tq8.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Cnl39Vbz.js → responsiveObserve-CV6a_w_Y.js} +1 -1
- package/src/assets/web-panel/assets/{slide-sn8tFd14.js → slide-mHpeQFgk.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-IKKY_QxZ.js → statusUtils-DIaQNQWA.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BNpV4Nqs.js → styleChecker-PJpzavV8.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BWyT_eoU.js → useFlexGapSupport-BH7zTw8S.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-D09p6_U9.js → useFs-BDNG3JDd.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CzW3TkJZ.js → usePersonalDataHub-CY2VWdUg.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-3s3WoAKB.js → vnode-D0kw9jhz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjy_673s.js → zoom-B5g7TN7r.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +105 -35
- package/src/commands/artifacts.js +598 -33
- package/src/commands/cowork.js +200 -5
- package/src/commands/session-budget.js +155 -0
- package/src/commands/session-location.js +968 -1
- package/src/commands/session.js +23 -2
- package/src/data/changelog.json +28 -2
- package/src/gateways/ws/artifact-protocol.js +77 -13
- package/src/gateways/ws/message-dispatcher.js +57 -0
- package/src/gateways/ws/session-protocol.js +391 -24
- package/src/gateways/ws/ws-agent-handler.js +191 -14
- package/src/gateways/ws/ws-server.js +14 -3
- package/src/gateways/ws/ws-session-gateway.js +101 -5
- package/src/harness/jsonl-session-store.js +1210 -0
- package/src/lib/agent-team/team-runner.js +43 -13
- package/src/lib/artifact-access-ledger.js +462 -0
- package/src/lib/artifact-cleanup-ledger.js +861 -0
- package/src/lib/artifact-deletion-ledger.js +678 -0
- package/src/lib/artifact-recovery.js +970 -0
- package/src/lib/artifact-store.js +256 -51
- package/src/lib/artifact-workbench-projection.js +171 -0
- package/src/lib/command-lifecycle-report.js +8 -4
- package/src/lib/cowork-adapter.js +9 -5
- package/src/lib/cowork-task-runner.js +20 -0
- package/src/lib/cowork-workflow.js +178 -20
- package/src/lib/direct-model-usage.js +62 -22
- package/src/lib/dynamic-workflow-runtime.js +3592 -112
- package/src/lib/execution-location-result-apply.js +339 -0
- package/src/lib/execution-location-result-artifact.js +271 -0
- package/src/lib/execution-location-result-review.js +253 -0
- package/src/lib/execution-location-result-store.js +331 -0
- package/src/lib/execution-location-result.js +617 -0
- package/src/lib/execution-location-target.js +240 -3
- package/src/lib/managed-tool-checkpoint.js +47 -4
- package/src/lib/mcp-stdio-executable-identity.js +16 -0
- package/src/lib/mcp-stdio-native-code-policy.js +74 -0
- package/src/lib/mcp-stdio-package-materialization.js +12 -3
- package/src/lib/process-execution-broker/index.js +112 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
- package/src/lib/process-execution-broker/workspace-transaction.js +29 -0
- package/src/lib/session-budget-production-root.js +376 -0
- package/src/lib/session-budget-runtime.js +109 -18
- package/src/lib/session-budget-usage.js +63 -0
- package/src/lib/session-host-snapshot.js +19 -2
- package/src/lib/session-projection.js +126 -10
- package/src/lib/session-resource-budget.js +766 -105
- package/src/lib/sub-agent-context.js +69 -22
- package/src/lib/web-ui-server.js +12 -1
- package/src/repl/agent-repl.js +237 -16
- package/src/runtime/agent-core.js +135 -0
- package/src/runtime/headless-runner.js +135 -46
- package/src/runtime/headless-stream.js +133 -25
- package/src/runtime/policies/agent-policy.js +10 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-D7tcdDS9.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DGbP36-3.js +0 -1
- package/src/assets/web-panel/assets/Providers-D-etEhg5.js +0 -1
- package/src/assets/web-panel/assets/TimelineRenderer-CpbWcya_.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DLv_872g.js +0 -1
- package/src/assets/web-panel/assets/index-CdiGCibe.js +0 -1
- package/src/assets/web-panel/assets/index-D_0z-gyz.js +0 -1
- package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
|
@@ -24,6 +24,7 @@ import fs from "fs";
|
|
|
24
24
|
import os from "os";
|
|
25
25
|
import path from "path";
|
|
26
26
|
import crypto from "crypto";
|
|
27
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
27
28
|
|
|
28
29
|
export const ARTIFACT_KINDS = Object.freeze([
|
|
29
30
|
"report",
|
|
@@ -62,6 +63,7 @@ export function publicArtifactMetadata(entry) {
|
|
|
62
63
|
expiresAt,
|
|
63
64
|
immutable,
|
|
64
65
|
recordDigest,
|
|
66
|
+
lineage,
|
|
65
67
|
} = entry;
|
|
66
68
|
return {
|
|
67
69
|
id,
|
|
@@ -76,6 +78,7 @@ export function publicArtifactMetadata(entry) {
|
|
|
76
78
|
expiresAt,
|
|
77
79
|
immutable: immutable === true,
|
|
78
80
|
recordDigest: recordDigest || null,
|
|
81
|
+
...(lineage ? { lineage } : {}),
|
|
79
82
|
};
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -120,10 +123,36 @@ function normalizeKind(kind) {
|
|
|
120
123
|
return ARTIFACT_KINDS.includes(k) ? k : "other";
|
|
121
124
|
}
|
|
122
125
|
|
|
126
|
+
function normalizeLineage(lineage) {
|
|
127
|
+
if (lineage == null) return null;
|
|
128
|
+
if (typeof lineage !== "object" || Array.isArray(lineage)) {
|
|
129
|
+
throw new Error("artifact lineage must be an object");
|
|
130
|
+
}
|
|
131
|
+
let normalized;
|
|
132
|
+
try {
|
|
133
|
+
normalized = JSON.parse(JSON.stringify(lineage));
|
|
134
|
+
} catch {
|
|
135
|
+
throw new Error("artifact lineage must be JSON serializable");
|
|
136
|
+
}
|
|
137
|
+
if (
|
|
138
|
+
!normalized ||
|
|
139
|
+
typeof normalized !== "object" ||
|
|
140
|
+
Array.isArray(normalized)
|
|
141
|
+
) {
|
|
142
|
+
throw new Error("artifact lineage must be a JSON object");
|
|
143
|
+
}
|
|
144
|
+
return normalized;
|
|
145
|
+
}
|
|
146
|
+
|
|
123
147
|
export class ArtifactStore {
|
|
124
|
-
constructor({
|
|
148
|
+
constructor({
|
|
149
|
+
dir = null,
|
|
150
|
+
now = () => Date.now(),
|
|
151
|
+
indexLock = withFileLock,
|
|
152
|
+
} = {}) {
|
|
125
153
|
this.dir = dir || artifactsDir();
|
|
126
154
|
this._now = typeof now === "function" ? now : () => now;
|
|
155
|
+
this._indexLock = indexLock;
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
_indexFile() {
|
|
@@ -138,6 +167,31 @@ export class ArtifactStore {
|
|
|
138
167
|
fs.mkdirSync(this._filesDir(), { recursive: true });
|
|
139
168
|
}
|
|
140
169
|
|
|
170
|
+
_withIndexLock(callback) {
|
|
171
|
+
this._ensureDirs();
|
|
172
|
+
return this._indexLock(this._indexFile(), callback, {
|
|
173
|
+
failIfUnavailable: true,
|
|
174
|
+
timeoutMs: 30_000,
|
|
175
|
+
retryMs: 1,
|
|
176
|
+
maxRetryMs: 8,
|
|
177
|
+
retryJitterMs: 4,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Run a read-only callback against one locked index generation. The callback
|
|
183
|
+
* may inspect stored bytes before it returns; all ArtifactStore mutations use
|
|
184
|
+
* this same lock and therefore cannot replace that generation mid-read.
|
|
185
|
+
*/
|
|
186
|
+
withIndexSnapshot(callback) {
|
|
187
|
+
if (typeof callback !== "function") {
|
|
188
|
+
throw new TypeError("artifact index snapshot requires a callback");
|
|
189
|
+
}
|
|
190
|
+
return this._withIndexLock(() =>
|
|
191
|
+
callback(Object.freeze(this._readEntries())),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
141
195
|
/**
|
|
142
196
|
* Publish a file as an artifact (copy + metadata row).
|
|
143
197
|
* @param {{ filePath: string, title?: string, kind?: string,
|
|
@@ -162,14 +216,16 @@ export class ArtifactStore {
|
|
|
162
216
|
);
|
|
163
217
|
}
|
|
164
218
|
const body = fs.readFileSync(abs);
|
|
165
|
-
return this.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
219
|
+
return this._withIndexLock(() =>
|
|
220
|
+
this._publishDataUnlocked(body, {
|
|
221
|
+
fileName: path.basename(abs),
|
|
222
|
+
title,
|
|
223
|
+
kind,
|
|
224
|
+
sessionId,
|
|
225
|
+
ttlDays,
|
|
226
|
+
sourcePath: abs,
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
173
229
|
}
|
|
174
230
|
|
|
175
231
|
/**
|
|
@@ -191,20 +247,78 @@ export class ArtifactStore {
|
|
|
191
247
|
if (typeof data !== "string" && !Buffer.isBuffer(data)) {
|
|
192
248
|
throw new Error("publishData requires string or Buffer data");
|
|
193
249
|
}
|
|
194
|
-
return this.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
250
|
+
return this._withIndexLock(() =>
|
|
251
|
+
this._publishDataUnlocked(Buffer.from(data), {
|
|
252
|
+
fileName,
|
|
253
|
+
title,
|
|
254
|
+
kind,
|
|
255
|
+
mime,
|
|
256
|
+
sessionId,
|
|
257
|
+
ttlDays,
|
|
258
|
+
immutable,
|
|
259
|
+
recordDigest,
|
|
260
|
+
sourcePath: null,
|
|
261
|
+
}),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Publish generated bytes once for a stable authority digest.
|
|
267
|
+
*
|
|
268
|
+
* The strict cross-process index lock makes response-loss retries return the
|
|
269
|
+
* same metadata row instead of creating duplicate managed artifacts. The
|
|
270
|
+
* caller still owns the semantic validation of the persisted lineage and
|
|
271
|
+
* must verifyIntegrity() before treating the returned entry as readable.
|
|
272
|
+
*/
|
|
273
|
+
publishDataOnce({
|
|
274
|
+
data,
|
|
275
|
+
fileName = "artifact.bin",
|
|
276
|
+
title,
|
|
277
|
+
kind,
|
|
278
|
+
mime,
|
|
279
|
+
sessionId = null,
|
|
280
|
+
ttlDays,
|
|
281
|
+
immutable = false,
|
|
282
|
+
recordDigest,
|
|
283
|
+
lineage,
|
|
284
|
+
} = {}) {
|
|
285
|
+
if (typeof data !== "string" && !Buffer.isBuffer(data)) {
|
|
286
|
+
throw new Error("publishDataOnce requires string or Buffer data");
|
|
287
|
+
}
|
|
288
|
+
const stableDigest = String(recordDigest || "");
|
|
289
|
+
if (!/^sha256:[a-f0-9]{64}$/u.test(stableDigest)) {
|
|
290
|
+
throw new Error("publishDataOnce requires a canonical recordDigest");
|
|
291
|
+
}
|
|
292
|
+
const normalizedLineage = normalizeLineage(lineage);
|
|
293
|
+
return this._withIndexLock(() => {
|
|
294
|
+
const matches = this._readEntries().filter(
|
|
295
|
+
(entry) => entry.recordDigest === stableDigest,
|
|
296
|
+
);
|
|
297
|
+
if (matches.length > 1) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`artifact authority ${stableDigest} has duplicate index rows`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (matches.length === 1) {
|
|
303
|
+
return Object.freeze({ entry: matches[0], published: false });
|
|
304
|
+
}
|
|
305
|
+
const entry = this._publishDataUnlocked(Buffer.from(data), {
|
|
306
|
+
fileName,
|
|
307
|
+
title,
|
|
308
|
+
kind,
|
|
309
|
+
mime,
|
|
310
|
+
sessionId,
|
|
311
|
+
ttlDays,
|
|
312
|
+
immutable,
|
|
313
|
+
recordDigest: stableDigest,
|
|
314
|
+
lineage: normalizedLineage,
|
|
315
|
+
sourcePath: null,
|
|
316
|
+
});
|
|
317
|
+
return Object.freeze({ entry, published: true });
|
|
204
318
|
});
|
|
205
319
|
}
|
|
206
320
|
|
|
207
|
-
|
|
321
|
+
_publishDataUnlocked(
|
|
208
322
|
body,
|
|
209
323
|
{
|
|
210
324
|
fileName,
|
|
@@ -215,6 +329,7 @@ export class ArtifactStore {
|
|
|
215
329
|
ttlDays,
|
|
216
330
|
immutable = false,
|
|
217
331
|
recordDigest = null,
|
|
332
|
+
lineage = null,
|
|
218
333
|
sourcePath = null,
|
|
219
334
|
},
|
|
220
335
|
) {
|
|
@@ -247,10 +362,15 @@ export class ArtifactStore {
|
|
|
247
362
|
expiresAt: new Date(createdAt + days * 24 * 60 * 60 * 1000).toISOString(),
|
|
248
363
|
immutable: immutable === true,
|
|
249
364
|
recordDigest: recordDigest ? String(recordDigest) : null,
|
|
365
|
+
...(lineage ? { lineage: normalizeLineage(lineage) } : {}),
|
|
250
366
|
};
|
|
251
367
|
this._ensureDirs();
|
|
252
368
|
const storedPath = path.join(this._filesDir(), entry.file);
|
|
253
|
-
fs.writeFileSync(storedPath, body, {
|
|
369
|
+
fs.writeFileSync(storedPath, body, {
|
|
370
|
+
flag: "wx",
|
|
371
|
+
mode: 0o600,
|
|
372
|
+
flush: true,
|
|
373
|
+
});
|
|
254
374
|
if (entry.immutable) {
|
|
255
375
|
try {
|
|
256
376
|
fs.chmodSync(storedPath, 0o444);
|
|
@@ -259,12 +379,37 @@ export class ArtifactStore {
|
|
|
259
379
|
// support; immutable here means append-only/no update API, not WORM.
|
|
260
380
|
}
|
|
261
381
|
}
|
|
262
|
-
|
|
382
|
+
let descriptor = null;
|
|
383
|
+
try {
|
|
384
|
+
descriptor = fs.openSync(
|
|
385
|
+
this._indexFile(),
|
|
386
|
+
fs.constants.O_WRONLY |
|
|
387
|
+
fs.constants.O_APPEND |
|
|
388
|
+
fs.constants.O_CREAT |
|
|
389
|
+
Number(fs.constants.O_NOFOLLOW || 0),
|
|
390
|
+
0o600,
|
|
391
|
+
);
|
|
392
|
+
const indexStat = fs.fstatSync(descriptor);
|
|
393
|
+
if (!indexStat.isFile() || Number(indexStat.nlink) !== 1) {
|
|
394
|
+
throw new Error("artifact index identity is invalid");
|
|
395
|
+
}
|
|
396
|
+
fs.writeFileSync(descriptor, `${JSON.stringify(entry)}\n`, "utf-8");
|
|
397
|
+
fs.fsyncSync(descriptor);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
try {
|
|
400
|
+
this._deleteStoredFile(entry);
|
|
401
|
+
} catch {
|
|
402
|
+
/* best-effort rollback of an unpublished stored copy */
|
|
403
|
+
}
|
|
404
|
+
throw error;
|
|
405
|
+
} finally {
|
|
406
|
+
if (descriptor !== null) fs.closeSync(descriptor);
|
|
407
|
+
}
|
|
263
408
|
return entry;
|
|
264
409
|
}
|
|
265
410
|
|
|
266
411
|
/** All (non-corrupt) metadata rows, oldest first. */
|
|
267
|
-
|
|
412
|
+
_readEntries({ sessionId } = {}) {
|
|
268
413
|
let raw;
|
|
269
414
|
try {
|
|
270
415
|
raw = fs.readFileSync(this._indexFile(), "utf-8");
|
|
@@ -285,6 +430,10 @@ export class ArtifactStore {
|
|
|
285
430
|
return sessionId ? out.filter((e) => e.sessionId === sessionId) : out;
|
|
286
431
|
}
|
|
287
432
|
|
|
433
|
+
list(options = {}) {
|
|
434
|
+
return this._withIndexLock(() => this._readEntries(options));
|
|
435
|
+
}
|
|
436
|
+
|
|
288
437
|
/** One entry by id (or null). */
|
|
289
438
|
get(id) {
|
|
290
439
|
return this.list().find((e) => e.id === id) || null;
|
|
@@ -348,47 +497,103 @@ export class ArtifactStore {
|
|
|
348
497
|
|
|
349
498
|
/** Remove one artifact (file + index row). @returns {boolean} found */
|
|
350
499
|
remove(id) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
500
|
+
return this._withIndexLock(() => {
|
|
501
|
+
const entries = this._readEntries();
|
|
502
|
+
const matches = entries.filter((entry) => entry.id === id);
|
|
503
|
+
if (matches.length === 0) return false;
|
|
504
|
+
if (matches.length > 1) {
|
|
505
|
+
throw new Error(`artifact id ${id} has duplicate index rows`);
|
|
506
|
+
}
|
|
507
|
+
const target = matches[0];
|
|
508
|
+
this._rewriteUnlocked(entries.filter((entry) => entry.id !== id));
|
|
509
|
+
try {
|
|
510
|
+
this._deleteStoredFile(target);
|
|
511
|
+
} catch {
|
|
512
|
+
/* best-effort — the locked index generation is the source of truth */
|
|
513
|
+
}
|
|
514
|
+
return true;
|
|
515
|
+
});
|
|
361
516
|
}
|
|
362
517
|
|
|
363
518
|
/**
|
|
364
|
-
*
|
|
519
|
+
* Low-level legacy primitive for removing expired artifacts.
|
|
520
|
+
* Official CLI and WebSocket callers must use artifact cleanup settlement so
|
|
521
|
+
* retries preserve a frozen scope and leave content-free audit evidence.
|
|
365
522
|
* @returns {{ removed: number }}
|
|
366
523
|
*/
|
|
367
524
|
cleanupExpired() {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
525
|
+
return this._withIndexLock(() => {
|
|
526
|
+
const entries = this._readEntries();
|
|
527
|
+
const now = this._now();
|
|
528
|
+
const keep = [];
|
|
529
|
+
const expired = [];
|
|
530
|
+
for (const entry of entries) {
|
|
531
|
+
const exp = Date.parse(entry.expiresAt || "");
|
|
532
|
+
if (Number.isFinite(exp) && exp <= now) {
|
|
533
|
+
expired.push(entry);
|
|
534
|
+
} else {
|
|
535
|
+
keep.push(entry);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (expired.length > 0) this._rewriteUnlocked(keep);
|
|
539
|
+
for (const entry of expired) {
|
|
375
540
|
try {
|
|
376
|
-
this._deleteStoredFile(
|
|
541
|
+
this._deleteStoredFile(entry);
|
|
377
542
|
} catch {
|
|
378
|
-
/* best-effort */
|
|
543
|
+
/* best-effort — the locked index generation is the source of truth */
|
|
379
544
|
}
|
|
380
|
-
removed += 1;
|
|
381
|
-
} else {
|
|
382
|
-
keep.push(e);
|
|
383
545
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return { removed };
|
|
546
|
+
return { removed: expired.length };
|
|
547
|
+
});
|
|
387
548
|
}
|
|
388
549
|
|
|
389
|
-
|
|
550
|
+
_rewriteUnlocked(entries) {
|
|
390
551
|
this._ensureDirs();
|
|
391
552
|
const text = entries.map((e) => JSON.stringify(e)).join("\n");
|
|
392
|
-
|
|
553
|
+
const indexFile = this._indexFile();
|
|
554
|
+
const temporary = `${indexFile}.${process.pid}.${crypto
|
|
555
|
+
.randomBytes(8)
|
|
556
|
+
.toString("hex")}.tmp`;
|
|
557
|
+
let descriptor = null;
|
|
558
|
+
try {
|
|
559
|
+
descriptor = fs.openSync(
|
|
560
|
+
temporary,
|
|
561
|
+
fs.constants.O_WRONLY |
|
|
562
|
+
fs.constants.O_CREAT |
|
|
563
|
+
fs.constants.O_EXCL |
|
|
564
|
+
Number(fs.constants.O_NOFOLLOW || 0),
|
|
565
|
+
0o600,
|
|
566
|
+
);
|
|
567
|
+
fs.writeFileSync(descriptor, text ? `${text}\n` : "", "utf-8");
|
|
568
|
+
fs.fsyncSync(descriptor);
|
|
569
|
+
fs.closeSync(descriptor);
|
|
570
|
+
descriptor = null;
|
|
571
|
+
fs.renameSync(temporary, indexFile);
|
|
572
|
+
this._syncIndexDirectory();
|
|
573
|
+
} finally {
|
|
574
|
+
if (descriptor !== null) fs.closeSync(descriptor);
|
|
575
|
+
try {
|
|
576
|
+
fs.rmSync(temporary, { force: true });
|
|
577
|
+
} catch {
|
|
578
|
+
/* best-effort cleanup of an unpublished index generation */
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
_syncIndexDirectory() {
|
|
584
|
+
let descriptor = null;
|
|
585
|
+
try {
|
|
586
|
+
descriptor = fs.openSync(this.dir, "r");
|
|
587
|
+
fs.fsyncSync(descriptor);
|
|
588
|
+
} catch (error) {
|
|
589
|
+
if (
|
|
590
|
+
process.platform !== "win32" ||
|
|
591
|
+
!["EACCES", "EINVAL", "ENOTSUP", "EPERM"].includes(error?.code)
|
|
592
|
+
) {
|
|
593
|
+
throw error;
|
|
594
|
+
}
|
|
595
|
+
} finally {
|
|
596
|
+
if (descriptor !== null) fs.closeSync(descriptor);
|
|
597
|
+
}
|
|
393
598
|
}
|
|
394
599
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { publicArtifactMetadata } from "./artifact-store.js";
|
|
2
|
+
import { readArtifactAccessLedger } from "./artifact-access-ledger.js";
|
|
3
|
+
import { readArtifactCleanupLedger } from "./artifact-cleanup-ledger.js";
|
|
4
|
+
import { readArtifactDeletionLedger } from "./artifact-deletion-ledger.js";
|
|
5
|
+
import {
|
|
6
|
+
buildArtifactRecoveryPlan,
|
|
7
|
+
readArtifactOrphanGcLedger,
|
|
8
|
+
} from "./artifact-recovery.js";
|
|
9
|
+
|
|
10
|
+
export const ARTIFACT_WORKBENCH_PROJECTION_SCHEMA = "cc-artifact-workbench/v1";
|
|
11
|
+
export const MAX_ARTIFACT_WORKBENCH_ACTIVITY = 200;
|
|
12
|
+
|
|
13
|
+
function returnedResultLineage(lineage) {
|
|
14
|
+
if (lineage?.schema !== "cc-execution-location-result-artifact-lineage/v1") {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const fields = [
|
|
18
|
+
"sessionId",
|
|
19
|
+
"requestId",
|
|
20
|
+
"reviewDigest",
|
|
21
|
+
"item",
|
|
22
|
+
"kind",
|
|
23
|
+
"mediaType",
|
|
24
|
+
"byteLength",
|
|
25
|
+
"sourceDigest",
|
|
26
|
+
];
|
|
27
|
+
if (fields.some((field) => lineage[field] == null)) return null;
|
|
28
|
+
return Object.freeze(
|
|
29
|
+
Object.fromEntries(fields.map((field) => [field, lineage[field]])),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function accessActivity(event) {
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
type: "access",
|
|
36
|
+
occurredAt: event.authorizedAt,
|
|
37
|
+
artifactId: event.artifactId,
|
|
38
|
+
artifactSessionId: event.artifactSessionId,
|
|
39
|
+
recordDigest: event.recordDigest,
|
|
40
|
+
action: event.action,
|
|
41
|
+
client: event.client,
|
|
42
|
+
settlementId: event.accessId,
|
|
43
|
+
eventDigest: event.eventDigest,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function deletionActivity(event) {
|
|
48
|
+
return Object.freeze({
|
|
49
|
+
type: "deletion",
|
|
50
|
+
phase: event.phase,
|
|
51
|
+
occurredAt: event.occurredAt,
|
|
52
|
+
artifactId: event.artifactId,
|
|
53
|
+
artifactSessionId: event.artifactSessionId,
|
|
54
|
+
recordDigest: event.recordDigest,
|
|
55
|
+
action: event.reason,
|
|
56
|
+
client: event.client,
|
|
57
|
+
settlementId: event.deletionId,
|
|
58
|
+
eventDigest: event.eventDigest,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function cleanupActivity(event) {
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
type: "cleanup",
|
|
65
|
+
phase: event.phase,
|
|
66
|
+
occurredAt: event.occurredAt,
|
|
67
|
+
artifactId: null,
|
|
68
|
+
artifactSessionId: null,
|
|
69
|
+
recordDigest: null,
|
|
70
|
+
action: "expired",
|
|
71
|
+
client: event.client,
|
|
72
|
+
settlementId: event.cleanupId,
|
|
73
|
+
itemCount: event.itemCount,
|
|
74
|
+
eventDigest: event.eventDigest,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function orphanGcActivity(event) {
|
|
79
|
+
return Object.freeze({
|
|
80
|
+
type: "orphan-gc",
|
|
81
|
+
phase: event.phase,
|
|
82
|
+
occurredAt: event.occurredAt,
|
|
83
|
+
artifactId: null,
|
|
84
|
+
artifactSessionId: null,
|
|
85
|
+
recordDigest: null,
|
|
86
|
+
action: event.decision,
|
|
87
|
+
client: "administrator",
|
|
88
|
+
settlementId: event.adjudicationId,
|
|
89
|
+
outcome: event.outcome,
|
|
90
|
+
eventDigest: event.eventDigest,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function occurredAtMs(event) {
|
|
95
|
+
const parsed = Date.parse(event.occurredAt || "");
|
|
96
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function buildArtifactWorkbenchProjection(store, options = {}) {
|
|
100
|
+
if (!store?.dir) {
|
|
101
|
+
throw new TypeError("artifact workbench requires an ArtifactStore");
|
|
102
|
+
}
|
|
103
|
+
const recovery = (
|
|
104
|
+
options.buildArtifactRecoveryPlan || buildArtifactRecoveryPlan
|
|
105
|
+
)(store, options.recoveryOptions || {});
|
|
106
|
+
const access = (options.readArtifactAccessLedger || readArtifactAccessLedger)(
|
|
107
|
+
store,
|
|
108
|
+
options.accessOptions || {},
|
|
109
|
+
);
|
|
110
|
+
const deletion = (
|
|
111
|
+
options.readArtifactDeletionLedger || readArtifactDeletionLedger
|
|
112
|
+
)(store, options.deletionOptions || {});
|
|
113
|
+
const cleanup = (
|
|
114
|
+
options.readArtifactCleanupLedger || readArtifactCleanupLedger
|
|
115
|
+
)(store, options.cleanupOptions || {});
|
|
116
|
+
const orphanGc = (
|
|
117
|
+
options.readArtifactOrphanGcLedger || readArtifactOrphanGcLedger
|
|
118
|
+
)(store, options.orphanGcOptions || {});
|
|
119
|
+
const accessByArtifact = new Map();
|
|
120
|
+
for (const event of access.events) {
|
|
121
|
+
const events = accessByArtifact.get(event.artifactId) || [];
|
|
122
|
+
events.push(event);
|
|
123
|
+
accessByArtifact.set(event.artifactId, events);
|
|
124
|
+
}
|
|
125
|
+
const artifacts = store.list().map((entry) => {
|
|
126
|
+
const metadata = publicArtifactMetadata(entry);
|
|
127
|
+
const accesses = accessByArtifact.get(entry.id) || [];
|
|
128
|
+
const latestAccess = accesses.at(-1) || null;
|
|
129
|
+
return Object.freeze({
|
|
130
|
+
...metadata,
|
|
131
|
+
returnedResult: returnedResultLineage(metadata.lineage),
|
|
132
|
+
history: Object.freeze({
|
|
133
|
+
accessCount: accesses.length,
|
|
134
|
+
latestAccess: latestAccess
|
|
135
|
+
? Object.freeze({
|
|
136
|
+
action: latestAccess.action,
|
|
137
|
+
client: latestAccess.client,
|
|
138
|
+
authorizedAt: latestAccess.authorizedAt,
|
|
139
|
+
eventDigest: latestAccess.eventDigest,
|
|
140
|
+
})
|
|
141
|
+
: null,
|
|
142
|
+
}),
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
const allActivity = [
|
|
146
|
+
...access.events.map(accessActivity),
|
|
147
|
+
...deletion.events.map(deletionActivity),
|
|
148
|
+
...cleanup.events.map(cleanupActivity),
|
|
149
|
+
...orphanGc.events.map(orphanGcActivity),
|
|
150
|
+
].sort((left, right) => {
|
|
151
|
+
const time = occurredAtMs(right) - occurredAtMs(left);
|
|
152
|
+
return time || right.eventDigest.localeCompare(left.eventDigest);
|
|
153
|
+
});
|
|
154
|
+
const activity = allActivity.slice(0, MAX_ARTIFACT_WORKBENCH_ACTIVITY);
|
|
155
|
+
return Object.freeze({
|
|
156
|
+
schema: ARTIFACT_WORKBENCH_PROJECTION_SCHEMA,
|
|
157
|
+
observedAt: recovery.observedAt,
|
|
158
|
+
artifacts: Object.freeze(artifacts),
|
|
159
|
+
recovery,
|
|
160
|
+
history: Object.freeze({
|
|
161
|
+
accessHeadDigest: access.headDigest,
|
|
162
|
+
deletionHeadDigest: deletion.headDigest,
|
|
163
|
+
cleanupHeadDigest: cleanup.headDigest,
|
|
164
|
+
orphanGcHeadDigest: orphanGc.headDigest,
|
|
165
|
+
totalEventCount: allActivity.length,
|
|
166
|
+
returnedEventCount: activity.length,
|
|
167
|
+
truncated: allActivity.length > activity.length,
|
|
168
|
+
activity: Object.freeze(activity),
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -38,12 +38,16 @@ export const COMMAND_ALIAS_DECISION_POLICY = Object.freeze({
|
|
|
38
38
|
requiredPlatforms: Object.freeze(["linux", "macos", "windows"]),
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
41
|
+
// Repository-owned anchor for the first public release containing the complete
|
|
42
|
+
// DELTA lifecycle exporter and repository-probe hardening. Coverage must begin
|
|
43
|
+
// at this exact npm publication boundary; report input cannot override it.
|
|
44
44
|
export const COMMAND_LIFECYCLE_DELTA_RELEASE_POLICY = Object.freeze({
|
|
45
45
|
schema: "chainlesschain.command-lifecycle-delta-release-policy.v1",
|
|
46
|
-
firstPublicRelease:
|
|
46
|
+
firstPublicRelease: Object.freeze({
|
|
47
|
+
version: "0.163.4",
|
|
48
|
+
commitSha: "27ed0ac2005e16ce5ddff53990e85b1d13ea0b1d",
|
|
49
|
+
npmPublishedAt: "2026-08-10T19:42:11.053Z",
|
|
50
|
+
}),
|
|
47
51
|
incompatibleReleases: Object.freeze(["0.162.198", "0.163.3"]),
|
|
48
52
|
});
|
|
49
53
|
|
|
@@ -108,8 +108,9 @@ async function invokeChatCall(callWrapper, provider, model, call) {
|
|
|
108
108
|
* @param {string} [options.apiKey] - API key override
|
|
109
109
|
* @param {(request: {call: function, provider: string, model: string}) => Promise<object>} [options.callWrapper]
|
|
110
110
|
* Optional host boundary around each real provider call. The callback
|
|
111
|
-
*
|
|
112
|
-
* receive only the
|
|
111
|
+
* may invoke `call({ signal })` to bind cancellation and resolves to a
|
|
112
|
+
* private `{content, usage?}` envelope; callers still receive only the
|
|
113
|
+
* response string.
|
|
113
114
|
* @returns {(messages: object[], opts?: object) => Promise<string>}
|
|
114
115
|
*/
|
|
115
116
|
export function createChatFn(options = {}) {
|
|
@@ -145,9 +146,10 @@ export function createChatFn(options = {}) {
|
|
|
145
146
|
callWrapper,
|
|
146
147
|
provider,
|
|
147
148
|
currentModel,
|
|
148
|
-
async () => {
|
|
149
|
+
async ({ signal = opts.signal || resolved.signal } = {}) => {
|
|
149
150
|
const res = await fetch(`${baseUrl}/api/chat`, {
|
|
150
151
|
method: "POST",
|
|
152
|
+
signal,
|
|
151
153
|
headers: { "Content-Type": "application/json" },
|
|
152
154
|
body: JSON.stringify({
|
|
153
155
|
model: currentModel,
|
|
@@ -185,9 +187,10 @@ export function createChatFn(options = {}) {
|
|
|
185
187
|
callWrapper,
|
|
186
188
|
provider,
|
|
187
189
|
currentModel,
|
|
188
|
-
async () => {
|
|
190
|
+
async ({ signal = opts.signal || resolved.signal } = {}) => {
|
|
189
191
|
const res = await fetch(`${baseUrl}/messages`, {
|
|
190
192
|
method: "POST",
|
|
193
|
+
signal,
|
|
191
194
|
headers: {
|
|
192
195
|
"Content-Type": "application/json",
|
|
193
196
|
"x-api-key": key,
|
|
@@ -214,9 +217,10 @@ export function createChatFn(options = {}) {
|
|
|
214
217
|
callWrapper,
|
|
215
218
|
provider,
|
|
216
219
|
currentModel,
|
|
217
|
-
async () => {
|
|
220
|
+
async ({ signal = opts.signal || resolved.signal } = {}) => {
|
|
218
221
|
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
219
222
|
method: "POST",
|
|
223
|
+
signal,
|
|
220
224
|
headers: {
|
|
221
225
|
"Content-Type": "application/json",
|
|
222
226
|
Authorization: `Bearer ${key}`,
|