chainlesschain 0.162.177 → 0.162.178
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 +10 -0
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DT-VPWuN.js → AIOps-DRftcSue.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DyOreOVN.js → ActionButton-BFvUaMKR.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DslNsKux.js → Analytics-C0-Rru4P.js} +3 -3
- package/src/assets/web-panel/assets/AppLayout-bx-BCljj.js +9 -0
- package/src/assets/web-panel/assets/{Artifacts-DoaAtEcY.js → Artifacts-DAJ8B3Un.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DPG3WdJ9.js → Audit-hOk4L1lr.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C_JO6R5G.js → BackgroundAgents-3pbDpM9z.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cv3BooyX.js → Backup-BtziGDn9.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-D2uQXWu-.js → BaseInput-kmshphJ3.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-B4Vczyf3.js → Chat-d7tGSosA.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-p-_PBpX_.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-qTgk4VeP.js → Checkbox-C4Eh4Tz5.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DlLWcIzj.js → Codegen-r7gmhZyA.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DNO73qp7.js → Col-U7mDaGlL.js} +1 -1
- package/src/assets/web-panel/assets/{Community-vRDNpc4j.js → Community-Bpu8aH1T.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CEWtIXni.js → Compact-CHb2XBJe.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-KgYC9Fxi.js → Compliance-Cn_r4dey.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-zj5wvkpT.js → Cowork-CdMWfDcz.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-BLDoCozU.js → Cron-BtnREHWy.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BYnff4VY.js → Crosschain-BKI9TSzr.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C75VFsVm.js → DID-pkNubzzJ.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-1rrz1snN.js +3 -0
- package/src/assets/web-panel/assets/{Dropdown-B5dbO4Kx.js → Dropdown-DLyFVxDc.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BDipZtHJ.js → EmailListRenderer-CtUWfYVq.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DmIzt8jj.js → FamilyGuardDashboard-B8eX2Qe4.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9QOS-uW.js → Federation-D8lsDGus.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B47HRwAm.js → FormItemContext-CRYt_aIw.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-3R4mXmgV.js → GenericCardRenderer-ClKzY8_N.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DrNXZf1C.js → Git-BXj5Dney.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BGd7JR98.js → Governance-Cm-I8RMz.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-D-2he1ed.js → Inference-CPvbhvgt.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-C0sHIbK5.js → KnowledgeGraph-sYNDZaoi.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DiBQO-jL.js → Logs-BZY_3CRZ.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DL_5eV2D.js → MarkdownRenderer-up0BW6pv.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-VR59OdrC.js → Marketplace-C5MEJ2Qe.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DrPzwSd3.js → McpTools-ihdrn43I.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-CBgKALwQ.js → Memory-Br8bPMlh.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-pQ9Tn-Mn.js → MobileBridge-BNntgrYW.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-mASB6jyj.js → MobileProjects-BdV915mw.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DTMgE7HC.js → Mtc-A3wDmlqL.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-CwTnCtiL.js → MtcAudit-CF-mBLWM.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-ngm6WU8f.js → Multisig-Iy2k_MSo.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-4YO4Qww9.js → NLProgramming-kk0W7HUb.js} +1 -1
- package/src/assets/web-panel/assets/Notes-B5u5t_4S.js +7 -0
- package/src/assets/web-panel/assets/{NotificationSettings-r2H8yPKb.js → NotificationSettings-BN0POf6_.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CHkSUrXB.js +1 -0
- package/src/assets/web-panel/assets/{Organization-DrO7jRWS.js → Organization-DwWQESuN.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cnwu4i4t.js → Overflow-DG6klrcC.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-zi-cDwA_.js → P2P-BwJcKkgZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DBEryDv7.js → PdhVaultBrowser-BwOHG0in.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-lqXK2C3x.js → Permissions-CGMXe4TX.js} +4 -4
- package/src/assets/web-panel/assets/PersonalDataHub-ziYqg7-A.js +8 -0
- package/src/assets/web-panel/assets/{Pipeline-Kep5KAGW.js → Pipeline-BzJfeoGt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CHdvOAh1.js → Privacy-DLh3Bblx.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BhKfdwe9.js → ProjectInit-USVWW36c.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Yavk9K8A.js → ProjectSettings-CiiOn62h.js} +2 -2
- package/src/assets/web-panel/assets/Projects-6V-PNmcA.js +1 -0
- package/src/assets/web-panel/assets/{Providers-Bfym6SQn.js → Providers-CBPWWzpu.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CLSQcrlR.js → QrScannerModal-QVwoRUq3.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-VPa2WKQN.js → QuickAsk-CXnR-2Vn.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CGXR8jVU.js → Recommend-ClaNzjmF.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_20YfMB.js → RemoteSession-C3rGw8vJ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-awZh68UV.js → Reputation-Ci-OWAoP.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Ctb4oiYN.js → Row-DRhAwXOS.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D3sdjvUV.js → RssFeed-BQLJQ2_y.js} +3 -3
- package/src/assets/web-panel/assets/{Search-C-zfA4kT.js → Search-QQUpkfFd.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D9XlmHA3.js → Security-DdXKzuKT.js} +4 -4
- package/src/assets/web-panel/assets/{Services-C3UKnr41.js → Services-x2Zh62Qd.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BQxagZfF.js → Skeleton-GdY4KwRz.js} +1 -1
- package/src/assets/web-panel/assets/Skills-Bv4tY1Fd.js +1 -0
- package/src/assets/web-panel/assets/{Sla-DveENvek.js → Sla-C3QnM-11.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BFjXLNgF.js → SpeechSettings-Bl3ytvmI.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BpHgbaP9.js → SyncSettings-Wp9IwTsP.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CF3rIrDe.js +1 -0
- package/src/assets/web-panel/assets/{Templates-DEiaoyve.js → Templates-CGfvVVM9.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ITg774nY.js → Tenant-CVXmwQ3U.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D3ZBcvnb.js → Terminal-bvwrt03E.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BsLU6JjU.js → TimelineRenderer-D0g74KhH.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Cb5Pquc9.js → Tokens-BH4IwzlT.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dwfy-m9N.js → Trigger-DJOpM533.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CnlSRGG0.js → Trust-B43EPRLr.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Y4TlvKVo.js → UkeySign-BmBFG-em.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BpKLtTTO.js → VideoEditing-n9oJwB8k.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-LLBzIq8x.js → Wallet-D5QeivHp.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BeSRiusP.js → WebAuthn-DcNy7igO.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BK_l1tXO.js → WorkflowEditor-BVjc7-5-.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DYd-X7Wg.js → chat-Dy5Tfj3m.js} +1 -1
- package/src/assets/web-panel/assets/{colors-C9uip71j.js → colors-C4c2CNZU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-JBrfjUP6.js → compact-item-DPa3O5vN.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D03fbuiq.js → createContext-TLcfNA6_.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BAwdDA_5.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLQOR6l-.js → hasIn-D41LVFkp.js} +1 -1
- package/src/assets/web-panel/assets/{index-rIxxfdTN.js → index-0oevYjfT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CChaISMb.js → index-388pLGJ1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfOs_bmg.js → index-B8nLBZbv.js} +3 -3
- package/src/assets/web-panel/assets/{index-D-1SqEpQ.js → index-BPTGAjee.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYv-LWdy.js → index-BPtrOBmW.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cpn19c9k.js → index-BQiIxHBy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpYKLrUT.js → index-BSM1cHVK.js} +1 -1
- package/src/assets/web-panel/assets/{index-CS63r8h3.js → index-BYPr9fkz.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q7mmL6w4.js → index-BsQ7Ev6i.js} +1 -1
- package/src/assets/web-panel/assets/{index-SO6lgMg0.js → index-Btk9qLL2.js} +1 -1
- package/src/assets/web-panel/assets/{index-iahNhZwm.js → index-BvmziZSG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnQgRjsm.js → index-BvopLC_T.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ba-Ps_AJ.js → index-C7xhTVyP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuNFCZCx.js → index-CNk_ZooP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CL0ixCxk.js → index-CQrEHoOG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CChQBM1d.js → index-CSxex23J.js} +1 -1
- package/src/assets/web-panel/assets/{index-5YN7F5bO.js → index-CVjgm992.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwoDpKzW.js → index-CWK4zOPe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcBZwlnG.js → index-CX0VsoP4.js} +1 -1
- package/src/assets/web-panel/assets/{index-B46YcgsV.js → index-CXl0YPNG.js} +1 -1
- package/src/assets/web-panel/assets/{index-l08afIbV.js → index-Cip7LWml.js} +1 -1
- package/src/assets/web-panel/assets/{index-6GuLRpBQ.js → index-Cq_hlWNt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYLZY4uh.js → index-CzZ49YZL.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0X76LjX.js → index-D2Nkivlm.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdH9l8wl.js → index-DBUgRs4D.js} +1 -1
- package/src/assets/web-panel/assets/index-DEjVkdzq.js +1 -0
- package/src/assets/web-panel/assets/{index-D4UY5Z60.js → index-DNsBj2Ob.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ce0Bf1YF.js → index-Di4X8cfI.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByHO3SqZ.js → index-DljFsVGI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2zmOsyD.js → index-Korc3QIe.js} +1 -1
- package/src/assets/web-panel/assets/index-LMOc5xrS.js +1 -0
- package/src/assets/web-panel/assets/{index-BNCdRmWf.js → index-MKZDje0H.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8KvuDz0.js → index-OHAfRu_3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaokIHt3.js → index-WEJr8PL5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CILVyK3b.js → index-WinCp6du.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVZWiUE_.js → index-Yjr6cqSK.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqWk1FLG.js → index-iQ6gtFih.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNMAAUVk.js → index-pt3s7yvV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ck17i0OD.js → index-tQ3igERQ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CG9z1c6w.js → initDefaultProps-BFZu87WZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B1Ky7Zff.js → motion-DJIo7qvt.js} +1 -1
- package/src/assets/web-panel/assets/{move-DME8X2P1.js → move-Cfr5qknL.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-cuJYSWB7.js → mtc-parser-Bgc-OadH.js} +1 -1
- package/src/assets/web-panel/assets/{omit-UI_EE0uZ.js → omit-X3WI-TNp.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BIoE_Z_V.js → pickAttrs-pJhU9IxK.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B48-9HKN.js → placementArrow-BNg_Vj3T.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-D7x_GDgD.js → responsiveObserve-TeSEGQ5-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DjzC5_7m.js → slide-D_t_P0bv.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C6BIymhh.js → statusUtils-DrrIMwAT.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B4dGrdD9.js → styleChecker-BjZCuFRm.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Bken4aNE.js → useFlexGapSupport-rTFdlqdQ.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BMlqECqv.js → useFs-BidCpfLX.js} +1 -1
- package/src/assets/web-panel/assets/usePersonalDataHub-C5mHttA9.js +1 -0
- package/src/assets/web-panel/assets/{vnode-CKJQmxa0.js → vnode-BrbcRiHZ.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CaZunuau.js → zoom-PISX7AkR.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/__tests__/hub-wechat.test.js +45 -0
- package/src/commands/background-session.js +5 -2
- package/src/commands/context.js +33 -1
- package/src/commands/hub.js +473 -39
- package/src/commands/init.js +212 -110
- package/src/commands/llm.js +22 -6
- package/src/commands/skill.js +11 -1
- package/src/data/changelog.json +76 -270
- package/src/gateways/ws/personal-data-hub-protocol.js +22 -2
- package/src/harness/golden-transcript.js +3 -0
- package/src/index.js +18 -7
- package/src/lib/approval-authority-store.js +891 -0
- package/src/lib/background-interaction-resolver.js +399 -54
- package/src/lib/background-session-transport.js +3 -1
- package/src/lib/context-breakdown.js +82 -3
- package/src/lib/event-runtime-host.js +225 -0
- package/src/lib/event-runtime-store.js +102 -21
- package/src/lib/event-runtime-worker.js +70 -2
- package/src/lib/hooks-v2-producers.js +33 -8
- package/src/lib/hooks-v2-runtime.js +360 -40
- package/src/lib/interaction-adapter.js +66 -0
- package/src/lib/operation-fingerprint.js +107 -2
- package/src/lib/pdh-dedicated-batch-collectors.js +124 -0
- package/src/lib/personal-data-hub-wiring.js +327 -52
- package/src/lib/process-execution-broker/credential-agent.js +954 -111
- package/src/lib/process-execution-broker/index.js +369 -85
- package/src/lib/process-execution-broker/platform-sandbox.js +320 -140
- package/src/lib/provider-http-error.js +94 -0
- package/src/lib/remote-approval-bridge.js +222 -26
- package/src/lib/side-effect-ledger-store.js +65 -14
- package/src/lib/skill-loader.js +215 -12
- package/src/lib/skill-process-broker.js +15 -1
- package/src/lib/sub-agent-context.js +42 -0
- package/src/lib/turn-binding-store.js +64 -15
- package/src/lib/turn-binding.js +85 -16
- package/src/repl/agent-repl.js +40 -6
- package/src/repl/remote-approval.js +13 -13
- package/src/repl/repl-turn-binding.js +46 -16
- package/src/runtime/agent-core.js +286 -35
- package/src/runtime/coding-agent-contract-shared.cjs +16 -0
- package/src/runtime/headless-runner.js +147 -34
- package/src/runtime/headless-stream.js +147 -7
- package/src/workers/background-agent-worker.js +70 -38
- package/src/assets/web-panel/assets/AppLayout-BNJJAYME.js +0 -9
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +0 -1
- package/src/assets/web-panel/assets/Dashboard-C4Gutni2.js +0 -3
- package/src/assets/web-panel/assets/Notes-DP1k45mF.js +0 -7
- package/src/assets/web-panel/assets/OrderTableRenderer-CoM_Zw0J.js +0 -1
- package/src/assets/web-panel/assets/PersonalDataHub-b63T3HJM.js +0 -9
- package/src/assets/web-panel/assets/Projects-CN8E_5Mq.js +0 -1
- package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +0 -1
- package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +0 -1
- package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +0 -1
- package/src/assets/web-panel/assets/index-Bh8BPtqR.js +0 -1
- package/src/assets/web-panel/assets/index-CeVrHLzZ.js +0 -1
- package/src/assets/web-panel/assets/usePersonalDataHub-DLdrfmMa.js +0 -1
|
@@ -61,6 +61,7 @@ import { SIDE_EFFECT_LEDGER_EVENT } from "../lib/side-effect-ledger-store.js";
|
|
|
61
61
|
import { TurnBindingLog, createTurnBindingFeed } from "../lib/turn-binding.js";
|
|
62
62
|
import { TURN_BINDING_EVENT } from "../lib/turn-binding-store.js";
|
|
63
63
|
import { operationIdempotencyKey } from "../lib/idempotency.js";
|
|
64
|
+
import { CLISkillLoader } from "../lib/skill-loader.js";
|
|
64
65
|
import { expandFileRefsAsync } from "./file-ref-expander.js";
|
|
65
66
|
import { composeSystemPrompt } from "./system-prompt.js";
|
|
66
67
|
import { buildUserContent } from "../lib/image-input.js";
|
|
@@ -79,6 +80,7 @@ import {
|
|
|
79
80
|
} from "../lib/exit-codes.cjs";
|
|
80
81
|
import { isolationLevel } from "../lib/agent-sandbox.js";
|
|
81
82
|
import { createBackgroundPhaseReporter } from "../lib/background-phase-reporter.js";
|
|
83
|
+
import { createBackgroundInteractionClient } from "../lib/background-interaction-resolver.js";
|
|
82
84
|
import { withQuietStdout } from "./quiet-stdout.js";
|
|
83
85
|
import { CostBudget } from "../lib/cost-budget.js";
|
|
84
86
|
import { estimateTokens } from "../harness/prompt-compressor.js";
|
|
@@ -734,21 +736,51 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
734
736
|
deps.backgroundPhaseReporter || createBackgroundPhaseReporter();
|
|
735
737
|
|
|
736
738
|
const sideEffectRunNonce = String(deps.now ? deps.now() : Date.now());
|
|
739
|
+
const _backgroundPolicyDigest = _bgPhase.enabled
|
|
740
|
+
? computePolicyDigest({
|
|
741
|
+
permissionMode: options.permissionMode,
|
|
742
|
+
allowedTools: options.allowedTools,
|
|
743
|
+
disallowedTools: options.disallowedTools,
|
|
744
|
+
permissionRules,
|
|
745
|
+
})
|
|
746
|
+
: null;
|
|
747
|
+
const _backgroundInteraction = _bgPhase.enabled
|
|
748
|
+
? deps.backgroundInteractionClient ||
|
|
749
|
+
createBackgroundInteractionClient({
|
|
750
|
+
backgroundAgentId: process.env.CC_BACKGROUND_AGENT_ID,
|
|
751
|
+
sessionId,
|
|
752
|
+
// One headless invocation is one user turn even when its model/tool
|
|
753
|
+
// loop contains several internal iterations.
|
|
754
|
+
turnId: options.turnId || sideEffectRunNonce,
|
|
755
|
+
})
|
|
756
|
+
: null;
|
|
737
757
|
let sideEffectLedger = new SideEffectLedger({ clock: deps.now || null });
|
|
758
|
+
let sideEffectLedgerLoadError = null;
|
|
738
759
|
let sideEffectSeq = 0;
|
|
739
760
|
let currentSideEffectOpId = null;
|
|
740
761
|
let resumeSideEffectContext = null;
|
|
741
762
|
let resumeAsyncHookContext = null;
|
|
742
763
|
const persistSideEffectLedger = () => {
|
|
743
764
|
if (!persist) return;
|
|
765
|
+
if (sideEffectLedgerLoadError) throw sideEffectLedgerLoadError;
|
|
744
766
|
try {
|
|
745
|
-
store.appendEvent(
|
|
767
|
+
const persisted = store.appendEvent(
|
|
746
768
|
sessionId,
|
|
747
769
|
SIDE_EFFECT_LEDGER_EVENT,
|
|
748
770
|
sideEffectLedger.toJSON(),
|
|
749
771
|
);
|
|
750
|
-
|
|
751
|
-
|
|
772
|
+
if (persisted === false) {
|
|
773
|
+
throw new Error("session store rejected the ledger snapshot");
|
|
774
|
+
}
|
|
775
|
+
} catch (cause) {
|
|
776
|
+
const error = new Error(
|
|
777
|
+
`Side-effect ledger persistence failed for ${sessionId}: ${
|
|
778
|
+
cause?.message || String(cause)
|
|
779
|
+
}`,
|
|
780
|
+
{ cause },
|
|
781
|
+
);
|
|
782
|
+
error.code = "SIDE_EFFECT_LEDGER_PERSIST_FAILED";
|
|
783
|
+
throw error;
|
|
752
784
|
}
|
|
753
785
|
};
|
|
754
786
|
// P1 explicit turn→checkpoint binding: feed the TurnBindingLog in real time
|
|
@@ -761,6 +793,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
761
793
|
// context. Only writes when a turn actually recorded something, and only for
|
|
762
794
|
// persisted runs — a tool-free Q&A or ephemeral run stays byte-identical.
|
|
763
795
|
let turnBindingLog = new TurnBindingLog();
|
|
796
|
+
let turnBindingLoadError = null;
|
|
764
797
|
if (persist) {
|
|
765
798
|
try {
|
|
766
799
|
const events = store.readEvents(sessionId) || [];
|
|
@@ -776,8 +809,15 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
776
809
|
break;
|
|
777
810
|
}
|
|
778
811
|
}
|
|
779
|
-
} catch {
|
|
780
|
-
|
|
812
|
+
} catch (cause) {
|
|
813
|
+
const error = new Error(
|
|
814
|
+
`Turn binding read failed for ${sessionId}: ${
|
|
815
|
+
cause?.message || String(cause)
|
|
816
|
+
}`,
|
|
817
|
+
{ cause },
|
|
818
|
+
);
|
|
819
|
+
error.code = "TURN_BINDING_READ_FAILED";
|
|
820
|
+
turnBindingLoadError = error;
|
|
781
821
|
}
|
|
782
822
|
}
|
|
783
823
|
// Event→table folding + id synthesis live in the SHARED feeder core
|
|
@@ -788,11 +828,26 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
788
828
|
: null;
|
|
789
829
|
const persistTurnBindingLog = () => {
|
|
790
830
|
if (!turnBindingFeed || !turnBindingFeed.isDirty()) return;
|
|
831
|
+
if (turnBindingLoadError) throw turnBindingLoadError;
|
|
791
832
|
try {
|
|
792
|
-
store.appendEvent(
|
|
833
|
+
const persisted = store.appendEvent(
|
|
834
|
+
sessionId,
|
|
835
|
+
TURN_BINDING_EVENT,
|
|
836
|
+
turnBindingLog.toJSON(),
|
|
837
|
+
);
|
|
838
|
+
if (persisted === false) {
|
|
839
|
+
throw new Error("session store rejected the binding snapshot");
|
|
840
|
+
}
|
|
793
841
|
turnBindingFeed.clearDirty();
|
|
794
|
-
} catch {
|
|
795
|
-
|
|
842
|
+
} catch (cause) {
|
|
843
|
+
const error = new Error(
|
|
844
|
+
`Turn binding persistence failed for ${sessionId}: ${
|
|
845
|
+
cause?.message || String(cause)
|
|
846
|
+
}`,
|
|
847
|
+
{ cause },
|
|
848
|
+
);
|
|
849
|
+
error.code = "TURN_BINDING_PERSIST_FAILED";
|
|
850
|
+
throw error;
|
|
796
851
|
}
|
|
797
852
|
};
|
|
798
853
|
if (persist) {
|
|
@@ -812,8 +867,17 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
812
867
|
break;
|
|
813
868
|
}
|
|
814
869
|
}
|
|
815
|
-
} catch {
|
|
816
|
-
|
|
870
|
+
} catch (cause) {
|
|
871
|
+
const error = new Error(
|
|
872
|
+
`Side-effect ledger read failed for ${sessionId}: ${
|
|
873
|
+
cause?.message || String(cause)
|
|
874
|
+
}`,
|
|
875
|
+
{ cause },
|
|
876
|
+
);
|
|
877
|
+
error.code = "SIDE_EFFECT_LEDGER_READ_FAILED";
|
|
878
|
+
// Keep read-only turns available; classified side effects call the
|
|
879
|
+
// persistence gate above before the generator can execute the tool.
|
|
880
|
+
sideEffectLedgerLoadError = error;
|
|
817
881
|
}
|
|
818
882
|
// On resume, surface any operation that was in flight when the prior run
|
|
819
883
|
// died: its outcome is UNKNOWN, so the model is told to VERIFY before any
|
|
@@ -968,10 +1032,19 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
968
1032
|
const _leanNoProjectMemory = options.projectMemory === false;
|
|
969
1033
|
let _loadedInstructions = null;
|
|
970
1034
|
let _loadedPersonaSkills = [];
|
|
1035
|
+
let _skillCacheLedger = null;
|
|
1036
|
+
const _runtimeSkillLoader =
|
|
1037
|
+
options.skillLoader || new CLISkillLoader();
|
|
971
1038
|
const systemContent = composeSystemPrompt(
|
|
972
1039
|
buildSystemPrompt(cwd, {
|
|
973
1040
|
additionalDirectories,
|
|
974
1041
|
projectMemory: options.projectMemory,
|
|
1042
|
+
sessionId,
|
|
1043
|
+
skillLoader: _runtimeSkillLoader,
|
|
1044
|
+
onSkillsLoaded: (skills, cacheLedger) => {
|
|
1045
|
+
_loadedPersonaSkills = Array.isArray(skills) ? skills : [];
|
|
1046
|
+
_skillCacheLedger = cacheLedger || null;
|
|
1047
|
+
},
|
|
975
1048
|
}),
|
|
976
1049
|
{
|
|
977
1050
|
systemPrompt: options.systemPrompt,
|
|
@@ -982,9 +1055,6 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
982
1055
|
onInstructionsLoaded: (loaded) => {
|
|
983
1056
|
_loadedInstructions = loaded;
|
|
984
1057
|
},
|
|
985
|
-
onSkillsLoaded: (skills) => {
|
|
986
|
-
_loadedPersonaSkills = Array.isArray(skills) ? skills : [];
|
|
987
|
-
},
|
|
988
1058
|
},
|
|
989
1059
|
);
|
|
990
1060
|
|
|
@@ -1367,10 +1437,17 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1367
1437
|
// Persist the admitted runtime schema surface so `cc context --sources` can
|
|
1368
1438
|
// attribute MCP cost to the actual server/tool definitions used by this run,
|
|
1369
1439
|
// rather than guessing from the current environment on a later invocation.
|
|
1370
|
-
|
|
1371
|
-
persist
|
|
1372
|
-
|
|
1373
|
-
|
|
1440
|
+
const persistContextSources = () => {
|
|
1441
|
+
if (!persist) return false;
|
|
1442
|
+
_skillCacheLedger =
|
|
1443
|
+
_runtimeSkillLoader.getCacheLedger?.() || _skillCacheLedger;
|
|
1444
|
+
if (
|
|
1445
|
+
!mcp?.extraToolDefinitions?.length &&
|
|
1446
|
+
!_loadedPersonaSkills.length &&
|
|
1447
|
+
!_skillCacheLedger?.descriptors?.resident
|
|
1448
|
+
) {
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1374
1451
|
try {
|
|
1375
1452
|
store.appendEvent(sessionId, "context_sources", {
|
|
1376
1453
|
mcp: (mcp?.extraToolDefinitions || []).map((definition) => ({
|
|
@@ -1384,13 +1461,22 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1384
1461
|
id: skill?.id || skill?.displayName || "skill",
|
|
1385
1462
|
displayName: skill?.displayName || skill?.id || "skill",
|
|
1386
1463
|
source: skill?.source || "skill",
|
|
1387
|
-
|
|
1464
|
+
tokens: estimateTokens(
|
|
1465
|
+
`## Persona: ${skill?.displayName || skill?.id || "skill"}\n${
|
|
1466
|
+
typeof skill?.body === "string" ? skill.body : ""
|
|
1467
|
+
}`,
|
|
1468
|
+
),
|
|
1469
|
+
bodyLoaded: skill?.bodyLoaded === true,
|
|
1388
1470
|
})),
|
|
1471
|
+
skillCache: _skillCacheLedger,
|
|
1389
1472
|
});
|
|
1473
|
+
return true;
|
|
1390
1474
|
} catch {
|
|
1391
1475
|
// Source attribution is observability-only; never fail the agent run.
|
|
1476
|
+
return false;
|
|
1392
1477
|
}
|
|
1393
|
-
}
|
|
1478
|
+
};
|
|
1479
|
+
persistContextSources();
|
|
1394
1480
|
|
|
1395
1481
|
const loopOptions = {
|
|
1396
1482
|
model,
|
|
@@ -1409,6 +1495,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1409
1495
|
baseUrl,
|
|
1410
1496
|
apiKey,
|
|
1411
1497
|
cwd,
|
|
1498
|
+
skillLoader: _runtimeSkillLoader,
|
|
1412
1499
|
additionalDirectories,
|
|
1413
1500
|
sandbox: options.sandbox || null,
|
|
1414
1501
|
sessionId,
|
|
@@ -1435,27 +1522,39 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1435
1522
|
disabledTools,
|
|
1436
1523
|
iterationBudget: budget,
|
|
1437
1524
|
toolAdmission: options.toolAdmission || null,
|
|
1438
|
-
// `ask_user_question` in a BACKGROUND turn child
|
|
1439
|
-
//
|
|
1440
|
-
//
|
|
1441
|
-
//
|
|
1442
|
-
// file (dashboard "Needs input" gets real data; the user's reply arrives
|
|
1443
|
-
// as the next attach prompt) and tell the model to end the turn. A
|
|
1444
|
-
// non-background run supplies no interaction → the handler's existing
|
|
1445
|
-
// `user_not_reachable` path stays byte-identical.
|
|
1525
|
+
// `ask_user_question` in a BACKGROUND turn child: send a bound request to
|
|
1526
|
+
// the worker over Node IPC and await the answer here. `executeTool` remains
|
|
1527
|
+
// suspended, so the model continues the SAME turn/tool call after the user
|
|
1528
|
+
// answers instead of receiving the answer as a follow-up turn.
|
|
1446
1529
|
...(_bgPhase.enabled
|
|
1447
1530
|
? {
|
|
1448
1531
|
interaction: {
|
|
1449
|
-
askUser: async ({
|
|
1532
|
+
askUser: async ({
|
|
1533
|
+
question,
|
|
1534
|
+
options: qOptions,
|
|
1535
|
+
multiSelect,
|
|
1536
|
+
timeoutMs,
|
|
1537
|
+
defaultValue,
|
|
1538
|
+
toolUseId,
|
|
1539
|
+
turnId,
|
|
1540
|
+
policyDigest,
|
|
1541
|
+
} = {}) => {
|
|
1450
1542
|
_bgPhase.reportQuestion({
|
|
1451
1543
|
question: typeof question === "string" ? question : "",
|
|
1452
1544
|
options: Array.isArray(qOptions) ? qOptions : null,
|
|
1453
1545
|
});
|
|
1454
|
-
|
|
1455
|
-
"question
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1546
|
+
return _backgroundInteraction.request({
|
|
1547
|
+
kind: "question",
|
|
1548
|
+
question,
|
|
1549
|
+
options: qOptions,
|
|
1550
|
+
multiSelect,
|
|
1551
|
+
timeoutMs,
|
|
1552
|
+
defaultValue,
|
|
1553
|
+
toolUseId,
|
|
1554
|
+
turnId,
|
|
1555
|
+
policyDigest: policyDigest || _backgroundPolicyDigest,
|
|
1556
|
+
sessionId,
|
|
1557
|
+
});
|
|
1459
1558
|
},
|
|
1460
1559
|
},
|
|
1461
1560
|
}
|
|
@@ -1824,10 +1923,15 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1824
1923
|
if (isText) writeErr(line + "\n");
|
|
1825
1924
|
emitStream({
|
|
1826
1925
|
type: "tool_use",
|
|
1926
|
+
...(event.tool_use_id ? { id: event.tool_use_id } : {}),
|
|
1927
|
+
tool: event.tool,
|
|
1928
|
+
args: event.args,
|
|
1929
|
+
});
|
|
1930
|
+
toolCalls.push({
|
|
1931
|
+
...(event.tool_use_id ? { id: event.tool_use_id } : {}),
|
|
1827
1932
|
tool: event.tool,
|
|
1828
1933
|
args: event.args,
|
|
1829
1934
|
});
|
|
1830
|
-
toolCalls.push({ tool: event.tool, args: event.args });
|
|
1831
1935
|
// P0-2: record an irreversible effect as STARTED (persisted before
|
|
1832
1936
|
// it settles) so a crash before the matching tool-result leaves a
|
|
1833
1937
|
// reconcilable "in flight" marker instead of a silent replay.
|
|
@@ -1882,6 +1986,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1882
1986
|
if (isText && err) writeErr(` Error: ${err}\n`);
|
|
1883
1987
|
emitStream({
|
|
1884
1988
|
type: "tool_result",
|
|
1989
|
+
...(event.tool_use_id ? { id: event.tool_use_id } : {}),
|
|
1885
1990
|
tool: event.tool,
|
|
1886
1991
|
is_error: Boolean(err),
|
|
1887
1992
|
error: err,
|
|
@@ -2198,6 +2303,14 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
2198
2303
|
// "the model call failed" from "the run itself errored" (1).
|
|
2199
2304
|
return { exitCode: classifyLoopError(err), result: message, isError: true };
|
|
2200
2305
|
} finally {
|
|
2306
|
+
// Capture body-cache hits/misses caused by list_skills/run_skill during the
|
|
2307
|
+
// turn. `cc context --sources` reads the newest snapshot.
|
|
2308
|
+
persistContextSources();
|
|
2309
|
+
try {
|
|
2310
|
+
_backgroundInteraction?.close?.();
|
|
2311
|
+
} catch {
|
|
2312
|
+
// Best-effort listener cleanup; never mask the run result.
|
|
2313
|
+
}
|
|
2201
2314
|
// Drop the signal handlers first — a normal return must not leave a
|
|
2202
2315
|
// process-wide SIGINT/SIGTERM listener behind (a later Ctrl-C would wrongly
|
|
2203
2316
|
// exit with 130, and repeated in-process runs would leak listeners).
|
|
@@ -72,10 +72,13 @@ import {
|
|
|
72
72
|
startSession as jsonlStartSession,
|
|
73
73
|
appendUserMessage as jsonlAppendUserMessage,
|
|
74
74
|
appendAssistantMessage as jsonlAppendAssistantMessage,
|
|
75
|
+
appendEvent as jsonlAppendEvent,
|
|
76
|
+
readEvents as jsonlReadEvents,
|
|
75
77
|
rebuildMessages as jsonlRebuildMessages,
|
|
76
78
|
sessionExists as jsonlSessionExists,
|
|
77
79
|
} from "../harness/jsonl-session-store.js";
|
|
78
80
|
import {
|
|
81
|
+
SideEffectLedger,
|
|
79
82
|
reconcileSideEffects,
|
|
80
83
|
classifyToolSideEffect,
|
|
81
84
|
} from "../lib/side-effect-ledger.js";
|
|
@@ -85,6 +88,8 @@ import {
|
|
|
85
88
|
persistSideEffectLedger,
|
|
86
89
|
} from "../lib/side-effect-ledger-store.js";
|
|
87
90
|
import { operationIdempotencyKey } from "../lib/idempotency.js";
|
|
91
|
+
import { TurnBindingLog, createTurnBindingFeed } from "../lib/turn-binding.js";
|
|
92
|
+
import { TURN_BINDING_EVENT } from "../lib/turn-binding-store.js";
|
|
88
93
|
import { getPlanModeManager } from "../lib/plan-mode.js";
|
|
89
94
|
import {
|
|
90
95
|
SESSION_SLASH_COMMANDS,
|
|
@@ -697,7 +702,15 @@ function buildSideEffectRecovery(ledger) {
|
|
|
697
702
|
async function runTurn(
|
|
698
703
|
messages,
|
|
699
704
|
loopOptions,
|
|
700
|
-
{
|
|
705
|
+
{
|
|
706
|
+
runLoop,
|
|
707
|
+
emit,
|
|
708
|
+
costBudget,
|
|
709
|
+
nextToolUseId,
|
|
710
|
+
sideEffects,
|
|
711
|
+
turnNumber,
|
|
712
|
+
turnBindingFeed,
|
|
713
|
+
},
|
|
701
714
|
) {
|
|
702
715
|
const usage = {
|
|
703
716
|
input_tokens: 0,
|
|
@@ -741,12 +754,15 @@ async function runTurn(
|
|
|
741
754
|
collapseConsecutiveMessagesInPlace(messages);
|
|
742
755
|
}
|
|
743
756
|
for await (const event of runLoop(messages, loopOptions)) {
|
|
757
|
+
turnBindingFeed?.handleEvent(event);
|
|
744
758
|
switch (event.type) {
|
|
745
759
|
case "tool-executing": {
|
|
746
760
|
// Additive protocol-v1 correlation id ("tu-<n>", session-scoped —
|
|
747
761
|
// agent-sdk docs/PROTOCOL.md §1.2.1): the matching tool_result below
|
|
748
762
|
// echoes the same id so UIs can pair calls without adjacency.
|
|
749
|
-
const toolUseId =
|
|
763
|
+
const toolUseId =
|
|
764
|
+
event.tool_use_id ||
|
|
765
|
+
(nextToolUseId ? nextToolUseId() : undefined);
|
|
750
766
|
const pm = getPlanModeManager();
|
|
751
767
|
const planItem = pm.startPlanItemForTool(event.tool, {
|
|
752
768
|
toolUseId,
|
|
@@ -826,7 +842,10 @@ async function runTurn(
|
|
|
826
842
|
// recent tool_use (same adjacency rule the is_error attribution
|
|
827
843
|
// below has always used).
|
|
828
844
|
const lastCall =
|
|
829
|
-
|
|
845
|
+
(event.tool_use_id
|
|
846
|
+
? toolCalls.find((call) => call.id === event.tool_use_id)
|
|
847
|
+
: null) ||
|
|
848
|
+
(toolCalls.length > 0 ? toolCalls[toolCalls.length - 1] : null);
|
|
830
849
|
if (lastCall) lastCall.is_error = Boolean(err);
|
|
831
850
|
const pm = getPlanModeManager();
|
|
832
851
|
const settledItem = lastCall?.planItemId
|
|
@@ -1107,6 +1126,8 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1107
1126
|
appendUserMessage: deps.appendUserMessage || jsonlAppendUserMessage,
|
|
1108
1127
|
appendAssistantMessage:
|
|
1109
1128
|
deps.appendAssistantMessage || jsonlAppendAssistantMessage,
|
|
1129
|
+
appendEvent: deps.appendEvent || jsonlAppendEvent,
|
|
1130
|
+
readEvents: deps.readEvents || jsonlReadEvents,
|
|
1110
1131
|
rebuildMessages: deps.rebuildMessages || jsonlRebuildMessages,
|
|
1111
1132
|
};
|
|
1112
1133
|
// --ephemeral forces persistence OFF even with an explicit session id (the
|
|
@@ -1125,12 +1146,15 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1125
1146
|
const loadLedger = deps.loadSideEffectLedger || loadSideEffectLedger;
|
|
1126
1147
|
const persistLedger = deps.persistSideEffectLedger || persistSideEffectLedger;
|
|
1127
1148
|
let sideEffectLedger = null;
|
|
1149
|
+
let sideEffectLedgerLoadError = null;
|
|
1128
1150
|
if (persist) {
|
|
1129
1151
|
try {
|
|
1130
1152
|
sideEffectLedger = loadLedger(sessionId);
|
|
1131
|
-
} catch {
|
|
1132
|
-
//
|
|
1133
|
-
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
// Read-only turns remain available, but the first classified side effect
|
|
1155
|
+
// fails before the generator can resume into the actual operation.
|
|
1156
|
+
sideEffectLedger = new SideEffectLedger();
|
|
1157
|
+
sideEffectLedgerLoadError = error;
|
|
1134
1158
|
}
|
|
1135
1159
|
}
|
|
1136
1160
|
let sideEffectSeq = 0;
|
|
@@ -1141,11 +1165,111 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1141
1165
|
const sideEffects = sideEffectLedger
|
|
1142
1166
|
? {
|
|
1143
1167
|
ledger: sideEffectLedger,
|
|
1144
|
-
persist: () =>
|
|
1168
|
+
persist: () => {
|
|
1169
|
+
if (sideEffectLedgerLoadError) throw sideEffectLedgerLoadError;
|
|
1170
|
+
const persisted = persistLedger(sessionId, sideEffectLedger);
|
|
1171
|
+
if (persisted === false) {
|
|
1172
|
+
const error = new Error(
|
|
1173
|
+
`Side-effect ledger persistence was rejected for ${sessionId}`,
|
|
1174
|
+
);
|
|
1175
|
+
error.code = "SIDE_EFFECT_LEDGER_PERSIST_FAILED";
|
|
1176
|
+
throw error;
|
|
1177
|
+
}
|
|
1178
|
+
return persisted;
|
|
1179
|
+
},
|
|
1145
1180
|
nextOpId: () => `${sideEffectRunNonce}:${sideEffectSeq++}`,
|
|
1146
1181
|
}
|
|
1147
1182
|
: null;
|
|
1148
1183
|
|
|
1184
|
+
// P1 unified turn/checkpoint producer for long-lived stream sessions. Load
|
|
1185
|
+
// the latest snapshot on resume, then append one explicit coverage record
|
|
1186
|
+
// for every input turn (including tool-free turns).
|
|
1187
|
+
let turnBindingLog = new TurnBindingLog();
|
|
1188
|
+
let turnBindingPersistenceError = null;
|
|
1189
|
+
let turnBindingDegradationEmitted = false;
|
|
1190
|
+
let turnBindingTerminalEmitted = false;
|
|
1191
|
+
const recordTurnBindingFailure = (error, operation) => {
|
|
1192
|
+
if (turnBindingPersistenceError) return turnBindingPersistenceError;
|
|
1193
|
+
const wrapped = new Error(
|
|
1194
|
+
`Turn binding ${operation} failed for ${sessionId}: ${
|
|
1195
|
+
error?.message || String(error)
|
|
1196
|
+
}`,
|
|
1197
|
+
{ cause: error },
|
|
1198
|
+
);
|
|
1199
|
+
wrapped.code =
|
|
1200
|
+
operation === "read"
|
|
1201
|
+
? "TURN_BINDING_READ_FAILED"
|
|
1202
|
+
: "TURN_BINDING_PERSIST_FAILED";
|
|
1203
|
+
turnBindingPersistenceError = wrapped;
|
|
1204
|
+
return wrapped;
|
|
1205
|
+
};
|
|
1206
|
+
const emitTurnBindingFailure = () => {
|
|
1207
|
+
if (!turnBindingPersistenceError) return false;
|
|
1208
|
+
if (!turnBindingDegradationEmitted) {
|
|
1209
|
+
emit({
|
|
1210
|
+
type: "recovery_degraded",
|
|
1211
|
+
component: "turn_binding",
|
|
1212
|
+
session_id: sessionId,
|
|
1213
|
+
error: turnBindingPersistenceError.message,
|
|
1214
|
+
});
|
|
1215
|
+
turnBindingDegradationEmitted = true;
|
|
1216
|
+
}
|
|
1217
|
+
if (!turnBindingTerminalEmitted) {
|
|
1218
|
+
emit({
|
|
1219
|
+
type: "result",
|
|
1220
|
+
subtype: "error",
|
|
1221
|
+
is_error: true,
|
|
1222
|
+
error: turnBindingPersistenceError.message,
|
|
1223
|
+
session_id: sessionId,
|
|
1224
|
+
});
|
|
1225
|
+
turnBindingTerminalEmitted = true;
|
|
1226
|
+
}
|
|
1227
|
+
return true;
|
|
1228
|
+
};
|
|
1229
|
+
if (persist) {
|
|
1230
|
+
try {
|
|
1231
|
+
const priorEvents = store.readEvents(sessionId) || [];
|
|
1232
|
+
for (let index = priorEvents.length - 1; index >= 0; index--) {
|
|
1233
|
+
const event = priorEvents[index];
|
|
1234
|
+
if (
|
|
1235
|
+
event?.type === TURN_BINDING_EVENT &&
|
|
1236
|
+
event.data &&
|
|
1237
|
+
typeof event.data === "object"
|
|
1238
|
+
) {
|
|
1239
|
+
turnBindingLog = TurnBindingLog.fromJSON(event.data);
|
|
1240
|
+
break;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
recordTurnBindingFailure(error, "read");
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const turnBindingFeed = persist
|
|
1248
|
+
? createTurnBindingFeed({
|
|
1249
|
+
log: turnBindingLog,
|
|
1250
|
+
nonce: sideEffectRunNonce,
|
|
1251
|
+
})
|
|
1252
|
+
: null;
|
|
1253
|
+
const persistTurnBindingLog = () => {
|
|
1254
|
+
if (!turnBindingFeed?.isDirty()) return true;
|
|
1255
|
+
if (turnBindingPersistenceError) return false;
|
|
1256
|
+
try {
|
|
1257
|
+
const persisted = store.appendEvent(
|
|
1258
|
+
sessionId,
|
|
1259
|
+
TURN_BINDING_EVENT,
|
|
1260
|
+
turnBindingLog.toJSON(),
|
|
1261
|
+
);
|
|
1262
|
+
if (persisted === false) {
|
|
1263
|
+
throw new Error("session store rejected the binding snapshot");
|
|
1264
|
+
}
|
|
1265
|
+
turnBindingFeed.clearDirty();
|
|
1266
|
+
return true;
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
recordTurnBindingFailure(error, "persistence");
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1149
1273
|
// ── Interactive approvals (--interactive-approvals; chat-panel UX) ────────
|
|
1150
1274
|
// CONFIRM-tier decisions (risky shell via the ApprovalGate, settings/hook
|
|
1151
1275
|
// `ask`) normally fail closed in headless. With this opt-in they become a
|
|
@@ -1962,6 +2086,11 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1962
2086
|
for (;;) {
|
|
1963
2087
|
const parsed = await nextEvent();
|
|
1964
2088
|
if (parsed == null) break; // stdin closed
|
|
2089
|
+
if (turnBindingPersistenceError) {
|
|
2090
|
+
emitTurnBindingFailure();
|
|
2091
|
+
sawError = true;
|
|
2092
|
+
break;
|
|
2093
|
+
}
|
|
1965
2094
|
|
|
1966
2095
|
if (parsed.slashCommand) {
|
|
1967
2096
|
const requestId =
|
|
@@ -2418,6 +2547,9 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
2418
2547
|
}
|
|
2419
2548
|
}
|
|
2420
2549
|
turns += 1;
|
|
2550
|
+
turnBindingFeed?.beginTurn(messages.length, {
|
|
2551
|
+
worktreeId: options.worktreeId ?? null,
|
|
2552
|
+
});
|
|
2421
2553
|
|
|
2422
2554
|
// Per-turn abort scope: an {"type":"interrupt"} from the pump above
|
|
2423
2555
|
// aborts THIS turn (LLM fetch included — the signal reaches chatWithTools)
|
|
@@ -2488,6 +2620,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
2488
2620
|
nextToolUseId,
|
|
2489
2621
|
sideEffects,
|
|
2490
2622
|
turnNumber: turns,
|
|
2623
|
+
turnBindingFeed,
|
|
2491
2624
|
},
|
|
2492
2625
|
);
|
|
2493
2626
|
} catch (err) {
|
|
@@ -2531,6 +2664,13 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
2531
2664
|
});
|
|
2532
2665
|
sawError = true;
|
|
2533
2666
|
continue;
|
|
2667
|
+
} finally {
|
|
2668
|
+
persistTurnBindingLog();
|
|
2669
|
+
}
|
|
2670
|
+
if (turnBindingPersistenceError) {
|
|
2671
|
+
emitTurnBindingFailure();
|
|
2672
|
+
sawError = true;
|
|
2673
|
+
break;
|
|
2534
2674
|
}
|
|
2535
2675
|
currentAbort = null;
|
|
2536
2676
|
|