chainlesschain 0.166.115 → 0.166.118
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 +43 -5
- package/package.json +4 -4
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BsoSoDNb.js → AIOps-BGySwMIP.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Dcx7Gm5t.js → ActionButton-Chn7fBLr.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-S1hsBIz3.js → Analytics-ORH7UkGa.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-B2j067qM.js → AppLayout-8xV4jWjP.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-D5iGV9ic.js → Artifacts-26SBK-x-.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-C8ChVvXu.js → Audit-CtXpqbXg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Cg97AAJS.js → BackgroundAgents-C5zLvQis.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CyxU9IUg.js → Backup-C93xY4vO.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKlWjA8I.js → BaseInput-DpVCHfAn.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-C-EqZza6.js → Chat-DM0LblR2.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BRFiXKPX.js → ChatBubbleRenderer-unlQUF1T.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-kkI13tSJ.js → Checkbox-BsAt3cKR.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-NlqEDwHK.js → Codegen-E_kp4KFd.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CfepEu79.js → Col-6msR3T6I.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BRo0qLXM.js → Community-PyX9c5D-.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BMu8TP0E.js → Compact-7G1bVDSH.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B7Q3Moef.js → Compliance-DcHN7GlD.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BVwt7Xmc.js → Cowork-WUIHJYDx.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-CgBv20Qy.js → Cron-CWgCbptg.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CwcSBJk2.js → Crosschain-BzKZ6roD.js} +1 -1
- package/src/assets/web-panel/assets/{DID-d20rCiIm.js → DID-r7zxeItn.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-PpZaUYpS.js → Dashboard-D3pBiN2H.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DjsLdX_Z.js → Dropdown-C4otRJlZ.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CaPkrtxn.js → EmailListRenderer-CzDTC8lm.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-DDY8cQnE.js → EvolutionSettings-DGkXiIs-.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-VQavMjyj.js → FamilyGuardDashboard-BKdEyuiR.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D0GvKQgI.js → Federation-dsI-xcpf.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Dl_TWHmb.js → FormItemContext-CEEb1fMR.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Bny2uagG.js → GenericCardRenderer-D_mfO3Ts.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B01KXq47.js → Git-CBwr2ZI3.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-F4c4Vwo1.js → Governance-D-Wy2Jjr.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-_025Q-8K.js → Inference-B4Yk_IRx.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DwbTmQb1.js → KnowledgeGraph-DoI7bi6m.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CnNo0hCm.js → Logs-Ce1IiKAj.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-GND_8y0V.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Dkz5bv7F.js → Marketplace-CsIEByzK.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CBtRwDW_.js → McpTools-_DRzjcIn.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Y9jnyTb7.js → Memory-zvLpyi__.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Br1urkOz.js → MobileBridge-DXd1ik2Z.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DnsyuerX.js → MobileProjects-DKh2q-Y9.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-he3ZzieF.js → Mtc-Ct-fhtIx.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BadphAuN.js → MtcAudit-J4zY3Oqk.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-3_pVLymK.js → Multisig-B2HJMCKe.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DmtmHfsI.js → NLProgramming-CwcSmU4q.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CE4L2O55.js → Notes-Da53L6wb.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-B7QmfJn9.js → NotificationSettings-BxhufdrA.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-O0wRSyJ0.js → OrderTableRenderer-BoFwohAi.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BrlMSsyT.js → Organization-MIsMrUes.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CY4ULPrZ.js → Overflow-Dy2P4v3q.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-kIANZYKd.js → P2P-CD9Sxn2y.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DEgB4rvF.js → PdhVaultBrowser-BvBWgfG0.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-CWxywRp5.js → Permissions-CwOlDRis.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DAg7iCbr.js → PersonalDataHub-CZYaxEpv.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-Brc6ONnR.js → Pipeline-kLU7PUbB.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CcinYTL8.js → Privacy-BVRycSiI.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Bz8XEsom.js → ProjectInit-DP3pr7uJ.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CsOMlSTZ.js → ProjectSettings-CACaDYjs.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CB1k4zTC.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CeunOQiH.js → Providers-D7Y9WHRE.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CN2a2_Fb.js → QrScannerModal-CeP8xzs1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CVeLif3e.js → QuickAsk-iJVFrhZ2.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-1k9WOEGM.js → Recommend-DmTpnagw.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-Q8UCqIMw.js → RemoteSession-C6ek8Uni.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CkrCgXKM.js → Reputation-CPfwc9de.js} +1 -1
- package/src/assets/web-panel/assets/{Row-JFdvuTzi.js → Row-C1wwtSMJ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C74JHCmk.js → RssFeed-BiDJsqaH.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DrJiVO1T.js → Search-BF6Aon95.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BJzNlSJq.js → Security-Bps0XnV4.js} +3 -3
- package/src/assets/web-panel/assets/{Services-D4TLMnJm.js → Services-uf92smGR.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-cswTDqhh.js → Skeleton-DQd4eVWW.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Bv0rM1BS.js → Skills-DgamiYa5.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-D9rE7hNh.js → Sla-CF1IVIrm.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-Bzxmjbpc.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-BwnMG8kq.js → SyncSettings-B8zKauiq.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-wJvU24cB.js → Tasks-DPKV_xSt.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CZbRU3tN.js → Templates-BikeSZIF.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-_jbpFJPZ.js → Tenant-CEaUaDkJ.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BzNm5WKZ.js → Terminal-ZPgOSQ_q.js} +3 -3
- package/src/assets/web-panel/assets/{TimelineRenderer-bmybWjuS.js → TimelineRenderer-DmyB4Udk.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-o1WDcqVQ.js → Tokens-g85Pm5TO.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BB1D6sIB.js → Trigger-D202hx9a.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CLxdCfy2.js → Trust-DzvQYVFy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B9LS6vS0.js → UkeySign-KRzwddVg.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Bg0-wxjz.js → VideoEditing-IRBXLO2z.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Bw0G2F6P.js → Wallet-DSf5KPgQ.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-irEiNY9b.js → WebAuthn-DNPxLTCc.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Bh08FbJs.js → WorkflowEditor-C3wAtVcK.js} +1 -1
- package/src/assets/web-panel/assets/{addon-fit-CK6X9sAG.js → addon-fit-CbkjSUJl.js} +1 -1
- package/src/assets/web-panel/assets/{chat-De-FPjVT.js → chat-q3XXUtv2.js} +1 -1
- package/src/assets/web-panel/assets/{colors-ygdyvinU.js → colors-CF54SNei.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BQ9jH9KV.js → compact-item-BaRkvZuT.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BFuVSyox.js → createContext-CDR9hsfs.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-1LO-NplX.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-i19GtR1P.js → hasIn-DlC--S8R.js} +1 -1
- package/src/assets/web-panel/assets/{index-DS1IwwGL.js → index-B0Ptc7yu.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1IsP_JO.js → index-B0V9rgdu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWfgeaG4.js → index-BEiUlFKb.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTY20pWu.js → index-BJO0n1LQ.js} +1 -1
- package/src/assets/web-panel/assets/index-BLaJsev4.js +1 -0
- package/src/assets/web-panel/assets/index-BLmviChG.js +1 -0
- package/src/assets/web-panel/assets/{index-IXkif6J_.js → index-BU5DJqk7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVhtukYX.js → index-BY-Tf3OM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BKlU5VZy.js → index-BZrE0Go8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bw5pTpnI.js → index-BizdX2X5.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGMxK155.js → index-C87Yr3h1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVnFgasM.js → index-C9Wypz9e.js} +1 -1
- package/src/assets/web-panel/assets/{index-C61w3omz.js → index-CAoG_E-q.js} +1 -1
- package/src/assets/web-panel/assets/{index-l5-J0jHx.js → index-CAxSyMrF.js} +1 -1
- package/src/assets/web-panel/assets/{index-28DjsABm.js → index-CC5HjNM6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPUCvc8i.js → index-CNZQEZ9g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpdXhhMY.js → index-CPvIhha6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAboGZTw.js → index-CR_wqMGy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DL7ewjD2.js → index-CSClXoB3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bhf-MuUh.js → index-CZ8azVgB.js} +3 -3
- package/src/assets/web-panel/assets/{index-BaaydJcC.js → index-Ccc5z8eJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2LieObB.js → index-CeiKbM3a.js} +1 -1
- package/src/assets/web-panel/assets/{index-We2nK3ju.js → index-Cl6av3X7.js} +1 -1
- package/src/assets/web-panel/assets/{index-m8waSAyv.js → index-CtGfeD7E.js} +1 -1
- package/src/assets/web-panel/assets/{index-cDJsyAkI.js → index-Ctid12Gq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlwxUZQL.js → index-D0H-N0uG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBYiCI1d.js → index-D0vIgprF.js} +1 -1
- package/src/assets/web-panel/assets/{index-_9tUBcLm.js → index-D4wUDFRg.js} +1 -1
- package/src/assets/web-panel/assets/{index-4HoFz1MA.js → index-DJFipyK7.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cq9k4-Ws.js → index-DX2REKNp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuuOX5u3.js → index-Dc3gD0cw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgjLaB7k.js → index-DiQzer2R.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMNjaSir.js → index-DntKLOs9.js} +1 -1
- package/src/assets/web-panel/assets/{index-vXtH3o6O.js → index-Dplhl6gh.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLMtCKAY.js → index-Dq8q_PWB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXrk8OYO.js → index-IVQdOBNN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBVcGaxk.js → index-Kerduyao.js} +1 -1
- package/src/assets/web-panel/assets/{index-BKblSCL9.js → index-W6cLGjpW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgoSgmjK.js → index-qZDNFll9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-B_EZHeqN.js → initDefaultProps-Ccz7v71u.js} +1 -1
- package/src/assets/web-panel/assets/{markdown-Yo_c6Krv.js → markdown-Bq2x0XsR.js} +1 -1
- package/src/assets/web-panel/assets/markdown-DUKxbhE4.js +51 -0
- package/src/assets/web-panel/assets/{motion-BrVb_Gy1.js → motion-BlCLpVDp.js} +1 -1
- package/src/assets/web-panel/assets/{move-CKxVRL_J.js → move-B80G7fLU.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C80rvQ1J.js → mtc-parser-Djw08NJh.js} +1 -1
- package/src/assets/web-panel/assets/{omit-De5vD7BR.js → omit-Bg_G1eUr.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BTyEDjth.js → pickAttrs-Df9zYUg2.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BWR94AjR.js → placementArrow-DjCDLhJ4.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-1g-Vnibk.js → responsiveObserve--Naz7NgY.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Dmg6dYuF.js → slide-CHn3ald-.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Ddj6rSab.js → statusUtils-BiGwoIK_.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DyP1mPDf.js → styleChecker-Zf2dQx1j.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D3d_0YuY.js → useFlexGapSupport-sLES8nyD.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-M3vq-gE2.js → useFs-DV1Hl4qu.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-GtP2TgtK.js → usePersonalDataHub-CZXr2Qxg.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BurE2ATQ.js → vnode-e8Eo3Oyl.js} +1 -1
- package/src/assets/web-panel/assets/{xterm-BZcWGsqw.js → xterm-zbskxxGA.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Ch-ViiDe.js → zoom-B8A6xKMF.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/compact.js +7 -2
- package/src/data/changelog.json +27 -1
- package/src/harness/jsonl-session-store.js +80 -74
- package/src/harness/prompt-compressor.js +57 -3
- package/src/lib/artifact-store.js +129 -20
- package/src/lib/bounded-artifact-file-read.js +128 -46
- package/src/lib/context-memory-kernel/context-image-transport.js +248 -0
- package/src/lib/context-memory-kernel/jsonl-session-context-port.js +4 -1
- package/src/lib/context-memory-kernel/live-compaction.js +40 -10
- package/src/lib/context-memory-kernel/message-adapter.js +80 -10
- package/src/lib/context-memory-kernel/message-bundle-store.js +1140 -0
- package/src/lib/context-memory-kernel/provider-context.js +23 -8
- package/src/lib/evolution/agent-evolution-ingress.js +103 -19
- package/src/lib/evolution/agent-evolution-runtime-composition.js +65 -27
- package/src/lib/evolution/agent-model-projection.js +90 -18
- package/src/lib/evolution/evidence-json-text.js +341 -42
- package/src/lib/evolution/evolution-artifact-ports.js +1005 -83
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +59 -15
- package/src/lib/evolution/evolution-evidence-projector.js +153 -16
- package/src/lib/evolution/evolution-evidence-storage-layout.js +115 -0
- package/src/lib/evolution/evolution-file-witness.js +7 -3
- package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +6 -2
- package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +6 -2
- package/src/lib/evolution/evolution-ledger.js +226 -55
- package/src/lib/evolution/evolution-private-storage.js +20 -0
- package/src/lib/evolution/evolution-run-ledger-adapter.js +298 -11
- package/src/lib/evolution/evolution-windows-directory-durability.js +215 -32
- package/src/lib/evolution/local-ui-evolution-runtime.js +3 -38
- package/src/lib/evolution/local-ui-signing-authority.js +69 -0
- package/src/lib/image-token-estimate.js +59 -0
- package/src/lib/ollama-provider-fetch.js +33 -0
- package/src/lib/provider-stream.js +14 -13
- package/src/lib/read-file-loop-guard.js +7 -4
- package/src/lib/session-history-origins.js +94 -35
- package/src/lib/session-message-provenance.js +26 -2
- package/src/lib/session-runtime-retention.js +10 -1
- package/src/lib/session-transcript-history.js +5 -1
- package/src/lib/session-transcript-migration.js +67 -12
- package/src/lib/storage-failure-cause-codes.js +449 -0
- package/src/lib/tool-result-preview.js +276 -0
- package/src/runtime/agent-core.js +111 -92
- package/src/runtime/headless-stream.js +121 -27
- package/src/assets/web-panel/assets/MarkdownRenderer-D8_iZief.js +0 -1
- package/src/assets/web-panel/assets/Projects-BeSOM6wi.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-Bfhvin8B.js +0 -1
- package/src/assets/web-panel/assets/devWarning-c2I2Rn4_.js +0 -1
- package/src/assets/web-panel/assets/index-CTs4dYcV.js +0 -1
- package/src/assets/web-panel/assets/index-DeuvaW15.js +0 -1
- package/src/assets/web-panel/assets/markdown-CsiA8-E5.js +0 -50
|
@@ -28,6 +28,11 @@ import { types as utilTypes } from "node:util";
|
|
|
28
28
|
import { withFileLock } from "./with-file-lock.js";
|
|
29
29
|
import { readBoundedArtifactFile } from "./bounded-artifact-file-read.js";
|
|
30
30
|
|
|
31
|
+
// Per-call, internal formatting opt-in. Public options/schema strings cannot
|
|
32
|
+
// select it, and it grants no read, signature or storage authority.
|
|
33
|
+
const TYPED_INDEX_ENCODERS = new WeakMap();
|
|
34
|
+
const DEFAULT_FUNCTION_BIND = Function.prototype.bind;
|
|
35
|
+
|
|
31
36
|
export const ARTIFACT_KINDS = Object.freeze([
|
|
32
37
|
"report",
|
|
33
38
|
"patch",
|
|
@@ -305,18 +310,19 @@ export class ArtifactStore {
|
|
|
305
310
|
* caller still owns the semantic validation of the persisted lineage and
|
|
306
311
|
* must verifyIntegrity() before treating the returned entry as readable.
|
|
307
312
|
*/
|
|
308
|
-
publishDataOnce({
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
313
|
+
publishDataOnce(options = {}) {
|
|
314
|
+
const {
|
|
315
|
+
data,
|
|
316
|
+
fileName = "artifact.bin",
|
|
317
|
+
title,
|
|
318
|
+
kind,
|
|
319
|
+
mime,
|
|
320
|
+
sessionId = null,
|
|
321
|
+
ttlDays,
|
|
322
|
+
immutable = false,
|
|
323
|
+
recordDigest,
|
|
324
|
+
lineage,
|
|
325
|
+
} = options;
|
|
320
326
|
if (typeof data !== "string" && !Buffer.isBuffer(data)) {
|
|
321
327
|
throw new Error("publishDataOnce requires string or Buffer data");
|
|
322
328
|
}
|
|
@@ -337,7 +343,7 @@ export class ArtifactStore {
|
|
|
337
343
|
if (matches.length === 1) {
|
|
338
344
|
return Object.freeze({ entry: matches[0], published: false });
|
|
339
345
|
}
|
|
340
|
-
const
|
|
346
|
+
const metadata = {
|
|
341
347
|
fileName,
|
|
342
348
|
title,
|
|
343
349
|
kind,
|
|
@@ -348,14 +354,22 @@ export class ArtifactStore {
|
|
|
348
354
|
recordDigest: stableDigest,
|
|
349
355
|
lineage: normalizedLineage,
|
|
350
356
|
sourcePath: null,
|
|
351
|
-
}
|
|
357
|
+
};
|
|
358
|
+
const encoder = TYPED_INDEX_ENCODERS.get(options);
|
|
359
|
+
if (encoder && hasDefaultTypedIndexWriter(this))
|
|
360
|
+
TYPED_INDEX_ENCODERS.set(metadata, encoder);
|
|
361
|
+
let entry;
|
|
362
|
+
try {
|
|
363
|
+
entry = this._publishDataUnlocked(Buffer.from(data), metadata);
|
|
364
|
+
} finally {
|
|
365
|
+
TYPED_INDEX_ENCODERS.delete(metadata);
|
|
366
|
+
}
|
|
352
367
|
return Object.freeze({ entry, published: true });
|
|
353
368
|
});
|
|
354
369
|
}
|
|
355
370
|
|
|
356
|
-
_publishDataUnlocked(
|
|
357
|
-
|
|
358
|
-
{
|
|
371
|
+
_publishDataUnlocked(body, metadata) {
|
|
372
|
+
const {
|
|
359
373
|
fileName,
|
|
360
374
|
title,
|
|
361
375
|
kind,
|
|
@@ -366,8 +380,7 @@ export class ArtifactStore {
|
|
|
366
380
|
recordDigest = null,
|
|
367
381
|
lineage = null,
|
|
368
382
|
sourcePath = null,
|
|
369
|
-
}
|
|
370
|
-
) {
|
|
383
|
+
} = metadata;
|
|
371
384
|
if (body.length > MAX_ARTIFACT_BYTES) {
|
|
372
385
|
throw new Error(
|
|
373
386
|
`artifact too large (${body.length} bytes > ${MAX_ARTIFACT_BYTES}); artifacts are deliverables, not build output`,
|
|
@@ -428,7 +441,14 @@ export class ArtifactStore {
|
|
|
428
441
|
if (!indexStat.isFile() || Number(indexStat.nlink) !== 1) {
|
|
429
442
|
throw new Error("artifact index identity is invalid");
|
|
430
443
|
}
|
|
431
|
-
|
|
444
|
+
const encoder = TYPED_INDEX_ENCODERS.get(metadata);
|
|
445
|
+
const encoded =
|
|
446
|
+
encoder && hasDefaultTypedIndexWriter(this) ? encoder(entry) : null;
|
|
447
|
+
fs.writeFileSync(
|
|
448
|
+
descriptor,
|
|
449
|
+
`${encoded ?? JSON.stringify(entry)}\n`,
|
|
450
|
+
"utf-8",
|
|
451
|
+
);
|
|
432
452
|
fs.fsyncSync(descriptor);
|
|
433
453
|
} catch (error) {
|
|
434
454
|
try {
|
|
@@ -645,3 +665,92 @@ export class ArtifactStore {
|
|
|
645
665
|
}
|
|
646
666
|
}
|
|
647
667
|
}
|
|
668
|
+
|
|
669
|
+
// Capture repository method identities before any importer can replace the
|
|
670
|
+
// prototype. These references grant no storage or read authority by themselves.
|
|
671
|
+
export const ARTIFACT_STORE_DEFAULT_READ_METHODS = Object.freeze({
|
|
672
|
+
list: ArtifactStore.prototype.list,
|
|
673
|
+
get: ArtifactStore.prototype.get,
|
|
674
|
+
storedPath: ArtifactStore.prototype.storedPath,
|
|
675
|
+
verifyIntegrity: ArtifactStore.prototype.verifyIntegrity,
|
|
676
|
+
filesDir: ArtifactStore.prototype._filesDir,
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
export const ARTIFACT_STORE_DEFAULT_PUBLISH_DATA_ONCE =
|
|
680
|
+
ArtifactStore.prototype.publishDataOnce;
|
|
681
|
+
|
|
682
|
+
const TYPED_INDEX_DEFAULT_METHODS = Object.freeze(
|
|
683
|
+
Object.fromEntries(
|
|
684
|
+
[
|
|
685
|
+
"publishDataOnce",
|
|
686
|
+
"_publishDataUnlocked",
|
|
687
|
+
"_readEntries",
|
|
688
|
+
"_withIndexLock",
|
|
689
|
+
"_ensureDirs",
|
|
690
|
+
"_indexFile",
|
|
691
|
+
"_filesDir",
|
|
692
|
+
"_deleteStoredFile",
|
|
693
|
+
"list",
|
|
694
|
+
"get",
|
|
695
|
+
"storedPath",
|
|
696
|
+
"verifyIntegrity",
|
|
697
|
+
].map((name) => [name, ArtifactStore.prototype[name]]),
|
|
698
|
+
),
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
function hasDefaultTypedIndexWriter(store) {
|
|
702
|
+
if (
|
|
703
|
+
utilTypes.isProxy(store) ||
|
|
704
|
+
Object.getPrototypeOf(store) !== ArtifactStore.prototype ||
|
|
705
|
+
Object.getOwnPropertyDescriptor(store, "_indexLock")?.value !==
|
|
706
|
+
withFileLock ||
|
|
707
|
+
Object.getOwnPropertyDescriptor(Object.prototype, "toJSON") !== undefined
|
|
708
|
+
)
|
|
709
|
+
return false;
|
|
710
|
+
for (const [name, expected] of Object.entries(TYPED_INDEX_DEFAULT_METHODS)) {
|
|
711
|
+
const descriptor =
|
|
712
|
+
Object.getOwnPropertyDescriptor(store, name) ||
|
|
713
|
+
Object.getOwnPropertyDescriptor(ArtifactStore.prototype, name);
|
|
714
|
+
if (
|
|
715
|
+
!descriptor ||
|
|
716
|
+
!("value" in descriptor) ||
|
|
717
|
+
descriptor.value !== expected
|
|
718
|
+
)
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** Internal ports adapter; callers still invoke their captured publication hook. */
|
|
725
|
+
export function captureArtifactStoreTypedIndexPublisher(
|
|
726
|
+
store,
|
|
727
|
+
publish,
|
|
728
|
+
encoder,
|
|
729
|
+
) {
|
|
730
|
+
// A custom bind is itself a publication hook, even on the default method.
|
|
731
|
+
// Inspect descriptors without invoking getters; the legacy bind call below
|
|
732
|
+
// still runs exactly once at the original capture point.
|
|
733
|
+
const defaultBind =
|
|
734
|
+
publish === ARTIFACT_STORE_DEFAULT_PUBLISH_DATA_ONCE &&
|
|
735
|
+
Object.getPrototypeOf(publish) === Function.prototype &&
|
|
736
|
+
Object.getOwnPropertyDescriptor(publish, "bind") === undefined &&
|
|
737
|
+
Object.getOwnPropertyDescriptor(Function.prototype, "bind")?.value ===
|
|
738
|
+
DEFAULT_FUNCTION_BIND;
|
|
739
|
+
// Preserve the original capture timing and custom bind semantics. A valid
|
|
740
|
+
// function can shadow its public .call property; invocation must not use it.
|
|
741
|
+
const boundPublish = Object.freeze(publish.bind(store));
|
|
742
|
+
return Object.freeze((options) => {
|
|
743
|
+
if (
|
|
744
|
+
defaultBind &&
|
|
745
|
+
publish === ARTIFACT_STORE_DEFAULT_PUBLISH_DATA_ONCE &&
|
|
746
|
+
hasDefaultTypedIndexWriter(store)
|
|
747
|
+
) {
|
|
748
|
+
TYPED_INDEX_ENCODERS.set(options, encoder);
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
return boundPublish(options);
|
|
752
|
+
} finally {
|
|
753
|
+
TYPED_INDEX_ENCODERS.delete(options);
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
}
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { readBoundedDescriptor } from "./evolution/bounded-descriptor-read.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
withEvolutionDirectoryFileIdentity,
|
|
7
|
+
withEvolutionFileIdentity,
|
|
8
|
+
} from "./evolution/evolution-file-identity.js";
|
|
6
9
|
import { artifactPhysicalIdentity } from "./evolution/evolution-artifact-identity.js";
|
|
7
10
|
|
|
8
11
|
export const ARTIFACT_BOUNDED_READ_FAILED_CODE =
|
|
@@ -67,10 +70,10 @@ function sameStat(left, right) {
|
|
|
67
70
|
);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
function assertReadArguments(args) {
|
|
74
|
+
const [target, expectedSize, maximumBytes] = args;
|
|
72
75
|
if (
|
|
73
|
-
|
|
76
|
+
args.length !== 3 ||
|
|
74
77
|
typeof target !== "string" ||
|
|
75
78
|
!path.isAbsolute(target) ||
|
|
76
79
|
path.resolve(target) !== target ||
|
|
@@ -83,49 +86,128 @@ export function readBoundedArtifactFile(target, expectedSize, maximumBytes) {
|
|
|
83
86
|
throw new TypeError(
|
|
84
87
|
"bounded artifact read requires a canonical path and finite size bounds",
|
|
85
88
|
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readWithIdentity(target, expectedSize, maximumBytes, samePathHandle) {
|
|
92
|
+
const before = fs.lstatSync(target, { bigint: true });
|
|
93
|
+
safeStat(before, expectedSize, maximumBytes, "artifact path");
|
|
94
|
+
if (canonical(fs.realpathSync.native(target)) !== canonical(target))
|
|
95
|
+
fail("artifact path is not physically canonical");
|
|
96
|
+
const fd = fs.openSync(
|
|
97
|
+
target,
|
|
98
|
+
fs.constants.O_RDONLY |
|
|
99
|
+
(fs.constants.O_NOFOLLOW || 0) |
|
|
100
|
+
(fs.constants.O_NONBLOCK || 0),
|
|
101
|
+
);
|
|
86
102
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
103
|
+
const opened = fs.fstatSync(fd, { bigint: true });
|
|
104
|
+
safeStat(opened, expectedSize, maximumBytes, "artifact descriptor");
|
|
105
|
+
if (!samePathHandle(before, opened) || !sameMetadata(before, opened))
|
|
106
|
+
fail("artifact path and descriptor identities differ");
|
|
107
|
+
const bytes = readBoundedDescriptor(
|
|
108
|
+
fs,
|
|
109
|
+
fd,
|
|
110
|
+
Number(opened.size),
|
|
111
|
+
maximumBytes,
|
|
112
|
+
);
|
|
113
|
+
const closed = fs.fstatSync(fd, { bigint: true });
|
|
114
|
+
const after = fs.lstatSync(target, { bigint: true });
|
|
115
|
+
safeStat(closed, expectedSize, maximumBytes, "artifact descriptor");
|
|
116
|
+
safeStat(after, expectedSize, maximumBytes, "artifact path");
|
|
117
|
+
if (!sameStat(opened, closed))
|
|
118
|
+
fail("artifact descriptor changed during bounded read");
|
|
119
|
+
if (
|
|
120
|
+
!sameStat(before, after) ||
|
|
121
|
+
!samePathHandle(after, closed) ||
|
|
122
|
+
!sameMetadata(after, closed) ||
|
|
123
|
+
canonical(fs.realpathSync.native(target)) !== canonical(target)
|
|
124
|
+
)
|
|
125
|
+
fail("artifact pathname changed during bounded read");
|
|
126
|
+
return bytes;
|
|
127
|
+
} finally {
|
|
128
|
+
fs.closeSync(fd);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function wrapReadFailure(cause) {
|
|
133
|
+
if (cause?.code === ARTIFACT_BOUNDED_READ_FAILED_CODE) throw cause;
|
|
134
|
+
fail("artifact bounded descriptor read failed", cause);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Read one stable window only. Callers retain hash/signature and root checks. */
|
|
138
|
+
export function readBoundedArtifactFile(target, expectedSize, maximumBytes) {
|
|
139
|
+
assertReadArguments(arguments);
|
|
140
|
+
try {
|
|
141
|
+
return withEvolutionFileIdentity(DIRECTORY_FS, target, (samePathHandle) =>
|
|
142
|
+
readWithIdentity(target, expectedSize, maximumBytes, samePathHandle),
|
|
143
|
+
);
|
|
144
|
+
} catch (cause) {
|
|
145
|
+
wrapReadFailure(cause);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* A synchronous batch shares one authenticated parent handle, never file
|
|
151
|
+
* contents or authority. Each child keeps the complete independent read
|
|
152
|
+
* window above. The parent is reattested before the batch can return.
|
|
153
|
+
*/
|
|
154
|
+
export function withBoundedArtifactDirectoryRead(directory, callback) {
|
|
155
|
+
if (
|
|
156
|
+
arguments.length !== 2 ||
|
|
157
|
+
typeof directory !== "string" ||
|
|
158
|
+
!path.isAbsolute(directory) ||
|
|
159
|
+
path.resolve(directory) !== directory ||
|
|
160
|
+
typeof callback !== "function" ||
|
|
161
|
+
callback.constructor?.name === "AsyncFunction"
|
|
162
|
+
)
|
|
163
|
+
throw new TypeError(
|
|
164
|
+
"bounded artifact directory read requires a canonical directory and synchronous callback",
|
|
165
|
+
);
|
|
166
|
+
let callbackFailure = null;
|
|
167
|
+
try {
|
|
168
|
+
return withEvolutionDirectoryFileIdentity(
|
|
169
|
+
DIRECTORY_FS,
|
|
170
|
+
directory,
|
|
171
|
+
(samePathHandle, _sameStableHandle, { parentPath }) => {
|
|
172
|
+
if (canonical(parentPath) !== canonical(directory))
|
|
173
|
+
fail("artifact batch parent is not physically canonical");
|
|
174
|
+
let active = true;
|
|
175
|
+
const read = Object.freeze((...args) => {
|
|
176
|
+
if (!active) fail("artifact batch read window is closed");
|
|
177
|
+
assertReadArguments(args);
|
|
178
|
+
const [target, expectedSize, maximumBytes] = args;
|
|
179
|
+
if (canonical(path.dirname(target)) !== canonical(parentPath))
|
|
180
|
+
fail(
|
|
181
|
+
"artifact batch read must name a direct child of its captured parent",
|
|
182
|
+
);
|
|
183
|
+
try {
|
|
184
|
+
return readWithIdentity(
|
|
185
|
+
target,
|
|
186
|
+
expectedSize,
|
|
187
|
+
maximumBytes,
|
|
188
|
+
samePathHandle,
|
|
189
|
+
);
|
|
190
|
+
} catch (cause) {
|
|
191
|
+
wrapReadFailure(cause);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
try {
|
|
195
|
+
const result = callback(read);
|
|
196
|
+
if (result && typeof result.then === "function")
|
|
197
|
+
throw new TypeError(
|
|
198
|
+
"artifact batch callback must not return a promise",
|
|
199
|
+
);
|
|
200
|
+
return result;
|
|
201
|
+
} catch (cause) {
|
|
202
|
+
callbackFailure = cause;
|
|
203
|
+
throw cause;
|
|
204
|
+
} finally {
|
|
205
|
+
active = false;
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
);
|
|
127
209
|
} catch (cause) {
|
|
128
|
-
if (cause
|
|
129
|
-
|
|
210
|
+
if (cause === callbackFailure) throw cause;
|
|
211
|
+
wrapReadFailure(cause);
|
|
130
212
|
}
|
|
131
213
|
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { normalizeContextItem } from "@chainlesschain/context-memory-kernel";
|
|
3
|
+
import { AGENT_MODEL_IMAGE_LIMITS } from "../evolution/agent-model-projection.js";
|
|
4
|
+
import {
|
|
5
|
+
IMAGE_TOKEN_RESERVE,
|
|
6
|
+
LOW_DETAIL_IMAGE_TOKEN_RESERVE,
|
|
7
|
+
inspectCanonicalImageDataUrl,
|
|
8
|
+
} from "../image-token-estimate.js";
|
|
9
|
+
|
|
10
|
+
export const CONTEXT_IMAGE_TRANSPORT_PREFIX = "cc-context-image://v1/";
|
|
11
|
+
const CONTEXT_IMAGE_TRANSPORT_MARKER = "cc-context-image";
|
|
12
|
+
|
|
13
|
+
const PLACEHOLDER_URL =
|
|
14
|
+
/^cc-context-image:\/\/v1\/([a-f0-9]{32})\/(sha256-[a-f0-9]{64})$/u;
|
|
15
|
+
|
|
16
|
+
function sha256(value) {
|
|
17
|
+
return `sha256-${createHash("sha256").update(value, "ascii").digest("hex")}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function supportedImageUrlBlock(message, block) {
|
|
21
|
+
if (
|
|
22
|
+
message?.role !== "user" ||
|
|
23
|
+
!Array.isArray(message.content) ||
|
|
24
|
+
!block ||
|
|
25
|
+
typeof block !== "object" ||
|
|
26
|
+
Array.isArray(block) ||
|
|
27
|
+
Object.keys(block).sort().join() !== "image_url,type" ||
|
|
28
|
+
block.type !== "image_url" ||
|
|
29
|
+
!block.image_url ||
|
|
30
|
+
typeof block.image_url !== "object" ||
|
|
31
|
+
Array.isArray(block.image_url) ||
|
|
32
|
+
Object.keys(block.image_url).some((key) => !["url", "detail"].includes(key))
|
|
33
|
+
)
|
|
34
|
+
return null;
|
|
35
|
+
|
|
36
|
+
const { url, detail } = block.image_url;
|
|
37
|
+
if (
|
|
38
|
+
typeof url !== "string" ||
|
|
39
|
+
(detail !== undefined && !["auto", "low", "high"].includes(detail))
|
|
40
|
+
)
|
|
41
|
+
return null;
|
|
42
|
+
if (
|
|
43
|
+
url.length >
|
|
44
|
+
Math.ceil(AGENT_MODEL_IMAGE_LIMITS.maxImageBytes / 3) * 4 + 128
|
|
45
|
+
)
|
|
46
|
+
throw codedTransportError(
|
|
47
|
+
"Image transport exceeds its per-image byte budget",
|
|
48
|
+
);
|
|
49
|
+
const image = inspectCanonicalImageDataUrl(url);
|
|
50
|
+
return image ? { url, detail, bytes: image.decodedBytes } : null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function codedTransportError(message) {
|
|
54
|
+
const error = new Error(message);
|
|
55
|
+
error.code = "CC_CONTEXT_IMAGE_TRANSPORT_INVALID";
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Replace supported user image data URLs with request-local digest commitments. */
|
|
60
|
+
export function captureContextImageTransport(messagesInput) {
|
|
61
|
+
const messages = Array.isArray(messagesInput) ? messagesInput : [];
|
|
62
|
+
const captures = new Map();
|
|
63
|
+
let requestImageBytes = 0;
|
|
64
|
+
let changed = false;
|
|
65
|
+
const transportedMessages = messages.map((message) => {
|
|
66
|
+
if (message?.role !== "user" || !Array.isArray(message.content)) {
|
|
67
|
+
return message;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let contentChanged = false;
|
|
71
|
+
let messageImageBytes = 0;
|
|
72
|
+
let messageImageCount = 0;
|
|
73
|
+
const content = message.content.map((block) => {
|
|
74
|
+
const image = supportedImageUrlBlock(message, block);
|
|
75
|
+
if (!image) return block;
|
|
76
|
+
|
|
77
|
+
messageImageCount++;
|
|
78
|
+
messageImageBytes += image.bytes;
|
|
79
|
+
requestImageBytes += image.bytes;
|
|
80
|
+
if (
|
|
81
|
+
image.bytes > AGENT_MODEL_IMAGE_LIMITS.maxImageBytes ||
|
|
82
|
+
messageImageCount > AGENT_MODEL_IMAGE_LIMITS.maxMessageImages ||
|
|
83
|
+
messageImageBytes > AGENT_MODEL_IMAGE_LIMITS.maxMessageImageBytes ||
|
|
84
|
+
requestImageBytes > AGENT_MODEL_IMAGE_LIMITS.maxRequestImageBytes
|
|
85
|
+
)
|
|
86
|
+
throw codedTransportError(
|
|
87
|
+
"Images exceed the bounded message or request image budget",
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const digest = sha256(image.url);
|
|
91
|
+
const nonce = randomBytes(16).toString("hex");
|
|
92
|
+
const placeholder = `${CONTEXT_IMAGE_TRANSPORT_PREFIX}${nonce}/${digest}`;
|
|
93
|
+
captures.set(placeholder, {
|
|
94
|
+
url: image.url,
|
|
95
|
+
digest,
|
|
96
|
+
detail: image.detail,
|
|
97
|
+
hasDetail: Object.hasOwn(block.image_url, "detail"),
|
|
98
|
+
reserveTokens:
|
|
99
|
+
image.detail === "low"
|
|
100
|
+
? LOW_DETAIL_IMAGE_TOKEN_RESERVE
|
|
101
|
+
: IMAGE_TOKEN_RESERVE,
|
|
102
|
+
});
|
|
103
|
+
contentChanged = true;
|
|
104
|
+
changed = true;
|
|
105
|
+
return {
|
|
106
|
+
...block,
|
|
107
|
+
image_url: { ...block.image_url, url: placeholder },
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
return contentChanged ? { ...message, content } : message;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
messages: changed ? transportedMessages : messages,
|
|
115
|
+
captures,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Preserve image token reserves while removing transport placeholders from item digests. */
|
|
120
|
+
export function adjustContextImageTokenEstimates(itemsInput, captures) {
|
|
121
|
+
const items = Array.isArray(itemsInput) ? itemsInput : [];
|
|
122
|
+
if (!(captures instanceof Map) || captures.size === 0) return items;
|
|
123
|
+
|
|
124
|
+
return items.map((item) => {
|
|
125
|
+
if (typeof item?.content !== "string") return item;
|
|
126
|
+
let placeholderBytes = 0;
|
|
127
|
+
let reserveTokens = 0;
|
|
128
|
+
for (const [placeholder, capture] of captures) {
|
|
129
|
+
const first = item.content.indexOf(placeholder);
|
|
130
|
+
if (first < 0) continue;
|
|
131
|
+
if (item.content.indexOf(placeholder, first + placeholder.length) >= 0) {
|
|
132
|
+
throw codedTransportError(
|
|
133
|
+
"A context image transport placeholder was duplicated during planning",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
placeholderBytes += Buffer.byteLength(placeholder, "utf8");
|
|
137
|
+
reserveTokens += capture.reserveTokens;
|
|
138
|
+
}
|
|
139
|
+
if (placeholderBytes === 0) return item;
|
|
140
|
+
|
|
141
|
+
const { digest: _previousDigest, ...withoutDigest } = item;
|
|
142
|
+
return normalizeContextItem({
|
|
143
|
+
...withoutDigest,
|
|
144
|
+
tokenEstimate: Math.max(
|
|
145
|
+
1,
|
|
146
|
+
Math.ceil(
|
|
147
|
+
(Buffer.byteLength(item.content, "utf8") - placeholderBytes) / 4,
|
|
148
|
+
) + reserveTokens,
|
|
149
|
+
),
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function containsTransportImageUrl(messages) {
|
|
155
|
+
return messages.some(
|
|
156
|
+
(message) =>
|
|
157
|
+
Array.isArray(message?.content) &&
|
|
158
|
+
message.content.some(
|
|
159
|
+
(block) =>
|
|
160
|
+
block?.type === "image_url" &&
|
|
161
|
+
typeof block.image_url?.url === "string" &&
|
|
162
|
+
block.image_url.url.includes(CONTEXT_IMAGE_TRANSPORT_MARKER),
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function containsCapturedPlaceholder(value, captures, seen = new Set()) {
|
|
168
|
+
if (typeof value === "string") {
|
|
169
|
+
for (const match of value.matchAll(
|
|
170
|
+
/cc-context-image:\/\/v1\/[a-f0-9]{32}\/sha256-[a-f0-9]{64}/gu,
|
|
171
|
+
)) {
|
|
172
|
+
if (captures.has(match[0])) return true;
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (!value || typeof value !== "object" || seen.has(value)) return false;
|
|
177
|
+
seen.add(value);
|
|
178
|
+
return Object.values(value).some((entry) =>
|
|
179
|
+
containsCapturedPlaceholder(entry, captures, seen),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Restore captured image data URLs and reject every unrecognized or altered placeholder. */
|
|
184
|
+
export function restoreContextImageTransport(messagesInput, captures) {
|
|
185
|
+
const messages = Array.isArray(messagesInput) ? messagesInput : [];
|
|
186
|
+
const captured = captures instanceof Map ? captures : new Map();
|
|
187
|
+
const seenPlaceholders = new Set();
|
|
188
|
+
const restored = messages.map((message) => {
|
|
189
|
+
if (message?.role !== "user" || !Array.isArray(message.content)) {
|
|
190
|
+
return message;
|
|
191
|
+
}
|
|
192
|
+
let contentChanged = false;
|
|
193
|
+
const content = message.content.map((block) => {
|
|
194
|
+
if (
|
|
195
|
+
!block ||
|
|
196
|
+
typeof block !== "object" ||
|
|
197
|
+
Array.isArray(block) ||
|
|
198
|
+
block.type !== "image_url" ||
|
|
199
|
+
!block.image_url ||
|
|
200
|
+
typeof block.image_url !== "object" ||
|
|
201
|
+
Array.isArray(block.image_url) ||
|
|
202
|
+
typeof block.image_url.url !== "string" ||
|
|
203
|
+
!block.image_url.url.includes(CONTEXT_IMAGE_TRANSPORT_MARKER)
|
|
204
|
+
)
|
|
205
|
+
return block;
|
|
206
|
+
|
|
207
|
+
const placeholder = block.image_url.url;
|
|
208
|
+
const match = PLACEHOLDER_URL.exec(placeholder);
|
|
209
|
+
const capture = captured.get(placeholder);
|
|
210
|
+
if (
|
|
211
|
+
Object.keys(block).sort().join() !== "image_url,type" ||
|
|
212
|
+
Object.keys(block.image_url).some(
|
|
213
|
+
(key) => !["url", "detail"].includes(key),
|
|
214
|
+
) ||
|
|
215
|
+
(block.image_url.detail !== undefined &&
|
|
216
|
+
!["auto", "low", "high"].includes(block.image_url.detail)) ||
|
|
217
|
+
!match ||
|
|
218
|
+
!capture ||
|
|
219
|
+
capture.hasDetail !== Object.hasOwn(block.image_url, "detail") ||
|
|
220
|
+
capture.detail !== block.image_url.detail ||
|
|
221
|
+
match[2] !== capture.digest ||
|
|
222
|
+
sha256(capture.url) !== capture.digest ||
|
|
223
|
+
seenPlaceholders.has(placeholder)
|
|
224
|
+
) {
|
|
225
|
+
throw codedTransportError(
|
|
226
|
+
"A context image transport placeholder is unknown, altered, or duplicated",
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
seenPlaceholders.add(placeholder);
|
|
230
|
+
contentChanged = true;
|
|
231
|
+
return {
|
|
232
|
+
...block,
|
|
233
|
+
image_url: { ...block.image_url, url: capture.url },
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
return contentChanged ? { ...message, content } : message;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (
|
|
240
|
+
containsTransportImageUrl(restored) ||
|
|
241
|
+
containsCapturedPlaceholder(restored, captured)
|
|
242
|
+
) {
|
|
243
|
+
throw codedTransportError(
|
|
244
|
+
"An unrecognized context image transport placeholder reached provider context",
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return restored;
|
|
248
|
+
}
|
|
@@ -120,6 +120,7 @@ export class JsonlSessionContextPort {
|
|
|
120
120
|
items: messagesToContextItems(authority.readMessages(), {
|
|
121
121
|
sessionId,
|
|
122
122
|
allowedSinks: this.allowedSinks,
|
|
123
|
+
allowExternalization: true,
|
|
123
124
|
}),
|
|
124
125
|
}),
|
|
125
126
|
};
|
|
@@ -215,7 +216,9 @@ export class JsonlSessionContextPort {
|
|
|
215
216
|
|
|
216
217
|
async appendCompaction(event, expectedHead) {
|
|
217
218
|
this._assertSession(event.sessionId);
|
|
218
|
-
const messages = contextItemsToMessages(event.outputItems
|
|
219
|
+
const messages = contextItemsToMessages(event.outputItems, {
|
|
220
|
+
sessionId: this.sessionId,
|
|
221
|
+
});
|
|
219
222
|
const stats = compactStats(event, messages);
|
|
220
223
|
const appendHead = this._appendHead(event.operationId, expectedHead);
|
|
221
224
|
try {
|