@covro/covro 0.5.1-dev.2 → 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 +2 -0
- package/README.zh-CN.md +2 -0
- 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/http.js +14 -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 +134 -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-DK1b1m5g.js → abnfDiagram-WEYOIA3C-ATf52olP.js} +1 -1
- package/dist/web/assets/{arc-0hKPa6cH.js → arc-ChLnTnZI.js} +1 -1
- package/dist/web/assets/{architecture-WOLXFQ4H-DUa2ELbW.js → architecture-WOLXFQ4H-B2oEg329.js} +1 -1
- package/dist/web/assets/{architectureDiagram-ZHVYKT53-DfpAMcHI.js → architectureDiagram-ZHVYKT53-BzSXU9d0.js} +1 -1
- package/dist/web/assets/{blockDiagram-RJAE7QA3-CD8LlPAk.js → blockDiagram-RJAE7QA3-xhCZ7iUm.js} +1 -1
- package/dist/web/assets/{c4Diagram-XDPPPE54-KVKGUBPP.js → c4Diagram-XDPPPE54-DZ8j79Mx.js} +1 -1
- package/dist/web/assets/channel-COC5jFhc.js +1 -0
- package/dist/web/assets/{chunk-2E7WHVMD-CPcfaznm.js → chunk-2E7WHVMD-y5rSOViW.js} +1 -1
- package/dist/web/assets/{chunk-4EMNKR2T-Ba4ZkZos.js → chunk-4EMNKR2T-B7WVoonr.js} +1 -1
- package/dist/web/assets/{chunk-55XCU76K-COU6aQoV.js → chunk-55XCU76K-D-tlN44X.js} +1 -1
- package/dist/web/assets/{chunk-7S6XSTFO-mof6va94.js → chunk-7S6XSTFO-31xOZx-Y.js} +1 -1
- package/dist/web/assets/{chunk-AQDTSKZB-B9S7XD0k.js → chunk-AQDTSKZB-CeCjUBmu.js} +1 -1
- package/dist/web/assets/{chunk-DSNLSHJZ-DiTTjo21.js → chunk-DSNLSHJZ-DgzXTo3_.js} +1 -1
- package/dist/web/assets/{chunk-HAGFOTED-bBaCv2Gr.js → chunk-HAGFOTED-Dm22rez0.js} +1 -1
- package/dist/web/assets/{chunk-HNE4Q36P-COubV5SQ.js → chunk-HNE4Q36P-CnFLQjbZ.js} +1 -1
- package/dist/web/assets/{chunk-NQOKRVIF-D7mCDLmx.js → chunk-NQOKRVIF-7DXXp2d_.js} +1 -1
- package/dist/web/assets/{chunk-UHW7PFBP-7E_Y7QMq.js → chunk-UHW7PFBP-Bw9-oVzV.js} +1 -1
- package/dist/web/assets/{chunk-VQ5W62QI-DIe2sxBO.js → chunk-VQ5W62QI-C9PHAHIJ.js} +1 -1
- package/dist/web/assets/{chunk-WQQL4CDK-sGc70NW0.js → chunk-WQQL4CDK-C9nKxc0i.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-CnTJfCp_.js → chunk-XXDRQBXY-DjdTeNXE.js} +1 -1
- package/dist/web/assets/{chunk-YZO67HFG-D1uBxw_D.js → chunk-YZO67HFG-C1z77_dC.js} +1 -1
- package/dist/web/assets/{chunk-ZAUX2LTX-quytYPeh.js → chunk-ZAUX2LTX-C7e41NBP.js} +1 -1
- package/dist/web/assets/{chunk-ZCHMQSOR-CebhdtBu.js → chunk-ZCHMQSOR-Gv41Qgvd.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-4MYWR3UA-_NujQwSa.js → classDiagram-v2-4MYWR3UA-PjZDyaSs.js} +1 -1
- package/dist/web/assets/{cose-bilkent-JH36ORCC-DltmODTL.js → cose-bilkent-JH36ORCC-CvwZK3HN.js} +1 -1
- package/dist/web/assets/{cynefin-EF2NZ3EQ-CyNUtvfu.js → cynefin-EF2NZ3EQ-Yn6p93cE.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-WF24UDRP-DH1m77PD.js → cynefinDiagram-WF24UDRP-BXa5Guxe.js} +1 -1
- package/dist/web/assets/{dagre-NK4GIZVX-DiCwjat3.js → dagre-NK4GIZVX-Cnskqb30.js} +1 -1
- package/dist/web/assets/{diagram-CBK3MWTC-DnoBkry8.js → diagram-CBK3MWTC-K8WEazkZ.js} +1 -1
- package/dist/web/assets/{diagram-I5XIIV2D-CIeRHNwj.js → diagram-I5XIIV2D-CLzouqUh.js} +1 -1
- package/dist/web/assets/{diagram-ODIL2UCW-BiyJagHP.js → diagram-ODIL2UCW-B_9ysyHU.js} +1 -1
- package/dist/web/assets/{diagram-PFBEJUXE-B6xGLENr.js → diagram-PFBEJUXE-D1ge5Ebx.js} +1 -1
- package/dist/web/assets/{diagram-RJA3I4VV-DCbGrXsR.js → diagram-RJA3I4VV-DWF6V26r.js} +1 -1
- package/dist/web/assets/{diagram-TYUNFSNR-BjYXlBMa.js → diagram-TYUNFSNR-BNM1pCS4.js} +1 -1
- package/dist/web/assets/{dist-C9Qd1YEe.js → dist-_uWZFvFF.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-V4P3TTPT-CpRXdAa6.js → ebnfDiagram-V4P3TTPT-Cge9YHFL.js} +1 -1
- package/dist/web/assets/{elk-QONTBCTQ-BZa9Vnbc.js → elk-QONTBCTQ-CY_393oL.js} +1 -1
- package/dist/web/assets/{erDiagram-KYQIGQAA-7AaYG_a9.js → erDiagram-KYQIGQAA-CCOr67SS.js} +1 -1
- package/dist/web/assets/{eventmodeling-K75KTNOO-BzIg1KNf.js → eventmodeling-K75KTNOO-BzNNWwRQ.js} +1 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-C8dLq0uV.js +1 -0
- package/dist/web/assets/{ganttDiagram-R7GPNTJY-DeNg-7K2.js → ganttDiagram-R7GPNTJY-8BewtZrJ.js} +1 -1
- package/dist/web/assets/{gitGraph-VSP46ZUC-BR-A7hdM.js → gitGraph-VSP46ZUC-CrqJbic_.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-LMyLpeqK.js → gitGraphDiagram-PDYL7XHY-Ddr5Q6Is.js} +1 -1
- package/dist/web/assets/{index-CEM8pkOB.css → index-C_i_PNi6.css} +1 -1
- package/dist/web/assets/{index-D2dQZgkr.js → index-DHENTsqN.js} +16 -15
- package/dist/web/assets/{info-OHQRW6UA-CRbCGNnm.js → info-OHQRW6UA-DgPvuC2J.js} +1 -1
- package/dist/web/assets/{infoDiagram-MVDRKAX5-DTUWae1O.js → infoDiagram-MVDRKAX5-zlubFwKS.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-CE4r0jTM.js → ishikawaDiagram-HPEHV2GE-DdQfvqVi.js} +1 -1
- package/dist/web/assets/{journeyDiagram-BOSHW7O7-KcSuVnTV.js → journeyDiagram-BOSHW7O7-DZ4XswBd.js} +1 -1
- package/dist/web/assets/{kanban-definition-FNSA4HC5-I0QF1Ftr.js → kanban-definition-FNSA4HC5-B1BQdpKc.js} +1 -1
- package/dist/web/assets/{line-DLSy1-se.js → line-jQDqs207.js} +1 -1
- package/dist/web/assets/{linear-XWNSUdmR.js → linear-DbYz4fU8.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-BcMfaJSH.js → mermaid-parser.core-CY5CVNe8.js} +3 -3
- package/dist/web/assets/{mermaid.core-CPl3cZ55.js → mermaid.core-mETL7nBW.js} +4 -4
- package/dist/web/assets/{mindmap-definition-6OY57UG3-BIB4ahHf.js → mindmap-definition-6OY57UG3-CxgWFLxa.js} +1 -1
- package/dist/web/assets/{packet-JDAUHWVQ-UZicf9-F.js → packet-JDAUHWVQ-Czsr9kk7.js} +1 -1
- package/dist/web/assets/{pegDiagram-ECIUJNBX-ckOqIfTt.js → pegDiagram-ECIUJNBX-BqYq-SA-.js} +1 -1
- package/dist/web/assets/{pie-XZMESJXO-DErhKWJX.js → pie-XZMESJXO-CRpxJzCy.js} +1 -1
- package/dist/web/assets/{pieDiagram-GNMPY74T-CrnS_2qe.js → pieDiagram-GNMPY74T-Bt2wKk8z.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-D57K4MC3-UK4DsDJz.js → quadrantDiagram-D57K4MC3-VOKqYTgn.js} +1 -1
- package/dist/web/assets/{radar-ABXABTNO-BYgJm4I9.js → radar-ABXABTNO-wXmArQH-.js} +1 -1
- package/dist/web/assets/{railroad-I3PHUGI6-2n6yUb7T.js → railroad-I3PHUGI6-CKPK4tQV.js} +1 -1
- package/dist/web/assets/{railroad-abnf-LNEFI6M7-CGAFqTzs.js → railroad-abnf-LNEFI6M7-BLEs3xqV.js} +1 -1
- package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-gJx_FXYW.js → railroad-ebnf-DWYD2UWJ-DS5QZgky.js} +1 -1
- package/dist/web/assets/{railroad-peg-JR7BVNR7-Dm0kEaxK.js → railroad-peg-JR7BVNR7-DHWXNOSl.js} +1 -1
- package/dist/web/assets/{railroadDiagram-Y72NRBL7-CqzySn4U.js → railroadDiagram-Y72NRBL7-Cy4eWKH1.js} +1 -1
- package/dist/web/assets/{requirementDiagram-EQYSW5RA-B33Hrc0L.js → requirementDiagram-EQYSW5RA-Bvbk8wwe.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-SY24JHO5-BA9Smc0j.js → sankeyDiagram-SY24JHO5-B4jGjMzu.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-BLEHNHQU-CUA-rKot.js → sequenceDiagram-BLEHNHQU-BpgUogv_.js} +1 -1
- package/dist/web/assets/{src-htchAh5W.js → src-DigPeGm7.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-kIYMkX3_.js → stateDiagram-v2-A6IESKWZ-tHq7e40Q.js} +1 -1
- package/dist/web/assets/{swimlanes-B3ENRKFQ-C8Lzsqig.js → swimlanes-B3ENRKFQ-DVUPAaDR.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-jJ6zDGAv.js +8 -0
- package/dist/web/assets/{timeline-definition-JXYKGLGB-C3uOHb1e.js → timeline-definition-JXYKGLGB-BHMM9AHV.js} +1 -1
- package/dist/web/assets/{treeView-D4JQ5SDB-Dq-ug1SE.js → treeView-D4JQ5SDB-DZ7k_TTS.js} +1 -1
- package/dist/web/assets/{treemap-SAJKECNS-mX01fnXB.js → treemap-SAJKECNS-C1wGfVb4.js} +1 -1
- package/dist/web/assets/{usecaseDiagram-WPOLBNNX-DMBep2-D.js → usecaseDiagram-WPOLBNNX-BTg0K_GO.js} +1 -1
- package/dist/web/assets/{vennDiagram-Q3Y3IDCW-CUardqBP.js → vennDiagram-Q3Y3IDCW-BTKShPx1.js} +1 -1
- package/dist/web/assets/{wardley-7MLQ67FV-CIOqSz29.js → wardley-7MLQ67FV-DnYPM5OB.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-IVAIDKWA-DtfU2fzi.js → wardleyDiagram-IVAIDKWA-Dzb67Boa.js} +1 -1
- package/dist/web/assets/{xychartDiagram-4URNHDU4-Cb3se94q.js → xychartDiagram-4URNHDU4-qijltKIO.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +3 -3
- package/dist/web/assets/channel-CpEW0zog.js +0 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-FDG_7_Jf.js +0 -1
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-CWrPdxiH.js +0 -8
package/dist/server/service.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CapacityRetries } from "./capacity-retry.js";
|
|
2
|
+
import { isModelCapacityError } from "../shared/turn-errors.js";
|
|
1
3
|
import { attentionKey, pendingAttention } from "../shared/thread-attention.js";
|
|
2
4
|
import { ShellService } from "./shell.js";
|
|
3
5
|
import { readGithubRepository } from "./github-repository.js";
|
|
@@ -49,11 +51,16 @@ class GatewayService {
|
|
|
49
51
|
this.shell = new ShellService(this.codex);
|
|
50
52
|
this.events = new EventHub(options.limits?.eventBytes);
|
|
51
53
|
this.projection = new Projection(options.limits?.itemBytes);
|
|
54
|
+
this.capacityRetries = new CapacityRetries(
|
|
55
|
+
(job) => this.dispatchCapacityRetry(job),
|
|
56
|
+
(id) => this.publishThread(id)
|
|
57
|
+
);
|
|
52
58
|
this.currentEpoch = this.codex.epoch;
|
|
53
59
|
this.store.recover();
|
|
54
60
|
this.removeListener = this.codex.onEvent((event) => this.consume(event));
|
|
55
61
|
}
|
|
56
62
|
options;
|
|
63
|
+
capacityRetries;
|
|
57
64
|
projectClones;
|
|
58
65
|
shell;
|
|
59
66
|
events;
|
|
@@ -86,6 +93,7 @@ class GatewayService {
|
|
|
86
93
|
runtimeSettingsUpdates = /* @__PURE__ */ new Map();
|
|
87
94
|
inFlightOperations = /* @__PURE__ */ new Set();
|
|
88
95
|
pendingThreadCreations = /* @__PURE__ */ new Map();
|
|
96
|
+
capacityLinks = /* @__PURE__ */ new Map();
|
|
89
97
|
listProjects() {
|
|
90
98
|
return this.projects.list();
|
|
91
99
|
}
|
|
@@ -132,6 +140,8 @@ class GatewayService {
|
|
|
132
140
|
return this.maintenanceReason === "backup" ? new AppError("BACKUP_MAINTENANCE", "\u6B63\u5728\u5907\u4EFD\uFF0C\u8BF7\u5B8C\u6210\u540E\u91CD\u8BD5\u3002", 503) : new AppError("UPDATE_MAINTENANCE", "\u6B63\u5728\u5347\u7EA7\uFF0C\u8BF7\u7B49\u5F85\u670D\u52A1\u91CD\u65B0\u8FDE\u63A5", 503);
|
|
133
141
|
}
|
|
134
142
|
prepareUpdate(reason = "update") {
|
|
143
|
+
if (this.capacityRetries.active())
|
|
144
|
+
return { ready: false, reason: "\u7B49\u5F85\u6A21\u578B\u5BB9\u91CF\u91CD\u8BD5\u7ED3\u675F\u6216\u53D6\u6D88" };
|
|
135
145
|
if (this.projectClones.hasActive) return { ready: false, reason: "\u7B49\u5F85\u9879\u76EE\u514B\u9686\u5B8C\u6210\u6216\u53D6\u6D88" };
|
|
136
146
|
if (this.shell.active()) return { ready: false, reason: "\u7B49\u5F85 Shell \u547D\u4EE4\u7ED3\u675F" };
|
|
137
147
|
if (this.inFlightOperations.size || this.resumes.size || this.pendingThreadCreations.size)
|
|
@@ -179,7 +189,7 @@ class GatewayService {
|
|
|
179
189
|
return link ? link.projectId === projectId : entry.workspaceKey === project.workspaceKey;
|
|
180
190
|
});
|
|
181
191
|
const active = this.projection.values().find(
|
|
182
|
-
(thread) => thread.projectId === projectId && (isActive(thread.status) || thread.actions.some((action) => ["pending", "responding"].includes(action.status)))
|
|
192
|
+
(thread) => thread.projectId === projectId && (isActive(thread.status) || this.capacityRetries.pending(thread.id) || thread.actions.some((action) => ["pending", "responding"].includes(action.status)))
|
|
183
193
|
);
|
|
184
194
|
if (pending || marker || active || this.shell.active(projectId))
|
|
185
195
|
throw new AppError(
|
|
@@ -453,6 +463,7 @@ class GatewayService {
|
|
|
453
463
|
updateArchiveState(threadId, archived) {
|
|
454
464
|
const thread = this.projection.get(threadId);
|
|
455
465
|
if (!thread || Boolean(thread.archived) === archived) return;
|
|
466
|
+
if (archived) this.capacityRetries.clear(threadId);
|
|
456
467
|
thread.archived = archived;
|
|
457
468
|
this.archiveRevisions.set(threadId, (this.archiveRevisions.get(threadId) ?? 0) + 1);
|
|
458
469
|
if (archived) this.releaseThreadOwnership(threadId);
|
|
@@ -494,6 +505,11 @@ class GatewayService {
|
|
|
494
505
|
throw new AppError("HISTORY_UNAVAILABLE", "\u5F53\u524D Codex \u4E0D\u652F\u6301\u5386\u53F2\u5206\u9875", 409);
|
|
495
506
|
const page = this.normalize(await this.codex.readThreadPage(threadId, cursor));
|
|
496
507
|
this.workspaces.assertOwnership(page.projectId, page.cwd, page.id);
|
|
508
|
+
const links = this.retryLinks(threadId);
|
|
509
|
+
for (const turn of page.turns) {
|
|
510
|
+
const root = links.get(turn.id);
|
|
511
|
+
if (root) turn.retryOf = root;
|
|
512
|
+
}
|
|
497
513
|
return page;
|
|
498
514
|
}
|
|
499
515
|
async exportThread(threadId) {
|
|
@@ -620,6 +636,7 @@ class GatewayService {
|
|
|
620
636
|
}
|
|
621
637
|
async resumeThread(threadId, input) {
|
|
622
638
|
return this.perform("thread.resume", threadId, input, async () => {
|
|
639
|
+
this.capacityRetries.clear(threadId);
|
|
623
640
|
const thread = await this.loadThread(threadId);
|
|
624
641
|
this.ensureReady();
|
|
625
642
|
this.ensureArchiveAvailable(thread);
|
|
@@ -721,11 +738,21 @@ class GatewayService {
|
|
|
721
738
|
});
|
|
722
739
|
}
|
|
723
740
|
async startTurn(threadId, input) {
|
|
741
|
+
return this.dispatchTurn(threadId, input);
|
|
742
|
+
}
|
|
743
|
+
async dispatchTurn(threadId, input, retry, expectedTurnId) {
|
|
724
744
|
return this.perform("turn.start", threadId, input, async () => {
|
|
745
|
+
if (!retry && !expectedTurnId && this.capacityRetries.view(threadId)?.status === "waiting")
|
|
746
|
+
this.capacityRetries.clear(threadId);
|
|
725
747
|
const thread = await this.loadThread(threadId);
|
|
726
748
|
this.ensureManaged(thread);
|
|
727
749
|
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
728
|
-
if (
|
|
750
|
+
if (expectedTurnId) {
|
|
751
|
+
const last = thread.turns.at(-1);
|
|
752
|
+
if (last?.id !== expectedTurnId || last.status !== "failed" || !isModelCapacityError(last.error ?? "", last.errorCode))
|
|
753
|
+
throw new AppError("TURN_CONFLICT", "\u76EE\u6807\u5BB9\u91CF\u5931\u8D25\u5DF2\u6539\u53D8\uFF0C\u8BF7\u5237\u65B0\u540E\u7EE7\u7EED", 409);
|
|
754
|
+
}
|
|
755
|
+
if (isActive(this.projection.get(threadId)?.status ?? thread.status))
|
|
729
756
|
throw new AppError("THREAD_BUSY", "\u4F1A\u8BDD\u5DF2\u6709\u6D3B\u52A8\u4EFB\u52A1\uFF0C\u8BF7\u8FFD\u52A0\u8981\u6C42\u6216\u4E2D\u65AD", 409, { threadId });
|
|
730
757
|
this.ensureCompatibilityConfirmed();
|
|
731
758
|
this.store.dispatch(input.clientOperationId, {
|
|
@@ -733,6 +760,15 @@ class GatewayService {
|
|
|
733
760
|
workspaceKey: ownership.workspaceKey,
|
|
734
761
|
codexEpoch: this.codex.epoch
|
|
735
762
|
});
|
|
763
|
+
if (retry && !this.capacityRetries.owns(retry))
|
|
764
|
+
throw new AppError("TURN_CONFLICT", "\u5BB9\u91CF\u91CD\u8BD5\u5DF2\u53D6\u6D88\u6216\u88AB\u66FF\u4EE3", 409);
|
|
765
|
+
const candidate = retry ?? this.capacityRetries.begin(
|
|
766
|
+
threadId,
|
|
767
|
+
input.clientOperationId,
|
|
768
|
+
this.retrySettings(thread.runtimeSettings, input.settings),
|
|
769
|
+
this.codex.epoch
|
|
770
|
+
);
|
|
771
|
+
const retryOf = retry?.rootTurnId ?? (expectedTurnId ? this.retryLinks(threadId).get(expectedTurnId) ?? expectedTurnId : void 0);
|
|
736
772
|
this.turnBaselines.set(input.clientOperationId, new Set(thread.turns.map((turn2) => turn2.id)));
|
|
737
773
|
this.publishThread(threadId);
|
|
738
774
|
const revision = this.projection.revision(threadId);
|
|
@@ -746,15 +782,70 @@ class GatewayService {
|
|
|
746
782
|
this.store.attachTurn(input.clientOperationId, turn.id);
|
|
747
783
|
this.turnBaselines.delete(input.clientOperationId);
|
|
748
784
|
this.projection.turn(threadId, turn, this.projection.revision(threadId) !== revision);
|
|
785
|
+
this.capacityRetries.observe({ type: "turn", threadId, turn }, input.clientOperationId);
|
|
786
|
+
if (retryOf) this.retryLinks(threadId).set(turn.id, retryOf);
|
|
787
|
+
if (candidate.cancelled && retry) await this.interruptCancelledCapacity(candidate, turn.id);
|
|
749
788
|
const projectedTurn = this.projection.get(threadId)?.turns.find((entry) => entry.id === turn.id);
|
|
750
789
|
if (projectedTurn && isTerminal(projectedTurn.status))
|
|
751
790
|
this.store.finishTurn(threadId, turn.id, this.codex.epoch);
|
|
752
791
|
this.publishThread(threadId);
|
|
753
|
-
return {
|
|
792
|
+
return {
|
|
793
|
+
result: { threadId, turnId: turn.id, ...retryOf ? { retryOf } : {} },
|
|
794
|
+
turnId: turn.id
|
|
795
|
+
};
|
|
754
796
|
});
|
|
755
797
|
}
|
|
798
|
+
retrySettings(runtime, input = {}) {
|
|
799
|
+
return {
|
|
800
|
+
...runtime?.model ? { model: runtime.model } : {},
|
|
801
|
+
...runtime?.reasoningEffort ? { reasoningEffort: runtime.reasoningEffort } : {},
|
|
802
|
+
...runtime?.serviceTier === "fast" || runtime?.serviceTier === null ? { serviceTier: runtime.serviceTier } : {},
|
|
803
|
+
...input
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
async dispatchCapacityRetry(job) {
|
|
807
|
+
if (!this.capacityRetries.owns(job) || !this.accepting || this.maintenance || !this.codex.ready || this.codex.epoch !== job.epoch)
|
|
808
|
+
throw new AppError("TURN_CONFLICT", "\u5BB9\u91CF\u91CD\u8BD5\u7684\u8FD0\u884C\u73AF\u5883\u5DF2\u6539\u53D8", 409);
|
|
809
|
+
const thread = this.projection.get(job.threadId);
|
|
810
|
+
const last = thread?.turns.at(-1);
|
|
811
|
+
if (!thread || !last || last.id !== job.turnId || last.status !== "failed" || this.store.activeForThread(job.threadId) || !job.previousOperationId || this.store.get(job.previousOperationId)?.state !== "acknowledged" || this.inFlightOperations.has(job.previousOperationId))
|
|
812
|
+
throw new AppError("TURN_CONFLICT", "\u539F\u4EFB\u52A1\u72B6\u6001\u5DF2\u6539\u53D8\uFF0C\u8BF7\u68C0\u67E5\u540E\u7EE7\u7EED", 409);
|
|
813
|
+
await this.dispatchTurn(
|
|
814
|
+
job.threadId,
|
|
815
|
+
{
|
|
816
|
+
clientOperationId: job.operationId,
|
|
817
|
+
text: "",
|
|
818
|
+
settings: job.settings
|
|
819
|
+
},
|
|
820
|
+
job
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
async cancelCapacityRetry(threadId, input) {
|
|
824
|
+
return this.perform("turn.retry.cancel", threadId, input, async () => {
|
|
825
|
+
const thread = await this.loadThread(threadId);
|
|
826
|
+
this.ensureManaged(thread);
|
|
827
|
+
const job = this.capacityRetries.current(threadId);
|
|
828
|
+
if (!job?.view || job.rootTurnId !== input.expectedTurnId)
|
|
829
|
+
throw new AppError("TURN_CONFLICT", "\u5BB9\u91CF\u91CD\u8BD5\u5DF2\u7ED3\u675F\u6216\u88AB\u66FF\u4EE3", 409);
|
|
830
|
+
this.store.dispatch(input.clientOperationId);
|
|
831
|
+
this.capacityRetries.cancel(job);
|
|
832
|
+
if (job.attempt > 0 && job.turnId) await this.interruptCancelledCapacity(job, job.turnId);
|
|
833
|
+
return { result: { threadId, cancelled: true } };
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
async interruptCancelledCapacity(job, turnId) {
|
|
837
|
+
const turn = this.projection.get(job.threadId)?.turns.find((entry) => entry.id === turnId);
|
|
838
|
+
if (!job.cancelled || job.interruptSent || !turn || !isActive(turn.status) || this.codex.epoch !== job.epoch || !this.codex.ready)
|
|
839
|
+
return;
|
|
840
|
+
job.interruptSent = true;
|
|
841
|
+
await this.codex.interruptTurn(job.threadId, turnId);
|
|
842
|
+
}
|
|
843
|
+
async retryTurn(threadId, input) {
|
|
844
|
+
return this.dispatchTurn(threadId, { ...input, text: "" }, void 0, input.expectedTurnId);
|
|
845
|
+
}
|
|
756
846
|
async compactThread(threadId, input) {
|
|
757
847
|
return this.perform("thread.compact", threadId, input, async () => {
|
|
848
|
+
this.capacityRetries.clear(threadId);
|
|
758
849
|
const thread = await this.loadThread(threadId);
|
|
759
850
|
this.ensureManaged(thread);
|
|
760
851
|
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
@@ -779,6 +870,7 @@ class GatewayService {
|
|
|
779
870
|
}
|
|
780
871
|
async startReview(threadId, input) {
|
|
781
872
|
return this.perform("review.start", threadId, input, async () => {
|
|
873
|
+
this.capacityRetries.clear(threadId);
|
|
782
874
|
const thread = await this.loadThread(threadId);
|
|
783
875
|
this.ensureManaged(thread);
|
|
784
876
|
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
@@ -916,6 +1008,7 @@ class GatewayService {
|
|
|
916
1008
|
}
|
|
917
1009
|
async shutdown() {
|
|
918
1010
|
this.accepting = false;
|
|
1011
|
+
this.capacityRetries.clearAll();
|
|
919
1012
|
await this.projectClones.close();
|
|
920
1013
|
this.store.recover();
|
|
921
1014
|
this.projection.runtimeReset();
|
|
@@ -957,6 +1050,8 @@ class GatewayService {
|
|
|
957
1050
|
const action = this.projection.resolveAction(target, "pending");
|
|
958
1051
|
if (action?.threadId) this.publishThread(action.threadId);
|
|
959
1052
|
}
|
|
1053
|
+
if (kind === "turn.start")
|
|
1054
|
+
this.capacityRetries.dispatchFailed(target, input.clientOperationId);
|
|
960
1055
|
if (dispatched && kind === "turn.start") this.publishThread(target);
|
|
961
1056
|
this.events.publish("operation", operation);
|
|
962
1057
|
if (uncertain)
|
|
@@ -1190,8 +1285,25 @@ class GatewayService {
|
|
|
1190
1285
|
thread.hasMessages || thread.archived || thread.turns.length || this.store.activeForThread(thread.id) || isActive(thread.status) || thread.actions.some((action) => ["pending", "responding"].includes(action.status))
|
|
1191
1286
|
);
|
|
1192
1287
|
}
|
|
1288
|
+
retryLinks(threadId) {
|
|
1289
|
+
let links = this.capacityLinks.get(threadId);
|
|
1290
|
+
if (!links) {
|
|
1291
|
+
links = this.store.capacityRetryTurns(threadId);
|
|
1292
|
+
this.capacityLinks.set(threadId, links);
|
|
1293
|
+
}
|
|
1294
|
+
return links;
|
|
1295
|
+
}
|
|
1193
1296
|
threadView(thread) {
|
|
1194
1297
|
const result = structuredClone(thread);
|
|
1298
|
+
const links = this.capacityLinks.get(thread.id) ?? (thread.turns.some((turn) => !turn.items.some((item) => item.type === "userMessage")) ? this.retryLinks(thread.id) : void 0);
|
|
1299
|
+
for (const turn of result.turns) {
|
|
1300
|
+
const root = links?.get(turn.id);
|
|
1301
|
+
if (root) turn.retryOf = root;
|
|
1302
|
+
}
|
|
1303
|
+
const retry = this.capacityRetries.view(thread.id);
|
|
1304
|
+
if (retry) result.capacityRetry = structuredClone(retry);
|
|
1305
|
+
else delete result.capacityRetry;
|
|
1306
|
+
if (this.capacityRetries.pending(thread.id)) result.status = "running";
|
|
1195
1307
|
result.archived = thread.archived ?? false;
|
|
1196
1308
|
result.historyVisible = this.historyVisible(thread);
|
|
1197
1309
|
const marker = this.store.activeForThread(thread.id);
|
|
@@ -1240,6 +1352,17 @@ class GatewayService {
|
|
|
1240
1352
|
}
|
|
1241
1353
|
}
|
|
1242
1354
|
consume(event) {
|
|
1355
|
+
if (event.type === "runtime" && (!event.diagnostics.ready || event.diagnostics.codexEpoch !== this.currentEpoch))
|
|
1356
|
+
this.capacityRetries.clearAll();
|
|
1357
|
+
const eventThread = event.type === "action" ? event.action.threadId : "threadId" in event ? event.threadId : void 0;
|
|
1358
|
+
const eventMarker = eventThread ? this.store.activeForThread(eventThread) : void 0;
|
|
1359
|
+
const matchesDispatch = event.type !== "turn" || eventMarker?.turnId === event.turn.id || eventMarker?.turnId === null && this.turnBaselines.get(eventMarker.operationId)?.has(event.turn.id) === false;
|
|
1360
|
+
this.capacityRetries.observe(event, matchesDispatch ? eventMarker?.operationId : void 0);
|
|
1361
|
+
if (event.type === "turn") {
|
|
1362
|
+
const job = this.capacityRetries.current(event.threadId);
|
|
1363
|
+
if (job?.attempt && job.turnId === event.turn.id && job.rootTurnId)
|
|
1364
|
+
this.retryLinks(event.threadId).set(event.turn.id, job.rootTurnId);
|
|
1365
|
+
}
|
|
1243
1366
|
if (event.type === "runtime") {
|
|
1244
1367
|
this.compatibilityConfirmation(event.diagnostics);
|
|
1245
1368
|
if (!event.diagnostics.ready || event.diagnostics.codexEpoch !== this.currentEpoch) {
|
|
@@ -1304,7 +1427,13 @@ class GatewayService {
|
|
|
1304
1427
|
return;
|
|
1305
1428
|
}
|
|
1306
1429
|
if (event.type === "turnError" || event.type === "turnProgress") {
|
|
1307
|
-
const changed = event.type === "turnError" ? this.projection.turnError(
|
|
1430
|
+
const changed = event.type === "turnError" ? this.projection.turnError(
|
|
1431
|
+
thread.id,
|
|
1432
|
+
event.turnId,
|
|
1433
|
+
event.message,
|
|
1434
|
+
event.willRetry,
|
|
1435
|
+
event
|
|
1436
|
+
) : this.projection.turnProgress(thread.id, event.turnId);
|
|
1308
1437
|
if (changed) this.publishThread(thread.id);
|
|
1309
1438
|
return;
|
|
1310
1439
|
}
|
|
@@ -1338,7 +1467,7 @@ class GatewayService {
|
|
|
1338
1467
|
if (isTerminal(event.turn.status)) {
|
|
1339
1468
|
this.store.finishTurn(thread.id, event.turn.id, this.codex.epoch);
|
|
1340
1469
|
this.projection.expireTurn(thread.id, event.turn.id);
|
|
1341
|
-
if (newCompletion) {
|
|
1470
|
+
if (newCompletion && !this.capacityRetries.pending(thread.id)) {
|
|
1342
1471
|
try {
|
|
1343
1472
|
this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
1344
1473
|
this.events.publishCompletion({
|