chainlesschain 0.165.9 → 0.166.0
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/completions/_cc +2 -1
- package/completions/cc.bash +2 -2
- package/completions/cc.fish +2 -2
- package/completions/cc.ps1 +2 -1
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-DxwjrnEA.js → AIOps-D5_pwQ7M.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-JdbYowzn.js → ActionButton-DgoYO32D.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C06GCahv.js → Analytics-BIpoZ4QH.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BUVEEyi1.js → AppLayout-BJ3XrCEe.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-D_ORzkJd.js → Artifacts-DUFs_izZ.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-8o_Y5ejG.js → Audit-BZdPwwfL.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DrkHd5VU.js → BackgroundAgents-DK9Fn-8B.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CKBZCX11.js → Backup-Bd6bOhV_.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C8HkI399.js → BaseInput-CRnWlXrd.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-zb2H2Wux.js → Chat-D1G_yEl6.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BDcNpU_F.js → ChatBubbleRenderer-DDAlEtm-.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-MwPtQmNz.js → Checkbox-CP35SH7R.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3p5QCkFK.js → Codegen-DugeL4pj.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BPLlhn52.js → Col-DPuJsvKg.js} +1 -1
- package/src/assets/web-panel/assets/{Community-C-3dBNOM.js → Community-CZqNjNNp.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BUcy-F_b.js → Compact-eeyIPEAb.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CI8NxT8m.js → Compliance-MGzTw87k.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DaDEBS-Q.js → Cowork-D3goVIA5.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BSv9HXu5.js → Cron-9HA8hLLG.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-yuiKlJmv.js → Crosschain-DCSOX3l6.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D6LfldB0.js → DID-ji3lN5Bc.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CBhLqG61.js → Dashboard-DlZvF1E5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BG8pIl3O.js → Dropdown-2FyZcow9.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CY92VIE6.js → EmailListRenderer-qH2bSpSY.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-1Q_ehauM.js → FamilyGuardDashboard-Bfx47wZj.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D3XirJIU.js → Federation-DhGcZbIv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DfI9UuGZ.js → FormItemContext-BMgi9_YQ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-IeCZk2m7.js → GenericCardRenderer-DugMac-4.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DzK4IJAZ.js → Git-D943BpH-.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BHf5KfmQ.js → Governance-DZ66a8Aa.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BmaxSALS.js → Inference-B00Mp2tN.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BgePFLA9.js → KnowledgeGraph-BdwXXO2Q.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-nqgxpOc4.js → Logs-_MVXyYWc.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-hOTeNyi7.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Bu8to-86.js → Marketplace-CxeHRsnO.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BFWTzEk0.js → McpTools-BOieTxZ4.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-B026G2GA.js → Memory-BQD26CM3.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CM2Eeg6I.js → MobileBridge-CPJ8ZlvR.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B79Ob3hy.js → MobileProjects-D7qRVu0y.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CxevSZuV.js → Mtc-BOyndGuk.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-X396cPEX.js → MtcAudit-CXQ_8ZoR.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-HWIB_aZ1.js → Multisig-32Oh9KbH.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-gnYD1Xwy.js → NLProgramming-CFAd00CX.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Dn2Gf_J1.js → Notes-BBCY_qJA.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BRKBQ5rq.js → NotificationSettings-uaQ0PdmW.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dy___Cgo.js → OrderTableRenderer-kpBxJ-SG.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-cqfccxnt.js → Organization-CmFn7Oeo.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CSX4FHN3.js → Overflow-R_avat3a.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Bwa4aghp.js → P2P-BIhLFmkY.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dhszyjig.js → PdhVaultBrowser-DTJjk-_i.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CXzTyk6e.js → Permissions-CkzYv8Yb.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CFCAAJbb.js → PersonalDataHub--0QvN2Nz.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-BPJbx6xt.js → Pipeline-Bfw5gQCH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BbiXvEHz.js → Privacy-DicjKfax.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-s7-wSD.js → ProjectInit-DUl_ykBp.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dyug206y.js → ProjectSettings-CPYDvN8a.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BtAI5Fwo.js → Projects-DnZ9_fAr.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-JpIPmz6N.js → Providers-BX1jP31w.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CZdoN64A.js → QrScannerModal-COab5qUl.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BwvnoRIT.js → QuickAsk-CdKQ_JZ0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-ASMlMQrD.js → Recommend-BPFHg8pK.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BiJzKFKi.js → RemoteSession-CqrfTJvO.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-C80TWLIO.js → Reputation-DM-l8Pb3.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C1VNmjmX.js → Row-CKGBTeCw.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-T2MyGUlG.js → RssFeed-Wb0YHIkc.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DXM6O83L.js → Search-DiBjxKv-.js} +1 -1
- package/src/assets/web-panel/assets/{Security-nv69kU7W.js → Security-BAVEmbWP.js} +4 -4
- package/src/assets/web-panel/assets/{Services-AyHWe7Qj.js → Services-A_a3TIRn.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D6PWLxFI.js → Skeleton-CloHJ-I7.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BqVeOmNQ.js → Skills-CkvV5hKa.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BegIPl9M.js → Sla-BYozxxsV.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CKJn3xk-.js → SpeechSettings-CWianiZF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Th4cnAMl.js → SyncSettings-BR4G75do.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BuKfBroh.js → Tasks-D6gyUVrM.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CMuWN8Ac.js → Templates-DbfYIRzT.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C4je8E8Q.js → Tenant-BHutvukT.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-agJESOcO.js → Terminal-CYEiy2wJ.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D2qDuVZ_.js → TimelineRenderer-D9s1AxfE.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Cvmn99rH.js → Tokens-ij9vxnoy.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D_QYIHH8.js → Trigger-Dq_DCVeQ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BmzvQLnX.js → Trust-Bs6wdfoH.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-D_LopHwH.js → UkeySign-CLazyr5t.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DoWl3PhQ.js → VideoEditing-viWotIHA.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-5NYcAkxe.js → Wallet-DlZ-A78d.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-B-_Q3ewp.js → WebAuthn-DSmOND3Y.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-CHET-gi2.js → WorkflowEditor-BempLOJi.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C6nvb-n6.js → chat-CyJzf4JI.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CTDE9LTX.js → colors-C62q7WrL.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-KnemdF8D.js → compact-item-7dYM1KW3.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DUJR3Uek.js → createContext-CYTy7z2Q.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BdTg_AMF.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CFNTaby8.js → hasIn-DHoclUqm.js} +1 -1
- package/src/assets/web-panel/assets/{index-65HO56Bv.js → index-2C8Xx2UO.js} +1 -1
- package/src/assets/web-panel/assets/{index-ox6mIwE2.js → index-3pI7AfmD.js} +1 -1
- package/src/assets/web-panel/assets/{index-JGTiDeOW.js → index-4gWv-Ubg.js} +1 -1
- package/src/assets/web-panel/assets/{index-rVDD7AAR.js → index-94ktimZG.js} +1 -1
- package/src/assets/web-panel/assets/{index-OmuhFYKE.js → index-9uR7rc9a.js} +1 -1
- package/src/assets/web-panel/assets/index-B6WvFV4N.js +1 -0
- package/src/assets/web-panel/assets/index-BKgfptzA.js +1 -0
- package/src/assets/web-panel/assets/{index-CquKnw5r.js → index-BMRmxFU_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzReoLyh.js → index-BMxvWauP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DP-PbO11.js → index-BghdJ87W.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSD9_I2_.js → index-Bzz0aJ86.js} +1 -1
- package/src/assets/web-panel/assets/{index-DF1Y0Vuw.js → index-C2efftJb.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8RtbSkV.js → index-C5YWYlW9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CHfUdnJH.js → index-CVTnruDt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwZM1rod.js → index-CW86F2Zw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyEtHvEE.js → index-CZRBvN6i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ceu92a8D.js → index-CjeViaU-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJyPeBBk.js → index-CnEZ4ZHj.js} +3 -3
- package/src/assets/web-panel/assets/{index-BdiC4LOs.js → index-D6BC4mmz.js} +1 -1
- package/src/assets/web-panel/assets/{index-k6uorYmf.js → index-DA-_807C.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqPpJkte.js → index-DAYanuSk.js} +1 -1
- package/src/assets/web-panel/assets/{index-NtAcnJYW.js → index-DHgSRimK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Wlc7o_C8.js → index-DJzZaPlb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKvBF3Ov.js → index-DK84lAS0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcF0D7UZ.js → index-DKdv8cju.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnLpuTAU.js → index-DNLD3nZY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZgCRMxk.js → index-DOFAwpUK.js} +1 -1
- package/src/assets/web-panel/assets/{index-v-mtxmWH.js → index-DYKwGrAn.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc7dbl_p.js → index-DghnbMlY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuizJo2E.js → index-DhJjifZe.js} +1 -1
- package/src/assets/web-panel/assets/{index-NUR0If7O.js → index-DkczsbiP.js} +1 -1
- package/src/assets/web-panel/assets/{index-u0h6QqVV.js → index-DvEtHpLc.js} +1 -1
- package/src/assets/web-panel/assets/{index-bxTPfdri.js → index-Dyq9tK5u.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfJzS3Qa.js → index-LK1MZoBj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DF-fcTvQ.js → index-NuNCstRO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXWg1crv.js → index-QXtrdrub.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTetEUWd.js → index-XrsAHHQN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHqSqnW7.js → index-ZXYp76IB.js} +1 -1
- package/src/assets/web-panel/assets/{index-C96N9xeL.js → index-lHfNxiB5.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-qEt8dI-t.js → initDefaultProps-CwCl5sFQ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DetKPWy-.js → motion-BFJFy70N.js} +1 -1
- package/src/assets/web-panel/assets/{move-BeNYeDV2.js → move-ByrtVGq8.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C0q0MK_T.js → mtc-parser-BFiH1mT3.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DLKkrKpE.js → omit-bdvgpivj.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bw1tWsY8.js → pickAttrs-BlfuuE3s.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DeesBLbd.js → placementArrow-t2SC3DMy.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-bqLhxHhn.js → responsiveObserve-CoEXv2Li.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DGCXRoQm.js → slide-BfXCf0Fu.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-H-re3Egl.js → statusUtils-DNsdyOuS.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BXg4qGX_.js → styleChecker-9aHukmoG.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CKLb2KA6.js → useFlexGapSupport-dTkPxzHN.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CTAhHeyw.js → useFs-DINwfItP.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BQ4pDh5g.js → usePersonalDataHub-Bj_RriBV.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-jrSK6j4j.js → vnode-BzRAZrTc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-fOQE24pw.js → zoom-CamPjkeK.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/command-manifest.json +1 -1
- package/src/commands/agent.js +1 -1
- package/src/commands/graph.js +100 -0
- package/src/commands/mcp.js +49 -21
- package/src/commands/orchestrate.js +85 -18
- package/src/commands/serve.js +39 -0
- package/src/commands/team.js +2 -0
- package/src/data/changelog.json +26 -0
- package/src/generated/cc-agent-protocol.schema.json +1964 -0
- package/src/lib/agent-sandbox.js +4 -7
- package/src/lib/app-server/bounded-queue.js +87 -0
- package/src/lib/app-server/cli-agent-kernel-adapter.js +309 -0
- package/src/lib/app-server/protocol.js +125 -0
- package/src/lib/app-server/rollout-store.js +603 -0
- package/src/lib/app-server/server.js +922 -0
- package/src/lib/app-server/sqlite-rollout-store.js +315 -0
- package/src/lib/app-server/stdio-transport.js +118 -0
- package/src/lib/codex-app-server-adapter.js +249 -0
- package/src/lib/graph-kernel/adapters.js +238 -0
- package/src/lib/graph-kernel/compiler.js +911 -0
- package/src/lib/graph-kernel/eval.js +207 -0
- package/src/lib/graph-kernel/event-store.js +101 -0
- package/src/lib/graph-kernel/index.js +7 -0
- package/src/lib/graph-kernel/runtime.js +2660 -0
- package/src/lib/graph-kernel/trace-reducer.js +315 -0
- package/src/lib/graph-kernel/trigger-adapter.js +183 -0
- package/src/lib/orchestrator.js +2 -1
- package/src/lib/record-replay/index.js +1 -0
- package/src/lib/record-replay/skill-recorder.js +386 -0
- package/src/lib/webhook-security.js +159 -0
- package/src/repl/agent-repl.js +5 -4
- package/src/repl/prompt-interactions.js +11 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-xkX6izBt.js +0 -1
- package/src/assets/web-panel/assets/devWarning-1DqLH1KX.js +0 -1
- package/src/assets/web-panel/assets/index-BhJtcPWo.js +0 -1
- package/src/assets/web-panel/assets/index-Cq8MWPDT.js +0 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ROLLOUT_STORE_SCHEMA,
|
|
8
|
+
ROLLOUT_STORE_VERSION,
|
|
9
|
+
_rolloutStoreInternals,
|
|
10
|
+
} from "./rollout-store.js";
|
|
11
|
+
|
|
12
|
+
const { buildRecord, canonicalJson, projectThread, verifyRecords } =
|
|
13
|
+
_rolloutStoreInternals;
|
|
14
|
+
|
|
15
|
+
let DatabaseSync = null;
|
|
16
|
+
try {
|
|
17
|
+
({ DatabaseSync } = createRequire(import.meta.url)("node:sqlite"));
|
|
18
|
+
} catch {
|
|
19
|
+
// node:sqlite is optional on the minimum supported Node 22.12 runtime.
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function sqliteError(code, message, cause = null) {
|
|
23
|
+
const error = new Error(message);
|
|
24
|
+
error.name = "SqliteRolloutStoreError";
|
|
25
|
+
error.code = code;
|
|
26
|
+
if (cause) error.cause = cause;
|
|
27
|
+
return error;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function clone(value) {
|
|
31
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function recordThreadId(input) {
|
|
35
|
+
return buildRecord(
|
|
36
|
+
{ threadId: input, eventType: "validation", payload: null },
|
|
37
|
+
null,
|
|
38
|
+
Date.now,
|
|
39
|
+
).thread_id;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function sqliteRolloutStoreAvailable() {
|
|
43
|
+
return typeof DatabaseSync === "function";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class SqliteRolloutStore {
|
|
47
|
+
constructor({ filename = ":memory:", now = Date.now, database = null } = {}) {
|
|
48
|
+
this.filename = filename === ":memory:" ? filename : path.resolve(filename);
|
|
49
|
+
this.now = now;
|
|
50
|
+
if (this.filename !== ":memory:") {
|
|
51
|
+
fs.mkdirSync(path.dirname(this.filename), { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
if (!database && !sqliteRolloutStoreAvailable()) {
|
|
54
|
+
throw sqliteError(
|
|
55
|
+
"CC_ROLLOUT_SQLITE_UNAVAILABLE",
|
|
56
|
+
"SQLite rollout storage requires a Node runtime with node:sqlite",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
this.db = database || new DatabaseSync(this.filename);
|
|
60
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
61
|
+
this.db.exec("PRAGMA synchronous = FULL");
|
|
62
|
+
this.db.exec("PRAGMA foreign_keys = ON");
|
|
63
|
+
this.db.exec(`
|
|
64
|
+
CREATE TABLE IF NOT EXISTS cc_rollout_meta (
|
|
65
|
+
key TEXT PRIMARY KEY,
|
|
66
|
+
value TEXT NOT NULL
|
|
67
|
+
) STRICT;
|
|
68
|
+
CREATE TABLE IF NOT EXISTS cc_rollout_events (
|
|
69
|
+
thread_id TEXT NOT NULL,
|
|
70
|
+
event_seq INTEGER NOT NULL,
|
|
71
|
+
idempotency_key TEXT,
|
|
72
|
+
event_json TEXT NOT NULL,
|
|
73
|
+
PRIMARY KEY (thread_id, event_seq),
|
|
74
|
+
UNIQUE (thread_id, idempotency_key)
|
|
75
|
+
) STRICT;
|
|
76
|
+
CREATE INDEX IF NOT EXISTS cc_rollout_events_thread
|
|
77
|
+
ON cc_rollout_events(thread_id, event_seq);
|
|
78
|
+
`);
|
|
79
|
+
this.db
|
|
80
|
+
.prepare(
|
|
81
|
+
"INSERT OR IGNORE INTO cc_rollout_meta(key, value) VALUES ('schema_version', ?)",
|
|
82
|
+
)
|
|
83
|
+
.run(String(ROLLOUT_STORE_VERSION));
|
|
84
|
+
const version = Number(
|
|
85
|
+
this.db
|
|
86
|
+
.prepare(
|
|
87
|
+
"SELECT value FROM cc_rollout_meta WHERE key = 'schema_version'",
|
|
88
|
+
)
|
|
89
|
+
.get()?.value,
|
|
90
|
+
);
|
|
91
|
+
if (version !== ROLLOUT_STORE_VERSION) {
|
|
92
|
+
throw sqliteError(
|
|
93
|
+
"CC_ROLLOUT_VERSION_UNSUPPORTED",
|
|
94
|
+
`SQLite rollout schema version ${version} is unsupported`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_read(threadId, { allowMissing = false } = {}) {
|
|
100
|
+
const id = recordThreadId(threadId);
|
|
101
|
+
const rows = this.db
|
|
102
|
+
.prepare(
|
|
103
|
+
"SELECT event_json FROM cc_rollout_events WHERE thread_id = ? ORDER BY event_seq",
|
|
104
|
+
)
|
|
105
|
+
.all(id);
|
|
106
|
+
if (!rows.length && !allowMissing) {
|
|
107
|
+
throw sqliteError(
|
|
108
|
+
"CC_ROLLOUT_THREAD_NOT_FOUND",
|
|
109
|
+
`thread does not exist: ${id}`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const records = rows.map((row) => {
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(row.event_json);
|
|
115
|
+
} catch (cause) {
|
|
116
|
+
throw sqliteError(
|
|
117
|
+
"CC_ROLLOUT_CORRUPT",
|
|
118
|
+
`rollout contains invalid JSON: ${id}`,
|
|
119
|
+
cause,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
try {
|
|
124
|
+
return verifyRecords(records, id);
|
|
125
|
+
} catch (cause) {
|
|
126
|
+
throw sqliteError("CC_ROLLOUT_CORRUPT", cause.message, cause);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_transaction(operation) {
|
|
131
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
132
|
+
try {
|
|
133
|
+
const result = operation();
|
|
134
|
+
this.db.exec("COMMIT");
|
|
135
|
+
return result;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
try {
|
|
138
|
+
this.db.exec("ROLLBACK");
|
|
139
|
+
} catch {
|
|
140
|
+
// Preserve the operation error; rollback failure is secondary.
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
start({
|
|
147
|
+
threadId = randomUUID(),
|
|
148
|
+
title = null,
|
|
149
|
+
parentThreadId = null,
|
|
150
|
+
metadata = {},
|
|
151
|
+
} = {}) {
|
|
152
|
+
const id = recordThreadId(threadId);
|
|
153
|
+
return this._transaction(() => {
|
|
154
|
+
const existing = this._read(id, { allowMissing: true });
|
|
155
|
+
if (existing.length) return projectThread(existing);
|
|
156
|
+
const record = buildRecord(
|
|
157
|
+
{
|
|
158
|
+
threadId: id,
|
|
159
|
+
eventType: "thread.started",
|
|
160
|
+
idempotencyKey: `thread:${id}`,
|
|
161
|
+
payload: { title, parentThreadId, metadata },
|
|
162
|
+
},
|
|
163
|
+
null,
|
|
164
|
+
this.now,
|
|
165
|
+
);
|
|
166
|
+
this.db
|
|
167
|
+
.prepare(
|
|
168
|
+
"INSERT INTO cc_rollout_events(thread_id, event_seq, idempotency_key, event_json) VALUES (?, ?, ?, ?)",
|
|
169
|
+
)
|
|
170
|
+
.run(
|
|
171
|
+
id,
|
|
172
|
+
record.event_seq,
|
|
173
|
+
record.idempotency_key,
|
|
174
|
+
JSON.stringify(record),
|
|
175
|
+
);
|
|
176
|
+
return projectThread([record]);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
append(input) {
|
|
181
|
+
const id = recordThreadId(input?.threadId);
|
|
182
|
+
return this._transaction(() => {
|
|
183
|
+
const records = this._read(id);
|
|
184
|
+
const duplicate = input.idempotencyKey
|
|
185
|
+
? records.find(
|
|
186
|
+
(record) => record.idempotency_key === input.idempotencyKey,
|
|
187
|
+
)
|
|
188
|
+
: null;
|
|
189
|
+
if (duplicate) {
|
|
190
|
+
if (
|
|
191
|
+
duplicate.event_type !== input.eventType ||
|
|
192
|
+
canonicalJson(duplicate.payload) !==
|
|
193
|
+
canonicalJson(input.payload ?? null)
|
|
194
|
+
) {
|
|
195
|
+
throw sqliteError(
|
|
196
|
+
"CC_ROLLOUT_IDEMPOTENCY_CONFLICT",
|
|
197
|
+
`idempotency key was reused with different content: ${input.idempotencyKey}`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return clone(duplicate);
|
|
201
|
+
}
|
|
202
|
+
const record = buildRecord(input, records.at(-1), this.now);
|
|
203
|
+
this.db
|
|
204
|
+
.prepare(
|
|
205
|
+
"INSERT INTO cc_rollout_events(thread_id, event_seq, idempotency_key, event_json) VALUES (?, ?, ?, ?)",
|
|
206
|
+
)
|
|
207
|
+
.run(
|
|
208
|
+
id,
|
|
209
|
+
record.event_seq,
|
|
210
|
+
record.idempotency_key,
|
|
211
|
+
JSON.stringify(record),
|
|
212
|
+
);
|
|
213
|
+
return clone(record);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
read(threadId, { afterSeq = 0, limit = 10_000 } = {}) {
|
|
218
|
+
return clone(
|
|
219
|
+
this._read(threadId)
|
|
220
|
+
.filter((record) => record.event_seq > Number(afterSeq || 0))
|
|
221
|
+
.slice(0, Math.max(1, Math.min(100_000, Number(limit) || 10_000))),
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
resume(threadId) {
|
|
226
|
+
return projectThread(this._read(threadId));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
list({ includeArchived = false, limit = 100 } = {}) {
|
|
230
|
+
const ids = this.db
|
|
231
|
+
.prepare(
|
|
232
|
+
"SELECT DISTINCT thread_id FROM cc_rollout_events ORDER BY thread_id",
|
|
233
|
+
)
|
|
234
|
+
.all()
|
|
235
|
+
.map((row) => row.thread_id);
|
|
236
|
+
return ids
|
|
237
|
+
.map((threadId) => this.resume(threadId))
|
|
238
|
+
.filter((thread) => includeArchived || thread.status !== "archived")
|
|
239
|
+
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))
|
|
240
|
+
.slice(0, Math.max(1, Number(limit) || 100));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
forkThread(sourceThreadId, { threadId = randomUUID(), title = null } = {}) {
|
|
244
|
+
const source = this.resume(sourceThreadId);
|
|
245
|
+
const target = this.start({
|
|
246
|
+
threadId,
|
|
247
|
+
title: title ?? source.title,
|
|
248
|
+
parentThreadId: source.id,
|
|
249
|
+
metadata: source.metadata,
|
|
250
|
+
});
|
|
251
|
+
this.append({
|
|
252
|
+
threadId: target.id,
|
|
253
|
+
eventType: "thread.forked",
|
|
254
|
+
idempotencyKey: `fork:${source.id}:${source.headHash}`,
|
|
255
|
+
payload: {
|
|
256
|
+
sourceThreadId: source.id,
|
|
257
|
+
sourceEventSeq: source.revision,
|
|
258
|
+
sourceHeadHash: source.headHash,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
return this.resume(target.id);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
checkpoint(threadId, payload = {}) {
|
|
265
|
+
return this.append({
|
|
266
|
+
threadId,
|
|
267
|
+
eventType: "rollout.checkpoint",
|
|
268
|
+
idempotencyKey: payload.idempotencyKey || null,
|
|
269
|
+
payload: { ...payload, idempotencyKey: undefined },
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
compact(threadId, options = {}) {
|
|
274
|
+
return this.append({
|
|
275
|
+
threadId,
|
|
276
|
+
eventType: "rollout.compacted",
|
|
277
|
+
idempotencyKey: options.idempotencyKey || null,
|
|
278
|
+
payload: {
|
|
279
|
+
summary: String(options.summary || ""),
|
|
280
|
+
retainedState: options.retainedState || {},
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
archive(threadId) {
|
|
286
|
+
this.append({
|
|
287
|
+
threadId,
|
|
288
|
+
eventType: "thread.archived",
|
|
289
|
+
idempotencyKey: `archive:${threadId}`,
|
|
290
|
+
payload: {},
|
|
291
|
+
});
|
|
292
|
+
return this.resume(threadId);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
migrate({ fromVersion = 1, toVersion = 1, dryRun = true } = {}) {
|
|
296
|
+
if (Number(fromVersion) !== 1 || Number(toVersion) !== 1) {
|
|
297
|
+
throw sqliteError(
|
|
298
|
+
"CC_ROLLOUT_VERSION_UNSUPPORTED",
|
|
299
|
+
"SQLite rollout adapter currently supports schema v1 only",
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return Object.freeze({
|
|
303
|
+
schema: ROLLOUT_STORE_SCHEMA,
|
|
304
|
+
fromVersion: 1,
|
|
305
|
+
toVersion: 1,
|
|
306
|
+
dryRun: dryRun !== false,
|
|
307
|
+
changes: 0,
|
|
308
|
+
backupRequired: false,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
close() {
|
|
313
|
+
this.db.close();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { once } from "node:events";
|
|
3
|
+
import { BoundedAsyncQueue, QueueOverloadedError } from "./bounded-queue.js";
|
|
4
|
+
import { CcAppServer } from "./server.js";
|
|
5
|
+
import { JSON_RPC_ERROR, JsonRpcError, rpcError } from "./protocol.js";
|
|
6
|
+
|
|
7
|
+
export class StdioAppServerTransport {
|
|
8
|
+
constructor({
|
|
9
|
+
input = process.stdin,
|
|
10
|
+
output = process.stdout,
|
|
11
|
+
maxLineBytes = 1024 * 1024,
|
|
12
|
+
maxQueuedMessages = 512,
|
|
13
|
+
maxQueuedBytes = 8 * 1024 * 1024,
|
|
14
|
+
} = {}) {
|
|
15
|
+
this.input = input;
|
|
16
|
+
this.output = output;
|
|
17
|
+
this.maxLineBytes = maxLineBytes;
|
|
18
|
+
this.queue = new BoundedAsyncQueue({
|
|
19
|
+
maxItems: maxQueuedMessages,
|
|
20
|
+
maxBytes: maxQueuedBytes,
|
|
21
|
+
sizeOf: (line) => Buffer.byteLength(line, "utf8"),
|
|
22
|
+
});
|
|
23
|
+
this.closed = false;
|
|
24
|
+
this.failure = null;
|
|
25
|
+
this.pump = this._pump();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async _pump() {
|
|
29
|
+
try {
|
|
30
|
+
for await (const line of this.queue) {
|
|
31
|
+
if (!this.output.write(line)) await once(this.output, "drain");
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
this.failure = error;
|
|
35
|
+
this.queue.close(error);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
send(message) {
|
|
41
|
+
if (this.failure) return Promise.reject(this.failure);
|
|
42
|
+
if (this.closed)
|
|
43
|
+
return Promise.reject(new Error("stdio transport is closed"));
|
|
44
|
+
const line = `${JSON.stringify(message)}\n`;
|
|
45
|
+
try {
|
|
46
|
+
this.queue.push(line);
|
|
47
|
+
return Promise.resolve();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof QueueOverloadedError) {
|
|
50
|
+
this.failure = error;
|
|
51
|
+
}
|
|
52
|
+
return Promise.reject(error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async close() {
|
|
57
|
+
if (this.closed) return;
|
|
58
|
+
this.closed = true;
|
|
59
|
+
this.queue.close();
|
|
60
|
+
await this.pump;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function runStdioAppServer(options = {}) {
|
|
65
|
+
const input = options.input || process.stdin;
|
|
66
|
+
const transport = options.transport || new StdioAppServerTransport(options);
|
|
67
|
+
const server =
|
|
68
|
+
options.server ||
|
|
69
|
+
new CcAppServer({
|
|
70
|
+
...options,
|
|
71
|
+
send: (message) => transport.send(message),
|
|
72
|
+
});
|
|
73
|
+
const readline = createInterface({ input, crlfDelay: Infinity });
|
|
74
|
+
const pending = new Set();
|
|
75
|
+
let fatal = null;
|
|
76
|
+
try {
|
|
77
|
+
for await (const line of readline) {
|
|
78
|
+
if (Buffer.byteLength(line, "utf8") > transport.maxLineBytes) {
|
|
79
|
+
await transport.send(
|
|
80
|
+
rpcError(
|
|
81
|
+
null,
|
|
82
|
+
new JsonRpcError(
|
|
83
|
+
JSON_RPC_ERROR.INVALID_REQUEST,
|
|
84
|
+
"JSON-RPC line exceeds the transport limit",
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
let message;
|
|
91
|
+
try {
|
|
92
|
+
message = JSON.parse(line);
|
|
93
|
+
} catch {
|
|
94
|
+
await transport.send(
|
|
95
|
+
rpcError(
|
|
96
|
+
null,
|
|
97
|
+
new JsonRpcError(JSON_RPC_ERROR.PARSE_ERROR, "Invalid JSON"),
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const request = Promise.resolve(server.receive(message)).catch(
|
|
103
|
+
(error) => {
|
|
104
|
+
fatal ||= error;
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
pending.add(request);
|
|
108
|
+
request.finally(() => pending.delete(request));
|
|
109
|
+
}
|
|
110
|
+
await Promise.all(pending);
|
|
111
|
+
} finally {
|
|
112
|
+
readline.close();
|
|
113
|
+
await server.close();
|
|
114
|
+
await transport.close();
|
|
115
|
+
}
|
|
116
|
+
if (fatal) throw fatal;
|
|
117
|
+
return server.status();
|
|
118
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
|
|
3
|
+
export const CODEX_APP_SERVER_PROTOCOL = "codex-app-server-experimental-v1";
|
|
4
|
+
export const CODEX_APP_SERVER_FEATURE_FLAG = "CC_EXPERIMENTAL_CODEX_APP_SERVER";
|
|
5
|
+
|
|
6
|
+
function adapterError(code, message, details = {}) {
|
|
7
|
+
const error = new Error(message);
|
|
8
|
+
error.name = "CodexAppServerAdapterError";
|
|
9
|
+
error.code = code;
|
|
10
|
+
Object.assign(error, details);
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseVersion(value) {
|
|
15
|
+
const match = String(value || "").match(/(\d+)\.(\d+)\.(\d+)/u);
|
|
16
|
+
return match ? match.slice(1).map(Number) : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function compareVersion(left, right) {
|
|
20
|
+
for (let index = 0; index < 3; index += 1) {
|
|
21
|
+
if (left[index] !== right[index]) return left[index] - right[index];
|
|
22
|
+
}
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isCodexAppServerVersionCompatible(version, matrix = []) {
|
|
27
|
+
const parsed = parseVersion(version);
|
|
28
|
+
if (!parsed || !Array.isArray(matrix) || matrix.length === 0) return false;
|
|
29
|
+
return matrix.some((range) => {
|
|
30
|
+
const minimum = parseVersion(range.min);
|
|
31
|
+
const maximum = parseVersion(range.maxExclusive);
|
|
32
|
+
return (
|
|
33
|
+
minimum &&
|
|
34
|
+
maximum &&
|
|
35
|
+
compareVersion(parsed, minimum) >= 0 &&
|
|
36
|
+
compareVersion(parsed, maximum) < 0
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function projectNotification(notification) {
|
|
42
|
+
const method = notification?.method;
|
|
43
|
+
const params = notification?.params || {};
|
|
44
|
+
if (method === "thread/started") {
|
|
45
|
+
return { method: "thread/updated", params: { thread: params.thread } };
|
|
46
|
+
}
|
|
47
|
+
if (method === "turn/started") {
|
|
48
|
+
return { method: "turn/started", params: { turn: params.turn } };
|
|
49
|
+
}
|
|
50
|
+
if (["item/started", "item/updated", "item/completed"].includes(method)) {
|
|
51
|
+
const item = params.item || {};
|
|
52
|
+
const status = method === "item/completed" ? "completed" : "streaming";
|
|
53
|
+
return {
|
|
54
|
+
method: method === "item/updated" ? "item/delta" : method,
|
|
55
|
+
params: {
|
|
56
|
+
item: {
|
|
57
|
+
id: item.id,
|
|
58
|
+
kind:
|
|
59
|
+
item.type === "agent_message"
|
|
60
|
+
? "assistant_message"
|
|
61
|
+
: item.type === "reasoning"
|
|
62
|
+
? "reasoning"
|
|
63
|
+
: item.type === "command_execution" ||
|
|
64
|
+
item.type === "file_change"
|
|
65
|
+
? "tool"
|
|
66
|
+
: "artifact",
|
|
67
|
+
status,
|
|
68
|
+
content: item.text ?? item.content ?? item,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
output:
|
|
72
|
+
method === "item/completed" && item.type === "agent_message"
|
|
73
|
+
? String(item.text || "")
|
|
74
|
+
: "",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (method === "turn/completed" || method === "turn/failed") {
|
|
78
|
+
const failed = method === "turn/failed";
|
|
79
|
+
return {
|
|
80
|
+
method: "turn/completed",
|
|
81
|
+
params: {
|
|
82
|
+
turn: {
|
|
83
|
+
...params.turn,
|
|
84
|
+
status: failed ? "failed" : "completed",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
terminal: failed ? "failed" : "completed",
|
|
88
|
+
error: failed ? params.error || "Codex App Server turn failed" : null,
|
|
89
|
+
usage: params.usage || null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return { unknownMethod: String(method || "<missing>") };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Feature-gated bridge for the experimental upstream Codex App Server.
|
|
97
|
+
* It is deliberately non-authoritative and falls back to the stable
|
|
98
|
+
* `codex exec --json` adapter before admission. Once a persistent turn is
|
|
99
|
+
* admitted it never starts a duplicate fallback execution.
|
|
100
|
+
*/
|
|
101
|
+
export class CodexAppServerAdapter extends EventEmitter {
|
|
102
|
+
constructor({
|
|
103
|
+
client,
|
|
104
|
+
fallback,
|
|
105
|
+
upstreamVersion,
|
|
106
|
+
compatibilityMatrix = [],
|
|
107
|
+
enabled = process.env[CODEX_APP_SERVER_FEATURE_FLAG] === "1",
|
|
108
|
+
timeoutMs = 120_000,
|
|
109
|
+
} = {}) {
|
|
110
|
+
super();
|
|
111
|
+
this.client = client;
|
|
112
|
+
this.fallback = fallback;
|
|
113
|
+
this.upstreamVersion = upstreamVersion;
|
|
114
|
+
this.compatibilityMatrix = compatibilityMatrix;
|
|
115
|
+
this.enabled = enabled === true;
|
|
116
|
+
this.timeoutMs = timeoutMs;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
runtimeClaims() {
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
protocol: CODEX_APP_SERVER_PROTOCOL,
|
|
122
|
+
execution: "real",
|
|
123
|
+
persistence: "provider_managed",
|
|
124
|
+
stability: "experimental",
|
|
125
|
+
authoritative: false,
|
|
126
|
+
productionCritical: false,
|
|
127
|
+
featureFlag: CODEX_APP_SERVER_FEATURE_FLAG,
|
|
128
|
+
enabled: this.enabled,
|
|
129
|
+
versionCompatible: isCodexAppServerVersionCompatible(
|
|
130
|
+
this.upstreamVersion,
|
|
131
|
+
this.compatibilityMatrix,
|
|
132
|
+
),
|
|
133
|
+
fallback: "codex-exec-jsonl-v1",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async execute({ prompt, threadId = null, ...options } = {}) {
|
|
138
|
+
const claims = this.runtimeClaims();
|
|
139
|
+
if (!claims.enabled)
|
|
140
|
+
return this._fallback(prompt, options, "feature_disabled");
|
|
141
|
+
if (!claims.versionCompatible) {
|
|
142
|
+
return this._fallback(prompt, options, "version_incompatible");
|
|
143
|
+
}
|
|
144
|
+
if (!this.client || typeof this.client.request !== "function") {
|
|
145
|
+
return this._fallback(prompt, options, "app_server_unavailable");
|
|
146
|
+
}
|
|
147
|
+
const notifications = [];
|
|
148
|
+
const unknownMethods = new Set();
|
|
149
|
+
let output = "";
|
|
150
|
+
let terminalResolve;
|
|
151
|
+
let terminalReject;
|
|
152
|
+
const terminal = new Promise((resolve, reject) => {
|
|
153
|
+
terminalResolve = resolve;
|
|
154
|
+
terminalReject = reject;
|
|
155
|
+
});
|
|
156
|
+
const timer = setTimeout(
|
|
157
|
+
() =>
|
|
158
|
+
terminalReject(
|
|
159
|
+
adapterError(
|
|
160
|
+
"CC_CODEX_APP_SERVER_TIMEOUT",
|
|
161
|
+
"Codex App Server turn timed out after admission",
|
|
162
|
+
),
|
|
163
|
+
),
|
|
164
|
+
this.timeoutMs,
|
|
165
|
+
);
|
|
166
|
+
timer.unref?.();
|
|
167
|
+
const onNotification = (notification) => {
|
|
168
|
+
const projected = projectNotification(notification);
|
|
169
|
+
if (projected.unknownMethod) unknownMethods.add(projected.unknownMethod);
|
|
170
|
+
else {
|
|
171
|
+
notifications.push(projected);
|
|
172
|
+
this.emit("notification", projected);
|
|
173
|
+
}
|
|
174
|
+
if (projected.output) output = projected.output;
|
|
175
|
+
if (projected.terminal) terminalResolve(projected);
|
|
176
|
+
};
|
|
177
|
+
this.client.on?.("notification", onNotification);
|
|
178
|
+
let admitted = false;
|
|
179
|
+
try {
|
|
180
|
+
if (!this.client.running && typeof this.client.start === "function") {
|
|
181
|
+
await this.client.start();
|
|
182
|
+
}
|
|
183
|
+
let activeThreadId = threadId;
|
|
184
|
+
if (!activeThreadId) {
|
|
185
|
+
const started = await this.client.request("thread/start", {
|
|
186
|
+
ephemeral: false,
|
|
187
|
+
provider: "codex",
|
|
188
|
+
});
|
|
189
|
+
activeThreadId = started?.thread?.id;
|
|
190
|
+
}
|
|
191
|
+
if (!activeThreadId) {
|
|
192
|
+
throw adapterError(
|
|
193
|
+
"CC_CODEX_APP_SERVER_PROTOCOL_FAILED",
|
|
194
|
+
"Codex App Server did not return a thread identity",
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const started = await this.client.request("turn/start", {
|
|
198
|
+
threadId: activeThreadId,
|
|
199
|
+
input: [{ type: "text", text: String(prompt || "") }],
|
|
200
|
+
});
|
|
201
|
+
admitted = true;
|
|
202
|
+
const terminalEvent =
|
|
203
|
+
started?.turn?.status === "completed"
|
|
204
|
+
? { terminal: "completed", usage: started.turn.usage || null }
|
|
205
|
+
: await terminal;
|
|
206
|
+
return Object.freeze({
|
|
207
|
+
protocol: CODEX_APP_SERVER_PROTOCOL,
|
|
208
|
+
threadId: activeThreadId,
|
|
209
|
+
terminal: terminalEvent.terminal,
|
|
210
|
+
output,
|
|
211
|
+
error: terminalEvent.error || null,
|
|
212
|
+
usage: terminalEvent.usage || null,
|
|
213
|
+
notifications: Object.freeze(notifications),
|
|
214
|
+
unknownMethods: Object.freeze([...unknownMethods].sort()),
|
|
215
|
+
fallback: false,
|
|
216
|
+
authoritative: false,
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!admitted) {
|
|
220
|
+
return this._fallback(prompt, options, error?.code || "startup_failed");
|
|
221
|
+
}
|
|
222
|
+
throw adapterError(
|
|
223
|
+
"CC_CODEX_APP_SERVER_FAILED_AFTER_ADMISSION",
|
|
224
|
+
"Codex App Server failed after the turn was admitted; fallback was suppressed to prevent duplicate effects",
|
|
225
|
+
{ cause: error },
|
|
226
|
+
);
|
|
227
|
+
} finally {
|
|
228
|
+
clearTimeout(timer);
|
|
229
|
+
this.client.off?.("notification", onNotification);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async _fallback(prompt, options, reason) {
|
|
234
|
+
if (typeof this.fallback !== "function") {
|
|
235
|
+
throw adapterError(
|
|
236
|
+
"CC_CODEX_APP_SERVER_FALLBACK_UNAVAILABLE",
|
|
237
|
+
`Codex App Server is unavailable and no exec fallback was provided (${reason})`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
const result = await this.fallback({ prompt, ...options });
|
|
241
|
+
return Object.freeze({
|
|
242
|
+
...result,
|
|
243
|
+
protocol: "codex-exec-jsonl-v1",
|
|
244
|
+
fallback: true,
|
|
245
|
+
fallbackReason: reason,
|
|
246
|
+
authoritative: false,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|