@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
package/dist/server/service.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
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";
|
|
5
|
+
import { readGithubRepository } from "./github-repository.js";
|
|
3
6
|
import { realpathSync } from "node:fs";
|
|
4
7
|
import { cp } from "node:fs/promises";
|
|
5
8
|
import { WorkspaceRegistry } from "./workspaces.js";
|
|
@@ -48,11 +51,16 @@ class GatewayService {
|
|
|
48
51
|
this.shell = new ShellService(this.codex);
|
|
49
52
|
this.events = new EventHub(options.limits?.eventBytes);
|
|
50
53
|
this.projection = new Projection(options.limits?.itemBytes);
|
|
54
|
+
this.capacityRetries = new CapacityRetries(
|
|
55
|
+
(job) => this.dispatchCapacityRetry(job),
|
|
56
|
+
(id) => this.publishThread(id)
|
|
57
|
+
);
|
|
51
58
|
this.currentEpoch = this.codex.epoch;
|
|
52
59
|
this.store.recover();
|
|
53
60
|
this.removeListener = this.codex.onEvent((event) => this.consume(event));
|
|
54
61
|
}
|
|
55
62
|
options;
|
|
63
|
+
capacityRetries;
|
|
56
64
|
projectClones;
|
|
57
65
|
shell;
|
|
58
66
|
events;
|
|
@@ -85,9 +93,27 @@ class GatewayService {
|
|
|
85
93
|
runtimeSettingsUpdates = /* @__PURE__ */ new Map();
|
|
86
94
|
inFlightOperations = /* @__PURE__ */ new Set();
|
|
87
95
|
pendingThreadCreations = /* @__PURE__ */ new Map();
|
|
96
|
+
capacityLinks = /* @__PURE__ */ new Map();
|
|
88
97
|
listProjects() {
|
|
89
98
|
return this.projects.list();
|
|
90
99
|
}
|
|
100
|
+
async getProjectGithubRepository(projectId) {
|
|
101
|
+
const project = this.projects.get(projectId);
|
|
102
|
+
const url = await readGithubRepository(project.canonicalRoot);
|
|
103
|
+
this.projects.get(projectId);
|
|
104
|
+
return { url };
|
|
105
|
+
}
|
|
106
|
+
async getThreadGithubRepository(threadId) {
|
|
107
|
+
let link = this.store.getThreadLink(threadId);
|
|
108
|
+
if (!link) {
|
|
109
|
+
this.normalize(await this.codex.readThreadMetadata(threadId));
|
|
110
|
+
link = this.store.getThreadLink(threadId);
|
|
111
|
+
}
|
|
112
|
+
const { cwd } = this.workspaces.assertOwnership(link.projectId, link.cwd, threadId);
|
|
113
|
+
const url = await readGithubRepository(cwd);
|
|
114
|
+
this.workspaces.assertOwnership(link.projectId, link.cwd, threadId);
|
|
115
|
+
return { url };
|
|
116
|
+
}
|
|
91
117
|
startShell(projectId, input) {
|
|
92
118
|
return this.perform("shell.start", projectId, input, async () => {
|
|
93
119
|
this.ensureCompatibilityConfirmed();
|
|
@@ -114,6 +140,8 @@ class GatewayService {
|
|
|
114
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);
|
|
115
141
|
}
|
|
116
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" };
|
|
117
145
|
if (this.projectClones.hasActive) return { ready: false, reason: "\u7B49\u5F85\u9879\u76EE\u514B\u9686\u5B8C\u6210\u6216\u53D6\u6D88" };
|
|
118
146
|
if (this.shell.active()) return { ready: false, reason: "\u7B49\u5F85 Shell \u547D\u4EE4\u7ED3\u675F" };
|
|
119
147
|
if (this.inFlightOperations.size || this.resumes.size || this.pendingThreadCreations.size)
|
|
@@ -161,7 +189,7 @@ class GatewayService {
|
|
|
161
189
|
return link ? link.projectId === projectId : entry.workspaceKey === project.workspaceKey;
|
|
162
190
|
});
|
|
163
191
|
const active = this.projection.values().find(
|
|
164
|
-
(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)))
|
|
165
193
|
);
|
|
166
194
|
if (pending || marker || active || this.shell.active(projectId))
|
|
167
195
|
throw new AppError(
|
|
@@ -435,6 +463,7 @@ class GatewayService {
|
|
|
435
463
|
updateArchiveState(threadId, archived) {
|
|
436
464
|
const thread = this.projection.get(threadId);
|
|
437
465
|
if (!thread || Boolean(thread.archived) === archived) return;
|
|
466
|
+
if (archived) this.capacityRetries.clear(threadId);
|
|
438
467
|
thread.archived = archived;
|
|
439
468
|
this.archiveRevisions.set(threadId, (this.archiveRevisions.get(threadId) ?? 0) + 1);
|
|
440
469
|
if (archived) this.releaseThreadOwnership(threadId);
|
|
@@ -476,6 +505,11 @@ class GatewayService {
|
|
|
476
505
|
throw new AppError("HISTORY_UNAVAILABLE", "\u5F53\u524D Codex \u4E0D\u652F\u6301\u5386\u53F2\u5206\u9875", 409);
|
|
477
506
|
const page = this.normalize(await this.codex.readThreadPage(threadId, cursor));
|
|
478
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
|
+
}
|
|
479
513
|
return page;
|
|
480
514
|
}
|
|
481
515
|
async exportThread(threadId) {
|
|
@@ -602,6 +636,7 @@ class GatewayService {
|
|
|
602
636
|
}
|
|
603
637
|
async resumeThread(threadId, input) {
|
|
604
638
|
return this.perform("thread.resume", threadId, input, async () => {
|
|
639
|
+
this.capacityRetries.clear(threadId);
|
|
605
640
|
const thread = await this.loadThread(threadId);
|
|
606
641
|
this.ensureReady();
|
|
607
642
|
this.ensureArchiveAvailable(thread);
|
|
@@ -703,11 +738,21 @@ class GatewayService {
|
|
|
703
738
|
});
|
|
704
739
|
}
|
|
705
740
|
async startTurn(threadId, input) {
|
|
741
|
+
return this.dispatchTurn(threadId, input);
|
|
742
|
+
}
|
|
743
|
+
async dispatchTurn(threadId, input, retry, expectedTurnId) {
|
|
706
744
|
return this.perform("turn.start", threadId, input, async () => {
|
|
745
|
+
if (!retry && !expectedTurnId && this.capacityRetries.view(threadId)?.status === "waiting")
|
|
746
|
+
this.capacityRetries.clear(threadId);
|
|
707
747
|
const thread = await this.loadThread(threadId);
|
|
708
748
|
this.ensureManaged(thread);
|
|
709
749
|
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
710
|
-
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))
|
|
711
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 });
|
|
712
757
|
this.ensureCompatibilityConfirmed();
|
|
713
758
|
this.store.dispatch(input.clientOperationId, {
|
|
@@ -715,6 +760,15 @@ class GatewayService {
|
|
|
715
760
|
workspaceKey: ownership.workspaceKey,
|
|
716
761
|
codexEpoch: this.codex.epoch
|
|
717
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);
|
|
718
772
|
this.turnBaselines.set(input.clientOperationId, new Set(thread.turns.map((turn2) => turn2.id)));
|
|
719
773
|
this.publishThread(threadId);
|
|
720
774
|
const revision = this.projection.revision(threadId);
|
|
@@ -728,15 +782,70 @@ class GatewayService {
|
|
|
728
782
|
this.store.attachTurn(input.clientOperationId, turn.id);
|
|
729
783
|
this.turnBaselines.delete(input.clientOperationId);
|
|
730
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);
|
|
731
788
|
const projectedTurn = this.projection.get(threadId)?.turns.find((entry) => entry.id === turn.id);
|
|
732
789
|
if (projectedTurn && isTerminal(projectedTurn.status))
|
|
733
790
|
this.store.finishTurn(threadId, turn.id, this.codex.epoch);
|
|
734
791
|
this.publishThread(threadId);
|
|
735
|
-
return {
|
|
792
|
+
return {
|
|
793
|
+
result: { threadId, turnId: turn.id, ...retryOf ? { retryOf } : {} },
|
|
794
|
+
turnId: turn.id
|
|
795
|
+
};
|
|
736
796
|
});
|
|
737
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
|
+
}
|
|
738
846
|
async compactThread(threadId, input) {
|
|
739
847
|
return this.perform("thread.compact", threadId, input, async () => {
|
|
848
|
+
this.capacityRetries.clear(threadId);
|
|
740
849
|
const thread = await this.loadThread(threadId);
|
|
741
850
|
this.ensureManaged(thread);
|
|
742
851
|
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
@@ -761,6 +870,7 @@ class GatewayService {
|
|
|
761
870
|
}
|
|
762
871
|
async startReview(threadId, input) {
|
|
763
872
|
return this.perform("review.start", threadId, input, async () => {
|
|
873
|
+
this.capacityRetries.clear(threadId);
|
|
764
874
|
const thread = await this.loadThread(threadId);
|
|
765
875
|
this.ensureManaged(thread);
|
|
766
876
|
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
@@ -898,6 +1008,7 @@ class GatewayService {
|
|
|
898
1008
|
}
|
|
899
1009
|
async shutdown() {
|
|
900
1010
|
this.accepting = false;
|
|
1011
|
+
this.capacityRetries.clearAll();
|
|
901
1012
|
await this.projectClones.close();
|
|
902
1013
|
this.store.recover();
|
|
903
1014
|
this.projection.runtimeReset();
|
|
@@ -939,6 +1050,8 @@ class GatewayService {
|
|
|
939
1050
|
const action = this.projection.resolveAction(target, "pending");
|
|
940
1051
|
if (action?.threadId) this.publishThread(action.threadId);
|
|
941
1052
|
}
|
|
1053
|
+
if (kind === "turn.start")
|
|
1054
|
+
this.capacityRetries.dispatchFailed(target, input.clientOperationId);
|
|
942
1055
|
if (dispatched && kind === "turn.start") this.publishThread(target);
|
|
943
1056
|
this.events.publish("operation", operation);
|
|
944
1057
|
if (uncertain)
|
|
@@ -1172,8 +1285,25 @@ class GatewayService {
|
|
|
1172
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))
|
|
1173
1286
|
);
|
|
1174
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
|
+
}
|
|
1175
1296
|
threadView(thread) {
|
|
1176
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";
|
|
1177
1307
|
result.archived = thread.archived ?? false;
|
|
1178
1308
|
result.historyVisible = this.historyVisible(thread);
|
|
1179
1309
|
const marker = this.store.activeForThread(thread.id);
|
|
@@ -1222,6 +1352,17 @@ class GatewayService {
|
|
|
1222
1352
|
}
|
|
1223
1353
|
}
|
|
1224
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
|
+
}
|
|
1225
1366
|
if (event.type === "runtime") {
|
|
1226
1367
|
this.compatibilityConfirmation(event.diagnostics);
|
|
1227
1368
|
if (!event.diagnostics.ready || event.diagnostics.codexEpoch !== this.currentEpoch) {
|
|
@@ -1286,7 +1427,13 @@ class GatewayService {
|
|
|
1286
1427
|
return;
|
|
1287
1428
|
}
|
|
1288
1429
|
if (event.type === "turnError" || event.type === "turnProgress") {
|
|
1289
|
-
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);
|
|
1290
1437
|
if (changed) this.publishThread(thread.id);
|
|
1291
1438
|
return;
|
|
1292
1439
|
}
|
|
@@ -1320,7 +1467,7 @@ class GatewayService {
|
|
|
1320
1467
|
if (isTerminal(event.turn.status)) {
|
|
1321
1468
|
this.store.finishTurn(thread.id, event.turn.id, this.codex.epoch);
|
|
1322
1469
|
this.projection.expireTurn(thread.id, event.turn.id);
|
|
1323
|
-
if (newCompletion) {
|
|
1470
|
+
if (newCompletion && !this.capacityRetries.pending(thread.id)) {
|
|
1324
1471
|
try {
|
|
1325
1472
|
this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
1326
1473
|
this.events.publishCompletion({
|