@covro/covro 0.5.1-dev.1 → 0.5.1-dev.3
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 +5 -1
- package/README.zh-CN.md +5 -1
- package/dist/codex/adapter.js +3 -1
- package/dist/codex/adapter.js.map +2 -2
- package/dist/codex/mapping.js +10 -1
- package/dist/codex/mapping.js.map +2 -2
- package/dist/server/capacity-retry.js +176 -0
- package/dist/server/capacity-retry.js.map +7 -0
- package/dist/server/db/operations.js +11 -0
- package/dist/server/db/operations.js.map +2 -2
- package/dist/server/github-repository.js +61 -0
- package/dist/server/github-repository.js.map +7 -0
- package/dist/server/http.js +22 -0
- package/dist/server/http.js.map +2 -2
- package/dist/server/projection.js +10 -3
- package/dist/server/projection.js.map +2 -2
- package/dist/server/service.js +152 -5
- package/dist/server/service.js.map +2 -2
- package/dist/shared/turn-errors.js +25 -0
- package/dist/shared/turn-errors.js.map +7 -0
- package/dist/shared/validation.js +2 -0
- package/dist/shared/validation.js.map +2 -2
- package/dist/web/assets/{abnfDiagram-WEYOIA3C-B7ZgLRcz.js → abnfDiagram-WEYOIA3C-ATf52olP.js} +1 -1
- package/dist/web/assets/{arc-l2Y0qpaA.js → arc-ChLnTnZI.js} +1 -1
- package/dist/web/assets/{architecture-WOLXFQ4H-CPMJhOrI.js → architecture-WOLXFQ4H-B2oEg329.js} +1 -1
- package/dist/web/assets/{architectureDiagram-ZHVYKT53-BGyNOKZB.js → architectureDiagram-ZHVYKT53-BzSXU9d0.js} +1 -1
- package/dist/web/assets/{blockDiagram-RJAE7QA3-CFTsFM2C.js → blockDiagram-RJAE7QA3-xhCZ7iUm.js} +1 -1
- package/dist/web/assets/{c4Diagram-XDPPPE54-DePVq6hL.js → c4Diagram-XDPPPE54-DZ8j79Mx.js} +1 -1
- package/dist/web/assets/channel-COC5jFhc.js +1 -0
- package/dist/web/assets/{chunk-2E7WHVMD-BjwXDHKa.js → chunk-2E7WHVMD-y5rSOViW.js} +1 -1
- package/dist/web/assets/{chunk-4EMNKR2T-DfazpkKZ.js → chunk-4EMNKR2T-B7WVoonr.js} +1 -1
- package/dist/web/assets/{chunk-55XCU76K-C8SPf4XM.js → chunk-55XCU76K-D-tlN44X.js} +1 -1
- package/dist/web/assets/{chunk-7S6XSTFO-4drJ38MC.js → chunk-7S6XSTFO-31xOZx-Y.js} +1 -1
- package/dist/web/assets/{chunk-AQDTSKZB-CT94xwkm.js → chunk-AQDTSKZB-CeCjUBmu.js} +1 -1
- package/dist/web/assets/{chunk-DSNLSHJZ-Do8zQFMz.js → chunk-DSNLSHJZ-DgzXTo3_.js} +1 -1
- package/dist/web/assets/{chunk-HAGFOTED-5XuJjFhz.js → chunk-HAGFOTED-Dm22rez0.js} +1 -1
- package/dist/web/assets/{chunk-HNE4Q36P-DlG6wQNV.js → chunk-HNE4Q36P-CnFLQjbZ.js} +1 -1
- package/dist/web/assets/{chunk-NQOKRVIF-D-7GGebB.js → chunk-NQOKRVIF-7DXXp2d_.js} +1 -1
- package/dist/web/assets/{chunk-UHW7PFBP-BTJa6uY3.js → chunk-UHW7PFBP-Bw9-oVzV.js} +1 -1
- package/dist/web/assets/{chunk-VQ5W62QI-DtWEqWoe.js → chunk-VQ5W62QI-C9PHAHIJ.js} +1 -1
- package/dist/web/assets/{chunk-WQQL4CDK-qNZoyGqM.js → chunk-WQQL4CDK-C9nKxc0i.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-DGCXD5QG.js → chunk-XXDRQBXY-DjdTeNXE.js} +1 -1
- package/dist/web/assets/{chunk-YZO67HFG-DwHNq7O3.js → chunk-YZO67HFG-C1z77_dC.js} +1 -1
- package/dist/web/assets/{chunk-ZAUX2LTX-1lvpL_7X.js → chunk-ZAUX2LTX-C7e41NBP.js} +1 -1
- package/dist/web/assets/{chunk-ZCHMQSOR-Dt3EEGC_.js → chunk-ZCHMQSOR-Gv41Qgvd.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-4MYWR3UA-DiM_A5E4.js → classDiagram-v2-4MYWR3UA-PjZDyaSs.js} +1 -1
- package/dist/web/assets/{cose-bilkent-JH36ORCC-B5yeBESI.js → cose-bilkent-JH36ORCC-CvwZK3HN.js} +1 -1
- package/dist/web/assets/{cynefin-EF2NZ3EQ-DepIuM8k.js → cynefin-EF2NZ3EQ-Yn6p93cE.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-WF24UDRP-9NThILAR.js → cynefinDiagram-WF24UDRP-BXa5Guxe.js} +1 -1
- package/dist/web/assets/{dagre-NK4GIZVX-C3KdtDhG.js → dagre-NK4GIZVX-Cnskqb30.js} +1 -1
- package/dist/web/assets/{diagram-CBK3MWTC-v3bh0eI-.js → diagram-CBK3MWTC-K8WEazkZ.js} +1 -1
- package/dist/web/assets/{diagram-I5XIIV2D-B-ICpu9I.js → diagram-I5XIIV2D-CLzouqUh.js} +1 -1
- package/dist/web/assets/{diagram-ODIL2UCW-DpmbfYP-.js → diagram-ODIL2UCW-B_9ysyHU.js} +1 -1
- package/dist/web/assets/{diagram-PFBEJUXE-DjCVTHlN.js → diagram-PFBEJUXE-D1ge5Ebx.js} +1 -1
- package/dist/web/assets/{diagram-RJA3I4VV-BgwHDpSh.js → diagram-RJA3I4VV-DWF6V26r.js} +1 -1
- package/dist/web/assets/{diagram-TYUNFSNR-BSQV5_eI.js → diagram-TYUNFSNR-BNM1pCS4.js} +1 -1
- package/dist/web/assets/{dist-DnjG1c62.js → dist-_uWZFvFF.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-V4P3TTPT-CpfF488c.js → ebnfDiagram-V4P3TTPT-Cge9YHFL.js} +1 -1
- package/dist/web/assets/{elk-QONTBCTQ-DpD_nrIC.js → elk-QONTBCTQ-CY_393oL.js} +1 -1
- package/dist/web/assets/{erDiagram-KYQIGQAA-17q4UWrx.js → erDiagram-KYQIGQAA-CCOr67SS.js} +1 -1
- package/dist/web/assets/{eventmodeling-K75KTNOO-CuFiu_4h.js → eventmodeling-K75KTNOO-BzNNWwRQ.js} +1 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-C8dLq0uV.js +1 -0
- package/dist/web/assets/{ganttDiagram-R7GPNTJY-9ZLwR83Q.js → ganttDiagram-R7GPNTJY-8BewtZrJ.js} +1 -1
- package/dist/web/assets/{gitGraph-VSP46ZUC-DjHWMo_n.js → gitGraph-VSP46ZUC-CrqJbic_.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-qoPQTUpT.js → gitGraphDiagram-PDYL7XHY-Ddr5Q6Is.js} +1 -1
- package/dist/web/assets/index-C_i_PNi6.css +1 -0
- package/dist/web/assets/index-DHENTsqN.js +148 -0
- package/dist/web/assets/{info-OHQRW6UA-B0YOBALB.js → info-OHQRW6UA-DgPvuC2J.js} +1 -1
- package/dist/web/assets/{infoDiagram-MVDRKAX5-DWLetMhi.js → infoDiagram-MVDRKAX5-zlubFwKS.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-D7fHGOrW.js → ishikawaDiagram-HPEHV2GE-DdQfvqVi.js} +1 -1
- package/dist/web/assets/{journeyDiagram-BOSHW7O7-DJJ-UZbG.js → journeyDiagram-BOSHW7O7-DZ4XswBd.js} +1 -1
- package/dist/web/assets/{kanban-definition-FNSA4HC5-zWaq8RKK.js → kanban-definition-FNSA4HC5-B1BQdpKc.js} +1 -1
- package/dist/web/assets/{line-BggZ3vsX.js → line-jQDqs207.js} +1 -1
- package/dist/web/assets/{linear-Cz_-flHq.js → linear-DbYz4fU8.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-C7Kpq-Lh.js → mermaid-parser.core-CY5CVNe8.js} +3 -3
- package/dist/web/assets/{mermaid.core-B-AKvFuX.js → mermaid.core-mETL7nBW.js} +4 -4
- package/dist/web/assets/{mindmap-definition-6OY57UG3-JGOdyl4n.js → mindmap-definition-6OY57UG3-CxgWFLxa.js} +1 -1
- package/dist/web/assets/{packet-JDAUHWVQ-DKCNvhvF.js → packet-JDAUHWVQ-Czsr9kk7.js} +1 -1
- package/dist/web/assets/{pegDiagram-ECIUJNBX-BKKUVsiJ.js → pegDiagram-ECIUJNBX-BqYq-SA-.js} +1 -1
- package/dist/web/assets/{pie-XZMESJXO-Cj1U8HAg.js → pie-XZMESJXO-CRpxJzCy.js} +1 -1
- package/dist/web/assets/{pieDiagram-GNMPY74T-B4gWYmj5.js → pieDiagram-GNMPY74T-Bt2wKk8z.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-D57K4MC3-CzKFFFWA.js → quadrantDiagram-D57K4MC3-VOKqYTgn.js} +1 -1
- package/dist/web/assets/{radar-ABXABTNO-BvW6txnl.js → radar-ABXABTNO-wXmArQH-.js} +1 -1
- package/dist/web/assets/{railroad-I3PHUGI6-CQFUQhrP.js → railroad-I3PHUGI6-CKPK4tQV.js} +1 -1
- package/dist/web/assets/{railroad-abnf-LNEFI6M7-Dp31IQhK.js → railroad-abnf-LNEFI6M7-BLEs3xqV.js} +1 -1
- package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-DDYlK9R4.js → railroad-ebnf-DWYD2UWJ-DS5QZgky.js} +1 -1
- package/dist/web/assets/{railroad-peg-JR7BVNR7-C8-c26bu.js → railroad-peg-JR7BVNR7-DHWXNOSl.js} +1 -1
- package/dist/web/assets/{railroadDiagram-Y72NRBL7-6FaSHHCa.js → railroadDiagram-Y72NRBL7-Cy4eWKH1.js} +1 -1
- package/dist/web/assets/{requirementDiagram-EQYSW5RA-C6Ry03Cr.js → requirementDiagram-EQYSW5RA-Bvbk8wwe.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-SY24JHO5-Cwnb3Qeu.js → sankeyDiagram-SY24JHO5-B4jGjMzu.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-BLEHNHQU-CRvwFUCU.js → sequenceDiagram-BLEHNHQU-BpgUogv_.js} +1 -1
- package/dist/web/assets/{src-DVW4doAe.js → src-DigPeGm7.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-wwkCw23o.js → stateDiagram-v2-A6IESKWZ-tHq7e40Q.js} +1 -1
- package/dist/web/assets/{swimlanes-B3ENRKFQ-ChNg0Cbk.js → swimlanes-B3ENRKFQ-DVUPAaDR.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-jJ6zDGAv.js +8 -0
- package/dist/web/assets/{timeline-definition-JXYKGLGB-BVfl26_t.js → timeline-definition-JXYKGLGB-BHMM9AHV.js} +1 -1
- package/dist/web/assets/{treeView-D4JQ5SDB-DJFvd6yo.js → treeView-D4JQ5SDB-DZ7k_TTS.js} +1 -1
- package/dist/web/assets/{treemap-SAJKECNS-BDiJSu2k.js → treemap-SAJKECNS-C1wGfVb4.js} +1 -1
- package/dist/web/assets/{usecaseDiagram-WPOLBNNX-Bq_3KTOD.js → usecaseDiagram-WPOLBNNX-BTg0K_GO.js} +1 -1
- package/dist/web/assets/{vennDiagram-Q3Y3IDCW-6YGgOzny.js → vennDiagram-Q3Y3IDCW-BTKShPx1.js} +1 -1
- package/dist/web/assets/{wardley-7MLQ67FV-4cm0j7qc.js → wardley-7MLQ67FV-DnYPM5OB.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-IVAIDKWA-BMqtyCjx.js → wardleyDiagram-IVAIDKWA-Dzb67Boa.js} +1 -1
- package/dist/web/assets/{xychartDiagram-4URNHDU4-CFsu67vz.js → xychartDiagram-4URNHDU4-qijltKIO.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +3 -3
- package/dist/web/assets/channel-DiiJLG5R.js +0 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-D63MFFYq.js +0 -1
- package/dist/web/assets/index-CGAKLE5y.js +0 -147
- package/dist/web/assets/index-QZirxrGa.css +0 -1
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-Dww2HRCa.js +0 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/codex/mapping.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n ItemView,\n RunStatus,\n RuntimeSettings,\n Settings,\n ThreadView,\n TurnView,\n} from '../shared/types.js';\nimport { permissionModes } from '../shared/permissions.js';\nimport type { PermissionSelection } from './permissions.js';\nexport { permissionSettings } from './permissions.js';\nimport type { Thread } from './generated/v2/Thread.js';\nimport type { ThreadItem } from './generated/v2/ThreadItem.js';\nimport type { ThreadStatus } from './generated/v2/ThreadStatus.js';\nimport type { Turn } from './generated/v2/Turn.js';\nimport type { ThreadStartParams } from './generated/v2/ThreadStartParams.js';\nimport type { TurnStartParams } from './generated/v2/TurnStartParams.js';\nimport type { ReasoningEffort } from './generated/ReasoningEffort.js';\nimport type { UserInput } from './generated/v2/UserInput.js';\nimport { parseQuestionReplies } from '../shared/async-questions.js';\nimport { imageDataBytes, MAX_IMAGES, MAX_IMAGE_BYTES } from '../shared/images.js';\nimport { CodexError, isRecord } from './transport.js';\n\nexport function taskInput(text: string, images: string[] = []): UserInput[] {\n return [\n ...(text ? [{ type: 'text' as const, text, text_elements: [] }] : []),\n ...images.map((url) => ({ type: 'image' as const, url })),\n ];\n}\n\n/** Codex 0.164.0 reports Fast as priority and standard speed as default. */\nexport function serviceTierSettings(\n tier: string | null | undefined,\n): Pick<RuntimeSettings, 'serviceTier'> {\n if (tier === undefined) return {};\n return { serviceTier: tier === 'priority' ? 'fast' : tier === 'default' ? null : tier };\n}\n\nexport function threadStatus(status: ThreadStatus): RunStatus {\n if (!isRecord(status))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex thread status is malformed', true);\n if (status.type === 'notLoaded') return 'unknown';\n if (status.type === 'idle') return 'idle';\n if (status.type === 'systemError') return 'failed';\n if (status.type !== 'active' || !Array.isArray(status.activeFlags))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex thread status is not supported', true);\n if (status.activeFlags.includes('waitingOnApproval')) return 'waitingApproval';\n if (status.activeFlags.includes('waitingOnUserInput')) return 'waitingInput';\n return 'running';\n}\nexport function mapThread(thread: Thread): ThreadView {\n assertObjectId(thread, 'thread');\n if (typeof thread.cwd !== 'string' || !Array.isArray(thread.turns) || !isRecord(thread.status))\n throw new CodexError(\n 'PROTOCOL_INCOMPATIBLE',\n 'Codex thread response is missing required fields',\n true,\n );\n const turns = thread.turns.map((turn) => mapTurn(thread.id, turn));\n let status = threadStatus(thread.status);\n // Persisted history is normally notLoaded after a restart, even when every turn\n // finished. Keep unfinished history uncertain; the gateway separately overlays\n // durable execution markers, including dispatches without a known turn ID.\n if (thread.status.type === 'notLoaded' && !turns.some((turn) => turn.status === 'running'))\n status = turns.at(-1)?.status ?? 'idle';\n return {\n id: thread.id,\n ...(typeof thread.forkedFromId === 'string' ? { forkedFromId: thread.forkedFromId } : {}),\n projectId: '',\n cwd: thread.cwd,\n source: typeof thread.source === 'string' ? thread.source : JSON.stringify(thread.source),\n originator: typeof thread.originator === 'string' ? thread.originator : null,\n title: thread.name || thread.preview || '\u65B0\u4F1A\u8BDD',\n // List/metadata responses omit turns. The native preview is history evidence.\n hasMessages: Boolean(thread.preview) || thread.turns.length > 0,\n status,\n turns,\n managed: false,\n runtimeSettings: {\n model: thread.model ?? null,\n reasoningEffort: thread.reasoningEffort ?? null,\n },\n updatedAt: thread.updatedAt * 1000,\n actions: [],\n };\n}\nexport function mapTurn(threadId: string, turn: Turn): TurnView {\n assertObjectId(turn, 'turn');\n if (!['inProgress', 'completed', 'failed', 'interrupted'].includes(turn.status))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex turn status is not supported', true);\n if (!Array.isArray(turn.items))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex turn response is missing items', true);\n return {\n id: turn.id,\n threadId,\n status: turn.status === 'inProgress' ? 'running' : turn.status,\n ...(validTiming(turn.startedAt) ? { startedAt: turn.startedAt * 1000 } : {}),\n ...(validTiming(turn.completedAt) ? { completedAt: turn.completedAt * 1000 } : {}),\n ...(validTiming(turn.durationMs) ? { durationMs: turn.durationMs } : {}),\n items: turn.items.map(mapItem),\n ...(turn.error ? { error: turn.error.message } : {}),\n };\n}\nfunction validTiming(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\nexport function mapItem(item: ThreadItem): ItemView {\n assertObjectId(item, 'item');\n if (typeof item.type !== 'string')\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex item has no type', true);\n switch (item.type) {\n case 'userMessage': {\n const content = item.content.filter((input) => !['skill', 'mention'].includes(input.type));\n const replies =\n content.length === 1 && content[0]?.type === 'text'\n ? parseQuestionReplies(content[0].text)\n : undefined;\n const imageInputs = item.content.filter(\n (input) => input.type === 'image' || input.type === 'localImage',\n );\n let bytes = 0;\n const images = imageInputs.slice(0, MAX_IMAGES).flatMap((input) => {\n if (input.type !== 'image' || !('url' in input)) return [];\n const size = imageDataBytes(input.url);\n if (size === undefined || bytes + size > MAX_IMAGE_BYTES) return [];\n bytes += size;\n return [input.url];\n });\n return {\n id: item.id,\n type: item.type,\n text: replies\n ? replies.map((reply) => `${reply.question}\\n${reply.answer}`).join('\\n\\n')\n : item.content\n .filter((input) => input.type !== 'image' && input.type !== 'localImage')\n .map((input) => (input.type === 'text' ? input.text : `[${input.type}]`))\n .join('\\n'),\n ...(replies ? { questionReplies: replies } : {}),\n ...(imageInputs.length ? { images, imageCount: imageInputs.length } : {}),\n };\n }\n case 'agentMessage':\n return {\n id: item.id,\n type: item.type,\n text: item.text,\n ...(item.delivery ? { delivery: item.delivery } : {}),\n ...(item.questions?.length ? { questions: structuredClone(item.questions) } : {}),\n };\n case 'reasoning':\n return { id: item.id, type: item.type, text: [...item.summary, ...item.content].join('\\n') };\n case 'plan':\n return { id: item.id, type: item.type, text: item.text };\n case 'contextCompaction':\n // Lifecycle is carried by notifications, not the persisted item body.\n return { id: item.id, type: item.type };\n case 'enteredReviewMode':\n case 'exitedReviewMode':\n return { id: item.id, type: item.type, text: item.review };\n case 'commandExecution':\n return {\n id: item.id,\n type: item.type,\n status: item.status,\n command: item.command,\n cwd: item.cwd,\n output: item.aggregatedOutput ?? '',\n ...(item.exitCode !== null && item.exitCode !== 0\n ? { error: `\u9000\u51FA\u4EE3\u7801 ${item.exitCode}` }\n : {}),\n };\n case 'fileChange':\n return {\n id: item.id,\n type: item.type,\n status: item.status,\n changes: item.changes.map((change) => ({\n path: change.path,\n kind: change.kind.type,\n diff: change.diff,\n })),\n };\n default:\n return {\n id: item.id,\n type: item.type,\n ...('status' in item ? { status: String(item.status) } : {}),\n details: item,\n };\n }\n}\nexport function threadSettings(\n settings: Settings = {},\n): Pick<\n ThreadStartParams,\n 'model' | 'serviceTier' | 'approvalPolicy' | 'approvalsReviewer' | 'sandbox' | 'config'\n> &\n PermissionSelection {\n const preset = settings.permissionMode && permissionModes[settings.permissionMode];\n return {\n ...(settings.model === undefined ? {} : { model: settings.model }),\n ...(settings.serviceTier === undefined ? {} : { serviceTier: settings.serviceTier }),\n ...(settings.approvalPolicy === undefined ? {} : { approvalPolicy: settings.approvalPolicy }),\n ...(settings.approvalReviewer === undefined\n ? {}\n : { approvalsReviewer: settings.approvalReviewer }),\n ...(settings.sandbox === undefined ? {} : { sandbox: settings.sandbox }),\n ...(preset\n ? {\n permissions: preset.profile,\n approvalPolicy: preset.approvalPolicy,\n approvalsReviewer: preset.approvalReviewer,\n }\n : {}),\n ...(settings.reasoningEffort === undefined\n ? {}\n : { config: { model_reasoning_effort: settings.reasoningEffort } }),\n };\n}\nexport function turnSettings(\n cwd: string,\n settings: Settings = {},\n): Pick<\n TurnStartParams,\n 'model' | 'serviceTier' | 'approvalPolicy' | 'approvalsReviewer' | 'sandboxPolicy' | 'effort'\n> &\n PermissionSelection {\n const preset = settings.permissionMode && permissionModes[settings.permissionMode];\n const sandboxPolicy: TurnStartParams['sandboxPolicy'] =\n settings.sandbox === 'read-only'\n ? { type: 'readOnly', networkAccess: false }\n : settings.sandbox === 'workspace-write'\n ? {\n type: 'workspaceWrite',\n writableRoots: [cwd],\n networkAccess: false,\n excludeTmpdirEnvVar: false,\n excludeSlashTmp: false,\n }\n : settings.sandbox === 'danger-full-access'\n ? { type: 'dangerFullAccess' }\n : undefined;\n return {\n ...(settings.model === undefined ? {} : { model: settings.model }),\n ...(settings.serviceTier === undefined ? {} : { serviceTier: settings.serviceTier }),\n ...(settings.approvalPolicy === undefined ? {} : { approvalPolicy: settings.approvalPolicy }),\n ...(settings.approvalReviewer === undefined\n ? {}\n : { approvalsReviewer: settings.approvalReviewer }),\n ...(sandboxPolicy === undefined ? {} : { sandboxPolicy }),\n ...(preset\n ? {\n permissions: preset.profile,\n approvalPolicy: preset.approvalPolicy,\n approvalsReviewer: preset.approvalReviewer,\n }\n : {}),\n ...(settings.reasoningEffort === undefined\n ? {}\n : { effort: settings.reasoningEffort as ReasoningEffort }),\n };\n}\nfunction assertObjectId(value: unknown, kind: string): void {\n if (!isRecord(value) || typeof value.id !== 'string')\n throw new CodexError('PROTOCOL_INCOMPATIBLE', `Codex ${kind} has no stable id`, true);\n}\n"],
|
|
5
|
-
"mappings": "AAQA,SAAS,uBAAuB;AAEhC,SAAS,0BAA0B;AASnC,SAAS,4BAA4B;AACrC,SAAS,gBAAgB,YAAY,uBAAuB;AAC5D,SAAS,YAAY,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import type {\n ItemView,\n RunStatus,\n RuntimeSettings,\n Settings,\n ThreadView,\n TurnView,\n} from '../shared/types.js';\nimport { permissionModes } from '../shared/permissions.js';\nimport type { PermissionSelection } from './permissions.js';\nexport { permissionSettings } from './permissions.js';\nimport type { Thread } from './generated/v2/Thread.js';\nimport type { ThreadItem } from './generated/v2/ThreadItem.js';\nimport type { ThreadStatus } from './generated/v2/ThreadStatus.js';\nimport type { Turn } from './generated/v2/Turn.js';\nimport type { ThreadStartParams } from './generated/v2/ThreadStartParams.js';\nimport type { TurnStartParams } from './generated/v2/TurnStartParams.js';\nimport type { ReasoningEffort } from './generated/ReasoningEffort.js';\nimport type { UserInput } from './generated/v2/UserInput.js';\nimport { parseQuestionReplies } from '../shared/async-questions.js';\nimport { imageDataBytes, MAX_IMAGES, MAX_IMAGE_BYTES } from '../shared/images.js';\nimport { CodexError, isRecord } from './transport.js';\n\n/** Keep public diagnostics bounded and independent of upstream protocol types. */\nexport function mapTurnError(error: {\n codexErrorInfo?: unknown;\n additionalDetails?: string | null;\n}) {\n const info = error.codexErrorInfo;\n const code = typeof info === 'string' ? info : isRecord(info) ? Object.keys(info)[0] : undefined;\n return {\n ...(code ? { errorCode: code.slice(0, 100) } : {}),\n ...(error.additionalDetails ? { errorDetails: error.additionalDetails.slice(0, 4096) } : {}),\n };\n}\n\nexport function taskInput(text: string, images: string[] = []): UserInput[] {\n return [\n ...(text ? [{ type: 'text' as const, text, text_elements: [] }] : []),\n ...images.map((url) => ({ type: 'image' as const, url })),\n ];\n}\n\n/** Codex 0.164.0 reports Fast as priority and standard speed as default. */\nexport function serviceTierSettings(\n tier: string | null | undefined,\n): Pick<RuntimeSettings, 'serviceTier'> {\n if (tier === undefined) return {};\n return { serviceTier: tier === 'priority' ? 'fast' : tier === 'default' ? null : tier };\n}\n\nexport function threadStatus(status: ThreadStatus): RunStatus {\n if (!isRecord(status))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex thread status is malformed', true);\n if (status.type === 'notLoaded') return 'unknown';\n if (status.type === 'idle') return 'idle';\n if (status.type === 'systemError') return 'failed';\n if (status.type !== 'active' || !Array.isArray(status.activeFlags))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex thread status is not supported', true);\n if (status.activeFlags.includes('waitingOnApproval')) return 'waitingApproval';\n if (status.activeFlags.includes('waitingOnUserInput')) return 'waitingInput';\n return 'running';\n}\nexport function mapThread(thread: Thread): ThreadView {\n assertObjectId(thread, 'thread');\n if (typeof thread.cwd !== 'string' || !Array.isArray(thread.turns) || !isRecord(thread.status))\n throw new CodexError(\n 'PROTOCOL_INCOMPATIBLE',\n 'Codex thread response is missing required fields',\n true,\n );\n const turns = thread.turns.map((turn) => mapTurn(thread.id, turn));\n let status = threadStatus(thread.status);\n // Persisted history is normally notLoaded after a restart, even when every turn\n // finished. Keep unfinished history uncertain; the gateway separately overlays\n // durable execution markers, including dispatches without a known turn ID.\n if (thread.status.type === 'notLoaded' && !turns.some((turn) => turn.status === 'running'))\n status = turns.at(-1)?.status ?? 'idle';\n return {\n id: thread.id,\n ...(typeof thread.forkedFromId === 'string' ? { forkedFromId: thread.forkedFromId } : {}),\n projectId: '',\n cwd: thread.cwd,\n source: typeof thread.source === 'string' ? thread.source : JSON.stringify(thread.source),\n originator: typeof thread.originator === 'string' ? thread.originator : null,\n title: thread.name || thread.preview || '\u65B0\u4F1A\u8BDD',\n // List/metadata responses omit turns. The native preview is history evidence.\n hasMessages: Boolean(thread.preview) || thread.turns.length > 0,\n status,\n turns,\n managed: false,\n runtimeSettings: {\n model: thread.model ?? null,\n reasoningEffort: thread.reasoningEffort ?? null,\n },\n updatedAt: thread.updatedAt * 1000,\n actions: [],\n };\n}\nexport function mapTurn(threadId: string, turn: Turn): TurnView {\n assertObjectId(turn, 'turn');\n if (!['inProgress', 'completed', 'failed', 'interrupted'].includes(turn.status))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex turn status is not supported', true);\n if (!Array.isArray(turn.items))\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex turn response is missing items', true);\n return {\n id: turn.id,\n threadId,\n status: turn.status === 'inProgress' ? 'running' : turn.status,\n ...(validTiming(turn.startedAt) ? { startedAt: turn.startedAt * 1000 } : {}),\n ...(validTiming(turn.completedAt) ? { completedAt: turn.completedAt * 1000 } : {}),\n ...(validTiming(turn.durationMs) ? { durationMs: turn.durationMs } : {}),\n items: turn.items.map(mapItem),\n ...(turn.error ? { error: turn.error.message, ...mapTurnError(turn.error) } : {}),\n };\n}\nfunction validTiming(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\nexport function mapItem(item: ThreadItem): ItemView {\n assertObjectId(item, 'item');\n if (typeof item.type !== 'string')\n throw new CodexError('PROTOCOL_INCOMPATIBLE', 'Codex item has no type', true);\n switch (item.type) {\n case 'userMessage': {\n const content = item.content.filter((input) => !['skill', 'mention'].includes(input.type));\n const replies =\n content.length === 1 && content[0]?.type === 'text'\n ? parseQuestionReplies(content[0].text)\n : undefined;\n const imageInputs = item.content.filter(\n (input) => input.type === 'image' || input.type === 'localImage',\n );\n let bytes = 0;\n const images = imageInputs.slice(0, MAX_IMAGES).flatMap((input) => {\n if (input.type !== 'image' || !('url' in input)) return [];\n const size = imageDataBytes(input.url);\n if (size === undefined || bytes + size > MAX_IMAGE_BYTES) return [];\n bytes += size;\n return [input.url];\n });\n return {\n id: item.id,\n type: item.type,\n text: replies\n ? replies.map((reply) => `${reply.question}\\n${reply.answer}`).join('\\n\\n')\n : item.content\n .filter((input) => input.type !== 'image' && input.type !== 'localImage')\n .map((input) => (input.type === 'text' ? input.text : `[${input.type}]`))\n .join('\\n'),\n ...(replies ? { questionReplies: replies } : {}),\n ...(imageInputs.length ? { images, imageCount: imageInputs.length } : {}),\n };\n }\n case 'agentMessage':\n return {\n id: item.id,\n type: item.type,\n text: item.text,\n ...(item.delivery ? { delivery: item.delivery } : {}),\n ...(item.questions?.length ? { questions: structuredClone(item.questions) } : {}),\n };\n case 'reasoning':\n return { id: item.id, type: item.type, text: [...item.summary, ...item.content].join('\\n') };\n case 'plan':\n return { id: item.id, type: item.type, text: item.text };\n case 'contextCompaction':\n // Lifecycle is carried by notifications, not the persisted item body.\n return { id: item.id, type: item.type };\n case 'enteredReviewMode':\n case 'exitedReviewMode':\n return { id: item.id, type: item.type, text: item.review };\n case 'commandExecution':\n return {\n id: item.id,\n type: item.type,\n status: item.status,\n command: item.command,\n cwd: item.cwd,\n output: item.aggregatedOutput ?? '',\n ...(item.exitCode !== null && item.exitCode !== 0\n ? { error: `\u9000\u51FA\u4EE3\u7801 ${item.exitCode}` }\n : {}),\n };\n case 'fileChange':\n return {\n id: item.id,\n type: item.type,\n status: item.status,\n changes: item.changes.map((change) => ({\n path: change.path,\n kind: change.kind.type,\n diff: change.diff,\n })),\n };\n default:\n return {\n id: item.id,\n type: item.type,\n ...('status' in item ? { status: String(item.status) } : {}),\n details: item,\n };\n }\n}\nexport function threadSettings(\n settings: Settings = {},\n): Pick<\n ThreadStartParams,\n 'model' | 'serviceTier' | 'approvalPolicy' | 'approvalsReviewer' | 'sandbox' | 'config'\n> &\n PermissionSelection {\n const preset = settings.permissionMode && permissionModes[settings.permissionMode];\n return {\n ...(settings.model === undefined ? {} : { model: settings.model }),\n ...(settings.serviceTier === undefined ? {} : { serviceTier: settings.serviceTier }),\n ...(settings.approvalPolicy === undefined ? {} : { approvalPolicy: settings.approvalPolicy }),\n ...(settings.approvalReviewer === undefined\n ? {}\n : { approvalsReviewer: settings.approvalReviewer }),\n ...(settings.sandbox === undefined ? {} : { sandbox: settings.sandbox }),\n ...(preset\n ? {\n permissions: preset.profile,\n approvalPolicy: preset.approvalPolicy,\n approvalsReviewer: preset.approvalReviewer,\n }\n : {}),\n ...(settings.reasoningEffort === undefined\n ? {}\n : { config: { model_reasoning_effort: settings.reasoningEffort } }),\n };\n}\nexport function turnSettings(\n cwd: string,\n settings: Settings = {},\n): Pick<\n TurnStartParams,\n 'model' | 'serviceTier' | 'approvalPolicy' | 'approvalsReviewer' | 'sandboxPolicy' | 'effort'\n> &\n PermissionSelection {\n const preset = settings.permissionMode && permissionModes[settings.permissionMode];\n const sandboxPolicy: TurnStartParams['sandboxPolicy'] =\n settings.sandbox === 'read-only'\n ? { type: 'readOnly', networkAccess: false }\n : settings.sandbox === 'workspace-write'\n ? {\n type: 'workspaceWrite',\n writableRoots: [cwd],\n networkAccess: false,\n excludeTmpdirEnvVar: false,\n excludeSlashTmp: false,\n }\n : settings.sandbox === 'danger-full-access'\n ? { type: 'dangerFullAccess' }\n : undefined;\n return {\n ...(settings.model === undefined ? {} : { model: settings.model }),\n ...(settings.serviceTier === undefined ? {} : { serviceTier: settings.serviceTier }),\n ...(settings.approvalPolicy === undefined ? {} : { approvalPolicy: settings.approvalPolicy }),\n ...(settings.approvalReviewer === undefined\n ? {}\n : { approvalsReviewer: settings.approvalReviewer }),\n ...(sandboxPolicy === undefined ? {} : { sandboxPolicy }),\n ...(preset\n ? {\n permissions: preset.profile,\n approvalPolicy: preset.approvalPolicy,\n approvalsReviewer: preset.approvalReviewer,\n }\n : {}),\n ...(settings.reasoningEffort === undefined\n ? {}\n : { effort: settings.reasoningEffort as ReasoningEffort }),\n };\n}\nfunction assertObjectId(value: unknown, kind: string): void {\n if (!isRecord(value) || typeof value.id !== 'string')\n throw new CodexError('PROTOCOL_INCOMPATIBLE', `Codex ${kind} has no stable id`, true);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { isModelCapacityError } from "../shared/turn-errors.js";
|
|
3
|
+
const DELAYS = [5e3, 1e4, 2e4];
|
|
4
|
+
class CapacityRetries {
|
|
5
|
+
constructor(run, changed) {
|
|
6
|
+
this.run = run;
|
|
7
|
+
this.changed = changed;
|
|
8
|
+
}
|
|
9
|
+
run;
|
|
10
|
+
changed;
|
|
11
|
+
jobs = /* @__PURE__ */ new Map();
|
|
12
|
+
begin(threadId, operationId, settings, epoch) {
|
|
13
|
+
this.clear(threadId);
|
|
14
|
+
const job = {
|
|
15
|
+
threadId,
|
|
16
|
+
operationId,
|
|
17
|
+
settings: structuredClone(settings),
|
|
18
|
+
epoch,
|
|
19
|
+
attempt: 0,
|
|
20
|
+
progressed: false,
|
|
21
|
+
nativeRetry: false,
|
|
22
|
+
cancelled: false,
|
|
23
|
+
handled: /* @__PURE__ */ new Set()
|
|
24
|
+
};
|
|
25
|
+
this.jobs.set(threadId, job);
|
|
26
|
+
return job;
|
|
27
|
+
}
|
|
28
|
+
current(threadId) {
|
|
29
|
+
return this.jobs.get(threadId);
|
|
30
|
+
}
|
|
31
|
+
owns(job) {
|
|
32
|
+
return this.current(job.threadId) === job && !job.cancelled;
|
|
33
|
+
}
|
|
34
|
+
view(threadId) {
|
|
35
|
+
return this.current(threadId)?.view;
|
|
36
|
+
}
|
|
37
|
+
pending(threadId) {
|
|
38
|
+
return ["waiting", "retrying"].includes(this.view(threadId)?.status ?? "");
|
|
39
|
+
}
|
|
40
|
+
active() {
|
|
41
|
+
return [...this.jobs.keys()].some((id) => this.pending(id));
|
|
42
|
+
}
|
|
43
|
+
clear(threadId) {
|
|
44
|
+
const job = this.current(threadId);
|
|
45
|
+
if (job?.timer) clearTimeout(job.timer);
|
|
46
|
+
if (job) job.cancelled = true;
|
|
47
|
+
this.jobs.delete(threadId);
|
|
48
|
+
}
|
|
49
|
+
clearAll() {
|
|
50
|
+
for (const id of this.jobs.keys()) this.clear(id);
|
|
51
|
+
}
|
|
52
|
+
cancel(job) {
|
|
53
|
+
if (job.timer) clearTimeout(job.timer);
|
|
54
|
+
delete job.timer;
|
|
55
|
+
job.cancelled = true;
|
|
56
|
+
if (job.view) job.view = { ...job.view, status: "cancelled", nextAttemptAt: void 0 };
|
|
57
|
+
this.changed(job.threadId);
|
|
58
|
+
}
|
|
59
|
+
observe(event, operationId) {
|
|
60
|
+
const threadId = event.type === "action" ? event.action.threadId : "threadId" in event ? event.threadId : void 0;
|
|
61
|
+
if (!threadId) return;
|
|
62
|
+
const job = this.current(threadId);
|
|
63
|
+
if (!job || job.cancelled) return;
|
|
64
|
+
if (event.type === "turn") {
|
|
65
|
+
if (operationId === job.operationId && job.boundOperationId !== operationId) {
|
|
66
|
+
job.boundOperationId = operationId;
|
|
67
|
+
job.turnId = event.turn.id;
|
|
68
|
+
job.rootTurnId ??= event.turn.id;
|
|
69
|
+
if (job.view) job.view.turnId = event.turn.id;
|
|
70
|
+
}
|
|
71
|
+
if (event.turn.id !== job.turnId) {
|
|
72
|
+
if (event.turn.status === "running") this.clear(threadId);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (event.turn.items.some((item) => item.type !== "userMessage")) job.progressed = true;
|
|
76
|
+
if (["completed", "failed", "interrupted"].includes(event.turn.status))
|
|
77
|
+
this.finished(job, event.turn);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (event.type === "action") {
|
|
81
|
+
if (!event.action.turnId || event.action.turnId === job.turnId) {
|
|
82
|
+
job.progressed = true;
|
|
83
|
+
if (job.timer) this.stop(job, "progress");
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!("turnId" in event) || event.turnId !== job.turnId) return;
|
|
88
|
+
if (event.type === "turnError") {
|
|
89
|
+
if (event.willRetry) {
|
|
90
|
+
job.nativeRetry = true;
|
|
91
|
+
if (job.timer) this.stop(job, "native");
|
|
92
|
+
} else {
|
|
93
|
+
job.error = { message: event.message, code: event.errorCode };
|
|
94
|
+
if (job.timer && !isModelCapacityError(event.message, event.errorCode))
|
|
95
|
+
this.stop(job, "error");
|
|
96
|
+
}
|
|
97
|
+
} else if (event.type === "turnProgress" || event.type === "delta" || event.type === "diff" || event.type === "autoReview" || event.type === "compactionCompleted" || event.type === "item" && event.item.type !== "userMessage") {
|
|
98
|
+
job.progressed = true;
|
|
99
|
+
if (job.timer) this.stop(job, "progress");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
finished(job, turn) {
|
|
103
|
+
if (turn.status !== "failed") {
|
|
104
|
+
this.clear(job.threadId);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (job.handled.has(turn.id)) return;
|
|
108
|
+
job.handled.add(turn.id);
|
|
109
|
+
const message = turn.error ?? job.error?.message ?? "";
|
|
110
|
+
const code = turn.errorCode ?? job.error?.code;
|
|
111
|
+
if (!isModelCapacityError(message, code)) {
|
|
112
|
+
if (job.view) this.stop(job, "error");
|
|
113
|
+
else this.clear(job.threadId);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
job.view ??= {
|
|
117
|
+
rootTurnId: job.rootTurnId,
|
|
118
|
+
turnId: turn.id,
|
|
119
|
+
status: "waiting",
|
|
120
|
+
attempt: job.attempt,
|
|
121
|
+
limit: DELAYS.length
|
|
122
|
+
};
|
|
123
|
+
if (job.progressed || job.nativeRetry) {
|
|
124
|
+
this.stop(job, job.progressed ? "progress" : "native");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const delay = DELAYS[job.attempt];
|
|
128
|
+
if (delay === void 0) {
|
|
129
|
+
this.stop(job, "limit");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
job.view = {
|
|
133
|
+
...job.view,
|
|
134
|
+
status: "waiting",
|
|
135
|
+
attempt: job.attempt + 1,
|
|
136
|
+
nextAttemptAt: Date.now() + delay
|
|
137
|
+
};
|
|
138
|
+
job.timer = setTimeout(() => {
|
|
139
|
+
delete job.timer;
|
|
140
|
+
if (!this.owns(job)) return;
|
|
141
|
+
job.attempt++;
|
|
142
|
+
job.previousOperationId = job.operationId;
|
|
143
|
+
job.operationId = randomUUID();
|
|
144
|
+
job.error = void 0;
|
|
145
|
+
job.view = {
|
|
146
|
+
...job.view,
|
|
147
|
+
status: "retrying",
|
|
148
|
+
attempt: job.attempt,
|
|
149
|
+
nextAttemptAt: void 0
|
|
150
|
+
};
|
|
151
|
+
this.changed(job.threadId);
|
|
152
|
+
void this.run(job).catch(() => {
|
|
153
|
+
if (this.owns(job)) {
|
|
154
|
+
this.stop(job, "error");
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}, delay);
|
|
158
|
+
job.timer.unref?.();
|
|
159
|
+
}
|
|
160
|
+
dispatchFailed(threadId, operationId) {
|
|
161
|
+
const job = this.current(threadId);
|
|
162
|
+
if (!job || job.operationId !== operationId || job.cancelled) return;
|
|
163
|
+
if (job.view) this.stop(job, "error");
|
|
164
|
+
else this.clear(threadId);
|
|
165
|
+
}
|
|
166
|
+
stop(job, reason) {
|
|
167
|
+
if (job.timer) clearTimeout(job.timer);
|
|
168
|
+
delete job.timer;
|
|
169
|
+
if (job.view) job.view = { ...job.view, status: "stopped", reason, nextAttemptAt: void 0 };
|
|
170
|
+
this.changed(job.threadId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
CapacityRetries
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=capacity-retry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/server/capacity-retry.ts"],
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from 'node:crypto';\nimport type { CodexEvent } from '../codex/types.js';\nimport type { CapacityRetryView, Settings, TurnView } from '../shared/types.js';\nimport { isModelCapacityError } from '../shared/turn-errors.js';\n\nconst DELAYS = [5_000, 10_000, 20_000] as const;\nexport interface CapacityAttempt {\n threadId: string;\n operationId: string;\n previousOperationId?: string;\n boundOperationId?: string;\n epoch: string;\n settings: Settings;\n turnId?: string;\n rootTurnId?: string;\n attempt: number;\n progressed: boolean;\n nativeRetry: boolean;\n cancelled: boolean;\n interruptSent?: boolean;\n error?: { message: string; code?: string };\n handled: Set<string>;\n view?: CapacityRetryView;\n timer?: ReturnType<typeof setTimeout>;\n}\n\n/** Owns only retries which Codex declined to perform. No prompt or attachment is replayed. */\nexport class CapacityRetries {\n private readonly jobs = new Map<string, CapacityAttempt>();\n constructor(\n private readonly run: (job: CapacityAttempt) => Promise<void>,\n private readonly changed: (threadId: string) => void,\n ) {}\n\n begin(threadId: string, operationId: string, settings: Settings, epoch: string) {\n this.clear(threadId);\n const job: CapacityAttempt = {\n threadId,\n operationId,\n settings: structuredClone(settings),\n epoch,\n attempt: 0,\n progressed: false,\n nativeRetry: false,\n cancelled: false,\n handled: new Set(),\n };\n this.jobs.set(threadId, job);\n return job;\n }\n\n current(threadId: string) {\n return this.jobs.get(threadId);\n }\n owns(job: CapacityAttempt) {\n return this.current(job.threadId) === job && !job.cancelled;\n }\n view(threadId: string) {\n return this.current(threadId)?.view;\n }\n pending(threadId: string) {\n return ['waiting', 'retrying'].includes(this.view(threadId)?.status ?? '');\n }\n active() {\n return [...this.jobs.keys()].some((id) => this.pending(id));\n }\n\n clear(threadId: string) {\n const job = this.current(threadId);\n if (job?.timer) clearTimeout(job.timer);\n if (job) job.cancelled = true;\n this.jobs.delete(threadId);\n }\n clearAll() {\n for (const id of this.jobs.keys()) this.clear(id);\n }\n\n cancel(job: CapacityAttempt) {\n if (job.timer) clearTimeout(job.timer);\n delete job.timer;\n job.cancelled = true;\n if (job.view) job.view = { ...job.view, status: 'cancelled', nextAttemptAt: undefined };\n this.changed(job.threadId);\n }\n\n observe(event: CodexEvent, operationId?: string) {\n const threadId =\n event.type === 'action'\n ? event.action.threadId\n : 'threadId' in event\n ? event.threadId\n : undefined;\n if (!threadId) return;\n const job = this.current(threadId);\n if (!job || job.cancelled) return;\n if (event.type === 'turn') {\n if (operationId === job.operationId && job.boundOperationId !== operationId) {\n job.boundOperationId = operationId;\n job.turnId = event.turn.id;\n job.rootTurnId ??= event.turn.id;\n if (job.view) job.view.turnId = event.turn.id;\n }\n if (event.turn.id !== job.turnId) {\n // A new turn from another client supersedes the original request.\n if (event.turn.status === 'running') this.clear(threadId);\n return;\n }\n if (event.turn.items.some((item) => item.type !== 'userMessage')) job.progressed = true;\n if (['completed', 'failed', 'interrupted'].includes(event.turn.status))\n this.finished(job, event.turn);\n return;\n }\n if (event.type === 'action') {\n if (!event.action.turnId || event.action.turnId === job.turnId) {\n job.progressed = true;\n if (job.timer) this.stop(job, 'progress');\n }\n return;\n }\n if (!('turnId' in event) || event.turnId !== job.turnId) return;\n if (event.type === 'turnError') {\n if (event.willRetry) {\n job.nativeRetry = true;\n if (job.timer) this.stop(job, 'native');\n } else {\n job.error = { message: event.message, code: event.errorCode };\n if (job.timer && !isModelCapacityError(event.message, event.errorCode))\n this.stop(job, 'error');\n }\n } else if (\n event.type === 'turnProgress' ||\n event.type === 'delta' ||\n event.type === 'diff' ||\n event.type === 'autoReview' ||\n event.type === 'compactionCompleted' ||\n (event.type === 'item' && event.item.type !== 'userMessage')\n ) {\n job.progressed = true;\n // Unexpected late progress invalidates a waiting retry rather than racing it.\n if (job.timer) this.stop(job, 'progress');\n }\n }\n\n private finished(job: CapacityAttempt, turn: TurnView) {\n if (turn.status !== 'failed') {\n this.clear(job.threadId);\n return;\n }\n if (job.handled.has(turn.id)) return;\n job.handled.add(turn.id);\n const message = turn.error ?? job.error?.message ?? '';\n const code = turn.errorCode ?? job.error?.code;\n if (!isModelCapacityError(message, code)) {\n if (job.view) this.stop(job, 'error');\n else this.clear(job.threadId);\n return;\n }\n job.view ??= {\n rootTurnId: job.rootTurnId!,\n turnId: turn.id,\n status: 'waiting',\n attempt: job.attempt,\n limit: DELAYS.length,\n };\n if (job.progressed || job.nativeRetry) {\n this.stop(job, job.progressed ? 'progress' : 'native');\n return;\n }\n const delay = DELAYS[job.attempt];\n if (delay === undefined) {\n this.stop(job, 'limit');\n return;\n }\n job.view = {\n ...job.view,\n status: 'waiting',\n attempt: job.attempt + 1,\n nextAttemptAt: Date.now() + delay,\n };\n job.timer = setTimeout(() => {\n delete job.timer;\n if (!this.owns(job)) return;\n job.attempt++;\n job.previousOperationId = job.operationId;\n job.operationId = randomUUID();\n job.error = undefined;\n job.view = {\n ...job.view!,\n status: 'retrying',\n attempt: job.attempt,\n nextAttemptAt: undefined,\n };\n this.changed(job.threadId);\n void this.run(job).catch(() => {\n // A failed or uncertain dispatch is never an invitation to replay the RPC.\n if (this.owns(job)) {\n this.stop(job, 'error');\n }\n });\n }, delay);\n job.timer.unref?.();\n }\n\n dispatchFailed(threadId: string, operationId: string) {\n const job = this.current(threadId);\n if (!job || job.operationId !== operationId || job.cancelled) return;\n if (job.view) this.stop(job, 'error');\n else this.clear(threadId);\n }\n\n private stop(job: CapacityAttempt, reason: NonNullable<CapacityRetryView['reason']>) {\n if (job.timer) clearTimeout(job.timer);\n delete job.timer;\n if (job.view) job.view = { ...job.view, status: 'stopped', reason, nextAttemptAt: undefined };\n this.changed(job.threadId);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -155,6 +155,17 @@ class OperationStore {
|
|
|
155
155
|
)
|
|
156
156
|
).all();
|
|
157
157
|
}
|
|
158
|
+
capacityRetryTurns(threadId) {
|
|
159
|
+
const rows = this.database.select({ result: operations.result }).from(operations).where(and(eq(operations.target, threadId), eq(operations.kind, "turn.start"))).all();
|
|
160
|
+
const links = /* @__PURE__ */ new Map();
|
|
161
|
+
for (const { result } of rows) {
|
|
162
|
+
if (typeof result !== "object" || result === null) continue;
|
|
163
|
+
const value = result;
|
|
164
|
+
if (typeof value.turnId === "string" && typeof value.retryOf === "string")
|
|
165
|
+
links.set(value.turnId, value.retryOf);
|
|
166
|
+
}
|
|
167
|
+
return links;
|
|
168
|
+
}
|
|
158
169
|
marker(operationId) {
|
|
159
170
|
return this.database.select().from(executionMarkers).where(eq(executionMarkers.operationId, operationId)).get();
|
|
160
171
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/server/db/operations.ts"],
|
|
4
|
-
"sourcesContent": ["import { createHash } from 'node:crypto';\nimport { and, eq, isNull, or } from 'drizzle-orm';\nimport type { AppDatabase } from './index.js';\nimport { executionMarkers, operations, threadLinks } from './schema.js';\nimport type { ApiError } from '../../shared/types.js';\n\nexport type OperationRow = typeof operations.$inferSelect;\nexport type ExecutionMarker = typeof executionMarkers.$inferSelect;\nexport type ThreadLink = typeof threadLinks.$inferSelect;\n\nexport class StoreConflict extends Error {\n constructor(\n public readonly code: 'OPERATION_CONFLICT' | 'THREAD_BUSY',\n message: string,\n public readonly threadId?: string,\n ) {\n super(message);\n }\n}\n\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;\n if (value !== null && typeof value === 'object') {\n return `{${Object.entries(value)\n .filter(([, item]) => item !== undefined)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`)\n .join(',')}}`;\n }\n return JSON.stringify(value) ?? 'null';\n}\n\nexport function requestDigest(value: unknown): string {\n return createHash('sha256').update(canonical(value)).digest('hex');\n}\n\n/** Every transaction is synchronous; no process/network work occurs inside one. */\nexport class OperationStore {\n constructor(public readonly database: AppDatabase) {}\n\n accept(\n clientOperationId: string,\n kind: string,\n target: string,\n request: unknown,\n ): { operation: OperationRow; inserted: boolean } {\n const digest = requestDigest(request);\n return this.database.transaction((tx) => {\n const now = Date.now();\n const inserted =\n tx\n .insert(operations)\n .values({\n clientOperationId,\n kind,\n target,\n requestDigest: digest,\n state: 'accepted',\n createdAt: now,\n updatedAt: now,\n })\n .onConflictDoNothing({ target: operations.clientOperationId })\n .run().changes === 1;\n const operation = tx\n .select()\n .from(operations)\n .where(eq(operations.clientOperationId, clientOperationId))\n .get();\n if (!operation) throw new Error('Operation insert did not persist');\n if (\n operation.kind !== kind ||\n operation.target !== target ||\n operation.requestDigest !== digest\n ) {\n throw new StoreConflict('OPERATION_CONFLICT', '\u540C\u4E00\u64CD\u4F5C ID \u5DF2\u7528\u4E8E\u4E0D\u540C\u7684\u8BF7\u6C42');\n }\n return { operation, inserted };\n });\n }\n\n get(id: string): OperationRow | undefined {\n return this.database\n .select()\n .from(operations)\n .where(eq(operations.clientOperationId, id))\n .get();\n }\n\n associateProject(id: string, projectId: string | null): void {\n this.database\n .update(operations)\n .set({ projectId })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'accepted')))\n .run();\n }\n\n /** Includes older rows whose project can be recovered from their stable target/link. */\n pendingProjectOperations(projectId: string | null): OperationRow[] {\n return this.database\n .select()\n .from(operations)\n .where(\n or(\n eq(operations.state, 'accepted'),\n eq(operations.state, 'dispatching'),\n and(eq(operations.state, 'unknown'), isNull(operations.reconciledAt)),\n ),\n )\n .all()\n .filter((operation) => {\n if (\n ![\n 'thread.create',\n 'thread.resume',\n 'thread.fork',\n 'thread.archive',\n 'thread.rename',\n ].includes(operation.kind)\n )\n return false;\n const owner =\n operation.projectId ??\n (operation.kind === 'thread.create'\n ? operation.target === '@unassigned'\n ? null\n : operation.target\n : this.getThreadLink(operation.target)?.projectId);\n return owner === projectId;\n });\n }\n\n dispatch(\n id: string,\n marker?: { threadId: string; workspaceKey: string; codexEpoch: string },\n ): void {\n this.database.transaction((tx) => {\n if (marker) {\n const occupied = tx\n .select()\n .from(executionMarkers)\n .where(\n and(isNull(executionMarkers.endedAt), eq(executionMarkers.threadId, marker.threadId)),\n )\n .get();\n if (occupied)\n throw new StoreConflict(\n 'THREAD_BUSY',\n '\u8BE5\u4F1A\u8BDD\u7684\u4E0A\u4E00\u8F6E\u6267\u884C\u5C1A\u672A\u7ED3\u675F\uFF0C\u8BF7\u8FFD\u52A0\u8981\u6C42\u6216\u7B49\u5F85\u7ED3\u675F',\n occupied.threadId,\n );\n }\n const updated = tx\n .update(operations)\n .set({ state: 'dispatching', updatedAt: Date.now() })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'accepted')))\n .run();\n if (updated.changes !== 1)\n throw new StoreConflict('OPERATION_CONFLICT', '\u64CD\u4F5C\u5DF2\u7ECF\u6D3E\u53D1\u6216\u7ED3\u675F');\n if (marker)\n tx.insert(executionMarkers)\n .values({ operationId: id, ...marker, state: 'active', createdAt: Date.now() })\n .run();\n });\n }\n\n acknowledge(id: string, result: unknown, turnId?: string): OperationRow {\n this.database.transaction((tx) => {\n tx.update(operations)\n .set({ state: 'acknowledged', result, error: null, updatedAt: Date.now() })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'dispatching')))\n .run();\n if (turnId !== undefined)\n tx.update(executionMarkers)\n .set({ turnId })\n .where(eq(executionMarkers.operationId, id))\n .run();\n });\n return this.get(id)!;\n }\n\n confirmWorkspaceCreation(id: string, threadId: string): OperationRow {\n const operation = this.get(id)!;\n this.database\n .update(operations)\n .set({\n state: 'acknowledged',\n error: null,\n updatedAt: Date.now(),\n result: {\n threadId,\n ...(operation.kind === 'thread.fork' ? { forkedFromId: operation.target } : {}),\n },\n })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'unknown')))\n .run();\n return this.get(id)!;\n }\n\n fail(id: string, error: ApiError, uncertain = false): OperationRow {\n this.database.transaction((tx) => {\n const changed = tx\n .update(operations)\n .set({ state: uncertain ? 'unknown' : 'failed', error, updatedAt: Date.now() })\n .where(\n and(\n eq(operations.clientOperationId, id),\n or(eq(operations.state, 'accepted'), eq(operations.state, 'dispatching')),\n ),\n )\n .run();\n // A late rejection must not turn an already-recovered unknown execution into a release.\n if (changed.changes === 0) return;\n tx.update(executionMarkers)\n .set(uncertain ? { state: 'unknown' } : { state: 'terminal', endedAt: Date.now() })\n .where(and(eq(executionMarkers.operationId, id), isNull(executionMarkers.endedAt)))\n .run();\n });\n return this.get(id)!;\n }\n\n /** Includes acknowledged requests with unfinished turns; none of these are retried. */\n recover(): void {\n this.database.transaction((tx) => {\n tx.update(operations)\n .set({\n state: 'failed',\n error: { code: 'NOT_DISPATCHED', message: '\u670D\u52A1\u5728\u6D3E\u53D1\u524D\u505C\u6B62\uFF1B\u64CD\u4F5C\u6CA1\u6709\u6267\u884C' },\n updatedAt: Date.now(),\n })\n .where(eq(operations.state, 'accepted'))\n .run();\n tx.update(operations)\n .set({\n state: 'unknown',\n error: { code: 'RESULT_UNKNOWN', message: '\u8FD0\u884C\u65F6\u5DF2\u505C\u6B62\uFF0C\u63D0\u4EA4\u7ED3\u679C\u9700\u8981\u6838\u5BF9' },\n updatedAt: Date.now(),\n })\n .where(eq(operations.state, 'dispatching'))\n .run();\n tx.update(executionMarkers)\n .set({ state: 'unknown' })\n .where(isNull(executionMarkers.endedAt))\n .run();\n });\n }\n\n activeMarkers(): ExecutionMarker[] {\n return this.database\n .select()\n .from(executionMarkers)\n .where(isNull(executionMarkers.endedAt))\n .all();\n }\n\n unresolvedOperations(): OperationRow[] {\n return this.database\n .select()\n .from(operations)\n .where(\n or(\n eq(operations.state, 'accepted'),\n eq(operations.state, 'dispatching'),\n and(eq(operations.state, 'unknown'), isNull(operations.reconciledAt)),\n ),\n )\n .all();\n }\n\n marker(operationId: string): ExecutionMarker | undefined {\n return this.database\n .select()\n .from(executionMarkers)\n .where(eq(executionMarkers.operationId, operationId))\n .get();\n }\n\n activeForThread(threadId: string): ExecutionMarker | undefined {\n return this.database\n .select()\n .from(executionMarkers)\n .where(and(eq(executionMarkers.threadId, threadId), isNull(executionMarkers.endedAt)))\n .get();\n }\n\n attachTurn(operationId: string, turnId: string): void {\n this.database\n .update(executionMarkers)\n .set({ turnId })\n .where(and(eq(executionMarkers.operationId, operationId), isNull(executionMarkers.endedAt)))\n .run();\n }\n\n finishTurn(threadId: string, turnId: string, codexEpoch: string): void {\n this.database\n .update(executionMarkers)\n .set({ state: 'terminal', endedAt: Date.now() })\n .where(\n and(\n eq(executionMarkers.threadId, threadId),\n eq(executionMarkers.turnId, turnId),\n eq(executionMarkers.codexEpoch, codexEpoch),\n isNull(executionMarkers.endedAt),\n ),\n )\n .run();\n }\n\n /** Keys the release to the original execution; an old decision cannot unlock a later turn. */\n reconcile(targetOperationId: string, reconciliationOperationId: string): void {\n this.database.transaction((tx) => {\n const now = Date.now();\n tx.update(operations)\n .set({ reconciledAt: now, reconciliationDecision: 'confirmed-ended' })\n .where(\n and(eq(operations.clientOperationId, targetOperationId), isNull(operations.reconciledAt)),\n )\n .run();\n tx.update(executionMarkers)\n .set({ state: 'reconciled', endedAt: now, reconciledAt: now, reconciliationOperationId })\n .where(\n and(\n eq(executionMarkers.operationId, targetOperationId),\n isNull(executionMarkers.endedAt),\n ),\n )\n .run();\n tx.update(operations)\n .set({\n state: 'acknowledged',\n result: { targetOperationId, decision: 'confirmed-ended', reconciledAt: now },\n updatedAt: now,\n })\n .where(\n and(\n eq(operations.clientOperationId, reconciliationOperationId),\n eq(operations.state, 'dispatching'),\n ),\n )\n .run();\n });\n }\n\n linkThread(link: Omit<ThreadLink, 'updatedAt'>): void {\n this.database\n .insert(threadLinks)\n .values({ ...link, updatedAt: Date.now() })\n .onConflictDoUpdate({ target: threadLinks.threadId, set: { ...link, updatedAt: Date.now() } })\n .run();\n }\n\n getThreadLink(threadId: string): ThreadLink | undefined {\n return this.database.select().from(threadLinks).where(eq(threadLinks.threadId, threadId)).get();\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { createHash } from 'node:crypto';\nimport { and, eq, isNull, or } from 'drizzle-orm';\nimport type { AppDatabase } from './index.js';\nimport { executionMarkers, operations, threadLinks } from './schema.js';\nimport type { ApiError } from '../../shared/types.js';\n\nexport type OperationRow = typeof operations.$inferSelect;\nexport type ExecutionMarker = typeof executionMarkers.$inferSelect;\nexport type ThreadLink = typeof threadLinks.$inferSelect;\n\nexport class StoreConflict extends Error {\n constructor(\n public readonly code: 'OPERATION_CONFLICT' | 'THREAD_BUSY',\n message: string,\n public readonly threadId?: string,\n ) {\n super(message);\n }\n}\n\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;\n if (value !== null && typeof value === 'object') {\n return `{${Object.entries(value)\n .filter(([, item]) => item !== undefined)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`)\n .join(',')}}`;\n }\n return JSON.stringify(value) ?? 'null';\n}\n\nexport function requestDigest(value: unknown): string {\n return createHash('sha256').update(canonical(value)).digest('hex');\n}\n\n/** Every transaction is synchronous; no process/network work occurs inside one. */\nexport class OperationStore {\n constructor(public readonly database: AppDatabase) {}\n\n accept(\n clientOperationId: string,\n kind: string,\n target: string,\n request: unknown,\n ): { operation: OperationRow; inserted: boolean } {\n const digest = requestDigest(request);\n return this.database.transaction((tx) => {\n const now = Date.now();\n const inserted =\n tx\n .insert(operations)\n .values({\n clientOperationId,\n kind,\n target,\n requestDigest: digest,\n state: 'accepted',\n createdAt: now,\n updatedAt: now,\n })\n .onConflictDoNothing({ target: operations.clientOperationId })\n .run().changes === 1;\n const operation = tx\n .select()\n .from(operations)\n .where(eq(operations.clientOperationId, clientOperationId))\n .get();\n if (!operation) throw new Error('Operation insert did not persist');\n if (\n operation.kind !== kind ||\n operation.target !== target ||\n operation.requestDigest !== digest\n ) {\n throw new StoreConflict('OPERATION_CONFLICT', '\u540C\u4E00\u64CD\u4F5C ID \u5DF2\u7528\u4E8E\u4E0D\u540C\u7684\u8BF7\u6C42');\n }\n return { operation, inserted };\n });\n }\n\n get(id: string): OperationRow | undefined {\n return this.database\n .select()\n .from(operations)\n .where(eq(operations.clientOperationId, id))\n .get();\n }\n\n associateProject(id: string, projectId: string | null): void {\n this.database\n .update(operations)\n .set({ projectId })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'accepted')))\n .run();\n }\n\n /** Includes older rows whose project can be recovered from their stable target/link. */\n pendingProjectOperations(projectId: string | null): OperationRow[] {\n return this.database\n .select()\n .from(operations)\n .where(\n or(\n eq(operations.state, 'accepted'),\n eq(operations.state, 'dispatching'),\n and(eq(operations.state, 'unknown'), isNull(operations.reconciledAt)),\n ),\n )\n .all()\n .filter((operation) => {\n if (\n ![\n 'thread.create',\n 'thread.resume',\n 'thread.fork',\n 'thread.archive',\n 'thread.rename',\n ].includes(operation.kind)\n )\n return false;\n const owner =\n operation.projectId ??\n (operation.kind === 'thread.create'\n ? operation.target === '@unassigned'\n ? null\n : operation.target\n : this.getThreadLink(operation.target)?.projectId);\n return owner === projectId;\n });\n }\n\n dispatch(\n id: string,\n marker?: { threadId: string; workspaceKey: string; codexEpoch: string },\n ): void {\n this.database.transaction((tx) => {\n if (marker) {\n const occupied = tx\n .select()\n .from(executionMarkers)\n .where(\n and(isNull(executionMarkers.endedAt), eq(executionMarkers.threadId, marker.threadId)),\n )\n .get();\n if (occupied)\n throw new StoreConflict(\n 'THREAD_BUSY',\n '\u8BE5\u4F1A\u8BDD\u7684\u4E0A\u4E00\u8F6E\u6267\u884C\u5C1A\u672A\u7ED3\u675F\uFF0C\u8BF7\u8FFD\u52A0\u8981\u6C42\u6216\u7B49\u5F85\u7ED3\u675F',\n occupied.threadId,\n );\n }\n const updated = tx\n .update(operations)\n .set({ state: 'dispatching', updatedAt: Date.now() })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'accepted')))\n .run();\n if (updated.changes !== 1)\n throw new StoreConflict('OPERATION_CONFLICT', '\u64CD\u4F5C\u5DF2\u7ECF\u6D3E\u53D1\u6216\u7ED3\u675F');\n if (marker)\n tx.insert(executionMarkers)\n .values({ operationId: id, ...marker, state: 'active', createdAt: Date.now() })\n .run();\n });\n }\n\n acknowledge(id: string, result: unknown, turnId?: string): OperationRow {\n this.database.transaction((tx) => {\n tx.update(operations)\n .set({ state: 'acknowledged', result, error: null, updatedAt: Date.now() })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'dispatching')))\n .run();\n if (turnId !== undefined)\n tx.update(executionMarkers)\n .set({ turnId })\n .where(eq(executionMarkers.operationId, id))\n .run();\n });\n return this.get(id)!;\n }\n\n confirmWorkspaceCreation(id: string, threadId: string): OperationRow {\n const operation = this.get(id)!;\n this.database\n .update(operations)\n .set({\n state: 'acknowledged',\n error: null,\n updatedAt: Date.now(),\n result: {\n threadId,\n ...(operation.kind === 'thread.fork' ? { forkedFromId: operation.target } : {}),\n },\n })\n .where(and(eq(operations.clientOperationId, id), eq(operations.state, 'unknown')))\n .run();\n return this.get(id)!;\n }\n\n fail(id: string, error: ApiError, uncertain = false): OperationRow {\n this.database.transaction((tx) => {\n const changed = tx\n .update(operations)\n .set({ state: uncertain ? 'unknown' : 'failed', error, updatedAt: Date.now() })\n .where(\n and(\n eq(operations.clientOperationId, id),\n or(eq(operations.state, 'accepted'), eq(operations.state, 'dispatching')),\n ),\n )\n .run();\n // A late rejection must not turn an already-recovered unknown execution into a release.\n if (changed.changes === 0) return;\n tx.update(executionMarkers)\n .set(uncertain ? { state: 'unknown' } : { state: 'terminal', endedAt: Date.now() })\n .where(and(eq(executionMarkers.operationId, id), isNull(executionMarkers.endedAt)))\n .run();\n });\n return this.get(id)!;\n }\n\n /** Includes acknowledged requests with unfinished turns; none of these are retried. */\n recover(): void {\n this.database.transaction((tx) => {\n tx.update(operations)\n .set({\n state: 'failed',\n error: { code: 'NOT_DISPATCHED', message: '\u670D\u52A1\u5728\u6D3E\u53D1\u524D\u505C\u6B62\uFF1B\u64CD\u4F5C\u6CA1\u6709\u6267\u884C' },\n updatedAt: Date.now(),\n })\n .where(eq(operations.state, 'accepted'))\n .run();\n tx.update(operations)\n .set({\n state: 'unknown',\n error: { code: 'RESULT_UNKNOWN', message: '\u8FD0\u884C\u65F6\u5DF2\u505C\u6B62\uFF0C\u63D0\u4EA4\u7ED3\u679C\u9700\u8981\u6838\u5BF9' },\n updatedAt: Date.now(),\n })\n .where(eq(operations.state, 'dispatching'))\n .run();\n tx.update(executionMarkers)\n .set({ state: 'unknown' })\n .where(isNull(executionMarkers.endedAt))\n .run();\n });\n }\n\n activeMarkers(): ExecutionMarker[] {\n return this.database\n .select()\n .from(executionMarkers)\n .where(isNull(executionMarkers.endedAt))\n .all();\n }\n\n unresolvedOperations(): OperationRow[] {\n return this.database\n .select()\n .from(operations)\n .where(\n or(\n eq(operations.state, 'accepted'),\n eq(operations.state, 'dispatching'),\n and(eq(operations.state, 'unknown'), isNull(operations.reconciledAt)),\n ),\n )\n .all();\n }\n\n capacityRetryTurns(threadId: string): Map<string, string> {\n const rows = this.database\n .select({ result: operations.result })\n .from(operations)\n .where(and(eq(operations.target, threadId), eq(operations.kind, 'turn.start')))\n .all();\n const links = new Map<string, string>();\n for (const { result } of rows) {\n if (typeof result !== 'object' || result === null) continue;\n const value = result as { turnId?: unknown; retryOf?: unknown };\n if (typeof value.turnId === 'string' && typeof value.retryOf === 'string')\n links.set(value.turnId, value.retryOf);\n }\n return links;\n }\n\n marker(operationId: string): ExecutionMarker | undefined {\n return this.database\n .select()\n .from(executionMarkers)\n .where(eq(executionMarkers.operationId, operationId))\n .get();\n }\n\n activeForThread(threadId: string): ExecutionMarker | undefined {\n return this.database\n .select()\n .from(executionMarkers)\n .where(and(eq(executionMarkers.threadId, threadId), isNull(executionMarkers.endedAt)))\n .get();\n }\n\n attachTurn(operationId: string, turnId: string): void {\n this.database\n .update(executionMarkers)\n .set({ turnId })\n .where(and(eq(executionMarkers.operationId, operationId), isNull(executionMarkers.endedAt)))\n .run();\n }\n\n finishTurn(threadId: string, turnId: string, codexEpoch: string): void {\n this.database\n .update(executionMarkers)\n .set({ state: 'terminal', endedAt: Date.now() })\n .where(\n and(\n eq(executionMarkers.threadId, threadId),\n eq(executionMarkers.turnId, turnId),\n eq(executionMarkers.codexEpoch, codexEpoch),\n isNull(executionMarkers.endedAt),\n ),\n )\n .run();\n }\n\n /** Keys the release to the original execution; an old decision cannot unlock a later turn. */\n reconcile(targetOperationId: string, reconciliationOperationId: string): void {\n this.database.transaction((tx) => {\n const now = Date.now();\n tx.update(operations)\n .set({ reconciledAt: now, reconciliationDecision: 'confirmed-ended' })\n .where(\n and(eq(operations.clientOperationId, targetOperationId), isNull(operations.reconciledAt)),\n )\n .run();\n tx.update(executionMarkers)\n .set({ state: 'reconciled', endedAt: now, reconciledAt: now, reconciliationOperationId })\n .where(\n and(\n eq(executionMarkers.operationId, targetOperationId),\n isNull(executionMarkers.endedAt),\n ),\n )\n .run();\n tx.update(operations)\n .set({\n state: 'acknowledged',\n result: { targetOperationId, decision: 'confirmed-ended', reconciledAt: now },\n updatedAt: now,\n })\n .where(\n and(\n eq(operations.clientOperationId, reconciliationOperationId),\n eq(operations.state, 'dispatching'),\n ),\n )\n .run();\n });\n }\n\n linkThread(link: Omit<ThreadLink, 'updatedAt'>): void {\n this.database\n .insert(threadLinks)\n .values({ ...link, updatedAt: Date.now() })\n .onConflictDoUpdate({ target: threadLinks.threadId, set: { ...link, updatedAt: Date.now() } })\n .run();\n }\n\n getThreadLink(threadId: string): ThreadLink | undefined {\n return this.database.select().from(threadLinks).where(eq(threadLinks.threadId, threadId)).get();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const exec = promisify(execFile);
|
|
4
|
+
function githubRepositoryUrl(remote) {
|
|
5
|
+
let path;
|
|
6
|
+
const scp = /^(?:[^\s/@:]+@)?github\.com:([^\s]+)$/i.exec(remote);
|
|
7
|
+
if (scp) path = scp[1];
|
|
8
|
+
else {
|
|
9
|
+
try {
|
|
10
|
+
const url = new URL(remote);
|
|
11
|
+
if (!["https:", "http:", "ssh:", "git:"].includes(url.protocol) || url.hostname.toLowerCase() !== "github.com" || url.port && !(url.protocol === "ssh:" && url.port === "22"))
|
|
12
|
+
return null;
|
|
13
|
+
path = url.pathname.slice(1);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const match = /^([a-z\d](?:[a-z\d-]*[a-z\d])?)\/([a-z\d_.-]+?)\/?$/i.exec(path);
|
|
19
|
+
if (!match) return null;
|
|
20
|
+
const repository = match[2].replace(/\.git$/i, "");
|
|
21
|
+
if (!repository || repository === "." || repository === "..") return null;
|
|
22
|
+
return `https://github.com/${match[1]}/${repository}`;
|
|
23
|
+
}
|
|
24
|
+
function selectGithubRepository(remotes) {
|
|
25
|
+
const origin = remotes.filter((remote) => remote.name === "origin");
|
|
26
|
+
const candidates = origin.length ? origin : remotes;
|
|
27
|
+
if (!candidates.length) return null;
|
|
28
|
+
const urls = candidates.map((remote) => githubRepositoryUrl(remote.url));
|
|
29
|
+
const first = urls[0];
|
|
30
|
+
return first && urls.every((url) => url?.toLowerCase() === first.toLowerCase()) ? first : null;
|
|
31
|
+
}
|
|
32
|
+
async function readGithubRepository(cwd) {
|
|
33
|
+
const options = { encoding: "utf8", timeout: 3e3, maxBuffer: 64 * 1024 };
|
|
34
|
+
try {
|
|
35
|
+
const { stdout: inside } = await exec(
|
|
36
|
+
"git",
|
|
37
|
+
["-C", cwd, "rev-parse", "--is-inside-work-tree"],
|
|
38
|
+
options
|
|
39
|
+
);
|
|
40
|
+
if (inside.trim() !== "true") return null;
|
|
41
|
+
const { stdout } = await exec(
|
|
42
|
+
"git",
|
|
43
|
+
["-C", cwd, "config", "--null", "--get-regexp", "^remote\\..*\\.url$"],
|
|
44
|
+
options
|
|
45
|
+
);
|
|
46
|
+
const remotes = stdout.split("\0").flatMap((entry) => {
|
|
47
|
+
const separator = entry.indexOf("\n");
|
|
48
|
+
const key = entry.slice(0, separator);
|
|
49
|
+
return separator >= 0 && key.startsWith("remote.") && key.endsWith(".url") ? [{ name: key.slice(7, -4), url: entry.slice(separator + 1) }] : [];
|
|
50
|
+
});
|
|
51
|
+
return selectGithubRepository(remotes);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
githubRepositoryUrl,
|
|
58
|
+
readGithubRepository,
|
|
59
|
+
selectGithubRepository
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=github-repository.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/server/github-repository.ts"],
|
|
4
|
+
"sourcesContent": ["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst exec = promisify(execFile);\n\n/** Return only a canonical public GitHub web URL; never forward remote credentials. */\nexport function githubRepositoryUrl(remote: string): string | null {\n let path: string;\n const scp = /^(?:[^\\s/@:]+@)?github\\.com:([^\\s]+)$/i.exec(remote);\n if (scp) path = scp[1]!;\n else {\n try {\n const url = new URL(remote);\n if (\n !['https:', 'http:', 'ssh:', 'git:'].includes(url.protocol) ||\n url.hostname.toLowerCase() !== 'github.com' ||\n (url.port && !(url.protocol === 'ssh:' && url.port === '22'))\n )\n return null;\n path = url.pathname.slice(1);\n } catch {\n return null;\n }\n }\n // Exact owner/repository paths prevent links to arbitrary GitHub pages or encoded paths.\n const match = /^([a-z\\d](?:[a-z\\d-]*[a-z\\d])?)\\/([a-z\\d_.-]+?)\\/?$/i.exec(path);\n if (!match) return null;\n const repository = match[2]!.replace(/\\.git$/i, '');\n if (!repository || repository === '.' || repository === '..') return null;\n return `https://github.com/${match[1]}/${repository}`;\n}\n\nexport function selectGithubRepository(remotes: { name: string; url: string }[]): string | null {\n const origin = remotes.filter((remote) => remote.name === 'origin');\n const candidates = origin.length ? origin : remotes;\n if (!candidates.length) return null;\n const urls = candidates.map((remote) => githubRepositoryUrl(remote.url));\n const first = urls[0];\n // Without origin, every remote must identify the same repository. Ambiguity hides the link.\n return first && urls.every((url) => url?.toLowerCase() === first.toLowerCase()) ? first : null;\n}\n\nexport async function readGithubRepository(cwd: string): Promise<string | null> {\n const options = { encoding: 'utf8' as const, timeout: 3000, maxBuffer: 64 * 1024 };\n try {\n const { stdout: inside } = await exec(\n 'git',\n ['-C', cwd, 'rev-parse', '--is-inside-work-tree'],\n options,\n );\n if (inside.trim() !== 'true') return null;\n // Git resolves parent checkouts and worktree config; no network or remote commands run.\n const { stdout } = await exec(\n 'git',\n ['-C', cwd, 'config', '--null', '--get-regexp', '^remote\\\\..*\\\\.url$'],\n options,\n );\n const remotes = stdout.split('\\0').flatMap((entry) => {\n const separator = entry.indexOf('\\n');\n const key = entry.slice(0, separator);\n return separator >= 0 && key.startsWith('remote.') && key.endsWith('.url')\n ? [{ name: key.slice(7, -4), url: entry.slice(separator + 1) }]\n : [];\n });\n return selectGithubRepository(remotes);\n } catch {\n // Missing Git, unreadable config, timeouts and non-repositories leave other actions available.\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAE1B,MAAM,OAAO,UAAU,QAAQ;AAGxB,SAAS,oBAAoB,QAA+B;AACjE,MAAI;AACJ,QAAM,MAAM,yCAAyC,KAAK,MAAM;AAChE,MAAI,IAAK,QAAO,IAAI,CAAC;AAAA,OAChB;AACH,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,MAAM;AAC1B,UACE,CAAC,CAAC,UAAU,SAAS,QAAQ,MAAM,EAAE,SAAS,IAAI,QAAQ,KAC1D,IAAI,SAAS,YAAY,MAAM,gBAC9B,IAAI,QAAQ,EAAE,IAAI,aAAa,UAAU,IAAI,SAAS;AAEvD,eAAO;AACT,aAAO,IAAI,SAAS,MAAM,CAAC;AAAA,IAC7B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,QAAQ,uDAAuD,KAAK,IAAI;AAC9E,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,MAAM,CAAC,EAAG,QAAQ,WAAW,EAAE;AAClD,MAAI,CAAC,cAAc,eAAe,OAAO,eAAe,KAAM,QAAO;AACrE,SAAO,sBAAsB,MAAM,CAAC,CAAC,IAAI,UAAU;AACrD;AAEO,SAAS,uBAAuB,SAAyD;AAC9F,QAAM,SAAS,QAAQ,OAAO,CAAC,WAAW,OAAO,SAAS,QAAQ;AAClE,QAAM,aAAa,OAAO,SAAS,SAAS;AAC5C,MAAI,CAAC,WAAW,OAAQ,QAAO;AAC/B,QAAM,OAAO,WAAW,IAAI,CAAC,WAAW,oBAAoB,OAAO,GAAG,CAAC;AACvE,QAAM,QAAQ,KAAK,CAAC;AAEpB,SAAO,SAAS,KAAK,MAAM,CAAC,QAAQ,KAAK,YAAY,MAAM,MAAM,YAAY,CAAC,IAAI,QAAQ;AAC5F;AAEA,eAAsB,qBAAqB,KAAqC;AAC9E,QAAM,UAAU,EAAE,UAAU,QAAiB,SAAS,KAAM,WAAW,KAAK,KAAK;AACjF,MAAI;AACF,UAAM,EAAE,QAAQ,OAAO,IAAI,MAAM;AAAA,MAC/B;AAAA,MACA,CAAC,MAAM,KAAK,aAAa,uBAAuB;AAAA,MAChD;AAAA,IACF;AACA,QAAI,OAAO,KAAK,MAAM,OAAQ,QAAO;AAErC,UAAM,EAAE,OAAO,IAAI,MAAM;AAAA,MACvB;AAAA,MACA,CAAC,MAAM,KAAK,UAAU,UAAU,gBAAgB,qBAAqB;AAAA,MACrE;AAAA,IACF;AACA,UAAM,UAAU,OAAO,MAAM,IAAI,EAAE,QAAQ,CAAC,UAAU;AACpD,YAAM,YAAY,MAAM,QAAQ,IAAI;AACpC,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS;AACpC,aAAO,aAAa,KAAK,IAAI,WAAW,SAAS,KAAK,IAAI,SAAS,MAAM,IACrE,CAAC,EAAE,MAAM,IAAI,MAAM,GAAG,EAAE,GAAG,KAAK,MAAM,MAAM,YAAY,CAAC,EAAE,CAAC,IAC5D,CAAC;AAAA,IACP,CAAC;AACD,WAAO,uBAAuB,OAAO;AAAA,EACvC,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/server/http.js
CHANGED
|
@@ -163,6 +163,14 @@ function createApp({
|
|
|
163
163
|
return c.json(op, status(op));
|
|
164
164
|
});
|
|
165
165
|
app.get("/api/projects", async (c) => c.json({ data: await service.listProjects() }));
|
|
166
|
+
app.get(
|
|
167
|
+
"/api/projects/:projectId/github-repository",
|
|
168
|
+
async (c) => c.json(await service.getProjectGithubRepository(id(c.req.param("projectId"))))
|
|
169
|
+
);
|
|
170
|
+
app.get(
|
|
171
|
+
"/api/threads/:threadId/github-repository",
|
|
172
|
+
async (c) => c.json(await service.getThreadGithubRepository(id(c.req.param("threadId"))))
|
|
173
|
+
);
|
|
166
174
|
app.get(
|
|
167
175
|
"/api/unassigned/runtime-settings",
|
|
168
176
|
async (c) => c.json(await service.readRuntimeSettings(null))
|
|
@@ -373,6 +381,20 @@ function createApp({
|
|
|
373
381
|
);
|
|
374
382
|
return c.json(op, status(op));
|
|
375
383
|
});
|
|
384
|
+
app.post("/api/threads/:threadId/retry", async (c) => {
|
|
385
|
+
const op = await service.retryTurn(
|
|
386
|
+
id(c.req.param("threadId")),
|
|
387
|
+
await parse(c, schemas.retryTurnSchema)
|
|
388
|
+
);
|
|
389
|
+
return c.json(op, status(op));
|
|
390
|
+
});
|
|
391
|
+
app.post("/api/threads/:threadId/retry/cancel", async (c) => {
|
|
392
|
+
const op = await service.cancelCapacityRetry(
|
|
393
|
+
id(c.req.param("threadId")),
|
|
394
|
+
await parse(c, schemas.interruptSchema)
|
|
395
|
+
);
|
|
396
|
+
return c.json(op, status(op));
|
|
397
|
+
});
|
|
376
398
|
app.post("/api/threads/:threadId/review", async (c) => {
|
|
377
399
|
const op = await service.startReview(
|
|
378
400
|
id(c.req.param("threadId")),
|