@cmmd-center/forge 0.13.77 → 0.13.79
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/dist/{BunHttpServer-DjilmGOM.mjs → BunHttpServer-BpaVqxli.mjs} +6 -6
- package/dist/{BunHttpServer-CXndtc1j.cjs → BunHttpServer-DTG5XWSi.cjs} +7 -7
- package/dist/{BunPTY-BiTlUj63.cjs → BunPTY-DkR9y_Jg.cjs} +3 -3
- package/dist/{BunPTY-Csgwhbi5.mjs → BunPTY-DsRB6_t-.mjs} +3 -3
- package/dist/{BunServices-BZKzHdrG.mjs → BunServices-ZpOiNNsP.mjs} +3 -3
- package/dist/{BunServices-DsWG7VM1.cjs → BunServices-blx5Lx2N.cjs} +3 -3
- package/dist/{Cache-BmGc_2BZ.cjs → Cache-D-JaJz5Q.cjs} +3 -3
- package/dist/{Cache-D57GWb9-.mjs → Cache-DOwpGEYJ.mjs} +3 -3
- package/dist/{Config-Cdfpphpo.mjs → Config-BnFFyBjD.mjs} +2 -2
- package/dist/{Config-CwKCCk14.cjs → Config-pbf0GGHJ.cjs} +2 -2
- package/dist/{HttpServer-Bclwvu0I.mjs → HttpServer-5UTabe01.mjs} +3 -3
- package/dist/{HttpServer-BBzRBmVt.cjs → HttpServer-DZ6iaxyk.cjs} +12 -12
- package/dist/{NodeHttpServer-Ds3Gp1Gp.mjs → NodeHttpServer-DePOzmYy.mjs} +7 -7
- package/dist/{NodeHttpServer-B4dCKqYY.cjs → NodeHttpServer-RV5QsVeb.cjs} +7 -7
- package/dist/{NodePTY-DgXWLtWp.mjs → NodePTY-Dduv48Vi.mjs} +4 -4
- package/dist/{NodePTY-BJ7874MO.cjs → NodePTY-DkaJNgpi.cjs} +4 -4
- package/dist/{NodeServices-CtC4a-h5.cjs → NodeServices-BkfWZHfh.cjs} +3 -3
- package/dist/{NodeServices-D3IxOzHl.mjs → NodeServices-Bt1GwPBR.mjs} +3 -3
- package/dist/{NodeServices-BvdIc9Vh.cjs → NodeServices-CocSeCvl.cjs} +1 -1
- package/dist/{NodeSqliteClient-D29w60zZ.mjs → NodeSqliteClient-BpmyZDcj.mjs} +6 -6
- package/dist/{NodeSqliteClient-CPsJxxdD.cjs → NodeSqliteClient-VL8Dgl03.cjs} +7 -7
- package/dist/{NodeTerminal-9iHZIdt1.mjs → NodeTerminal-CeaWke6-.mjs} +3 -3
- package/dist/{NodeTerminal-CO-i-6Je.cjs → NodeTerminal-DQESKR0W.cjs} +3 -3
- package/dist/{PTY-BzdbZ0Ym.mjs → PTY-BRT13Y8S.mjs} +2 -2
- package/dist/{PTY-B4ZctbwQ.cjs → PTY-C8ypptmz.cjs} +2 -2
- package/dist/{Path-CZJ1gPPo.mjs → Path-BIaGSU2M.mjs} +34 -3
- package/dist/{Path-BQOH_fWQ.cjs → Path-Dp3btyUm.cjs} +40 -3
- package/dist/{Schema-DOBWF8gQ.mjs → Schema-Ay3DUM83.mjs} +82 -11
- package/dist/{Schema-CDCJ3xZo.cjs → Schema-DxddW8-V.cjs} +105 -10
- package/dist/{SqlError-BMBkezUO.mjs → SqlError-BBR7DPrQ.mjs} +3 -3
- package/dist/{SqlError-g4pLzNWR.cjs → SqlError-BqOsoG0V.cjs} +3 -3
- package/dist/{SqliteClient-k0pbfeIC.cjs → SqliteClient-BgoGs_ji.cjs} +6 -6
- package/dist/{SqliteClient-Bgo_s5sA.mjs → SqliteClient-DQYXFVMu.mjs} +5 -5
- package/dist/bin.cjs +458 -49
- package/dist/bin.mjs +455 -46
- package/dist/client/assets/{DiffPanel-CtO6uWUR.js → DiffPanel-DxiNj12C.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-D0ANxwBg.js → DiffPanel.logic-D7xB9Erg.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-C_jUanNz.js → DiffPanelShell-1dMsYoGN.js} +115 -112
- package/dist/client/assets/{DiffWorkerPoolProvider-BAShH3us.js → DiffWorkerPoolProvider-BS3XcZ9G.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-BK_z4V5D.js → PullRequestCodePanel-BXvOtt5U.js} +2 -2
- package/dist/client/assets/{Virtualizer-D6ltPf0M.js → Virtualizer-DcohbA3j.js} +2 -2
- package/dist/client/assets/{index-Wp8UVHJj.js → index-BGgOXu17.js} +13 -13
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
const require_chunk = require("./chunk-BhaIgcZK.cjs");
|
|
5
|
-
const require_Schema$1 = require("./Schema-
|
|
6
|
-
const require_Path = require("./Path-
|
|
7
|
-
const require_NodeTerminal = require("./NodeTerminal-
|
|
8
|
-
const require_NodeServices = require("./NodeServices-
|
|
9
|
-
const require_HttpServer = require("./HttpServer-
|
|
10
|
-
const require_Config = require("./Config-
|
|
11
|
-
const require_Cache = require("./Cache-
|
|
12
|
-
const require_SqlError = require("./SqlError-
|
|
5
|
+
const require_Schema$1 = require("./Schema-DxddW8-V.cjs");
|
|
6
|
+
const require_Path = require("./Path-Dp3btyUm.cjs");
|
|
7
|
+
const require_NodeTerminal = require("./NodeTerminal-DQESKR0W.cjs");
|
|
8
|
+
const require_NodeServices = require("./NodeServices-BkfWZHfh.cjs");
|
|
9
|
+
const require_HttpServer = require("./HttpServer-DZ6iaxyk.cjs");
|
|
10
|
+
const require_Config = require("./Config-pbf0GGHJ.cjs");
|
|
11
|
+
const require_Cache = require("./Cache-D-JaJz5Q.cjs");
|
|
12
|
+
const require_SqlError = require("./SqlError-BqOsoG0V.cjs");
|
|
13
13
|
const require_Mime = require("./Mime-Bilnh8zE.cjs");
|
|
14
14
|
const require_esm = require("./esm-BZhXwX5p.cjs");
|
|
15
15
|
const require_runtimeControlAuthority = require("./runtimeControlAuthority-DCitZFTk.cjs");
|
|
16
|
-
const require_PTY = require("./PTY-
|
|
16
|
+
const require_PTY = require("./PTY-C8ypptmz.cjs");
|
|
17
17
|
const require_settle = require("./settle-B6Rn65UV.cjs");
|
|
18
18
|
let node_child_process = require("node:child_process");
|
|
19
19
|
node_child_process = require_chunk.__toESM(node_child_process);
|
|
@@ -66275,7 +66275,7 @@ function normalizeNumberish(value) {
|
|
|
66275
66275
|
}
|
|
66276
66276
|
//#endregion
|
|
66277
66277
|
//#region package.json
|
|
66278
|
-
var version$1 = "0.13.
|
|
66278
|
+
var version$1 = "0.13.79";
|
|
66279
66279
|
//#endregion
|
|
66280
66280
|
//#region src/sentry.ts
|
|
66281
66281
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -76953,7 +76953,7 @@ const makeNoSerialization$1 = /* @__PURE__ */ require_Schema$1.fnUntraced(functi
|
|
|
76953
76953
|
const span = disableTracing ? void 0 : yield* require_Schema$1.makeSpanScoped(`${spanPrefix}.${rpc._tag}`, { attributes: options.spanAttributes });
|
|
76954
76954
|
const fiber = require_Path.getCurrent();
|
|
76955
76955
|
const id = generateRequestId();
|
|
76956
|
-
const scope = require_Schema$1.getUnsafe(fiber.context, require_Schema$1.Scope);
|
|
76956
|
+
const scope = require_Schema$1.getUnsafe$1(fiber.context, require_Schema$1.Scope);
|
|
76957
76957
|
yield* require_Schema$1.addFinalizerExit(scope, (exit) => {
|
|
76958
76958
|
if (!entries.has(id)) return require_Schema$1.void_;
|
|
76959
76959
|
entries.delete(id);
|
|
@@ -77444,7 +77444,7 @@ const makeNoSerialization = /* @__PURE__ */ require_Schema$1.fnUntraced(function
|
|
|
77444
77444
|
});
|
|
77445
77445
|
});
|
|
77446
77446
|
const applyMiddleware = (context, handler, options) => {
|
|
77447
|
-
for (const service of options.rpc.middlewares) handler = require_Schema$1.getUnsafe(context, service)(handler, options);
|
|
77447
|
+
for (const service of options.rpc.middlewares) handler = require_Schema$1.getUnsafe$1(context, service)(handler, options);
|
|
77448
77448
|
return handler;
|
|
77449
77449
|
};
|
|
77450
77450
|
/**
|
|
@@ -115412,7 +115412,7 @@ const fromPool = /* @__PURE__ */ require_Schema$1.fnUntraced(function* (options)
|
|
|
115412
115412
|
}, client ? require_Schema$1.succeed(client) : reserveRaw);
|
|
115413
115413
|
const reserveRaw = require_Schema$1.callback((resume) => {
|
|
115414
115414
|
const fiber = require_Path.getCurrent();
|
|
115415
|
-
const scope = require_Schema$1.getUnsafe(fiber.context, require_Schema$1.Scope);
|
|
115415
|
+
const scope = require_Schema$1.getUnsafe$1(fiber.context, require_Schema$1.Scope);
|
|
115416
115416
|
let cause = void 0;
|
|
115417
115417
|
function onError(cause_) {
|
|
115418
115418
|
cause = cause_;
|
|
@@ -119238,8 +119238,8 @@ require_Schema$1.effectDiscard(runMigrations());
|
|
|
119238
119238
|
//#endregion
|
|
119239
119239
|
//#region src/persistence/Layers/Sqlite.ts
|
|
119240
119240
|
const defaultSqliteClientLoaders = {
|
|
119241
|
-
bun: () => Promise.resolve().then(() => require("./SqliteClient-
|
|
119242
|
-
node: () => Promise.resolve().then(() => require("./NodeSqliteClient-
|
|
119241
|
+
bun: () => Promise.resolve().then(() => require("./SqliteClient-BgoGs_ji.cjs")),
|
|
119242
|
+
node: () => Promise.resolve().then(() => require("./NodeSqliteClient-VL8Dgl03.cjs"))
|
|
119243
119243
|
};
|
|
119244
119244
|
const makeRuntimeSqliteLayer = require_Schema$1.fn("makeRuntimeSqliteLayer")(function* (config) {
|
|
119245
119245
|
const loader = defaultSqliteClientLoaders[process.versions.bun !== void 0 ? "bun" : "node"];
|
|
@@ -213922,6 +213922,7 @@ function categorizePublicProviderError(message) {
|
|
|
213922
213922
|
if (/\bforge_provider_model_not_allowed\b/.test(m) || /\bnot\s+scoped\s+to\s+the\s+requested\s+model\b/.test(m)) return "The workspace AI credential does not cover the model this turn asked for. Retry, or pick that model for the thread.";
|
|
213923
213923
|
if (/\bmodel\b[\s\S]*\b(not\s+(found|available|supported)|does\s+not\s+exist|unsupported|invalid|unknown)\b/.test(m) || /\b(unsupported|unknown|invalid)\b[\s\S]*\bmodel\b/.test(m) || /\b(model_not_found|invalid_model|model_not_supported)\b/.test(m)) return "The selected model isn't available right now. Pick a different model and retry.";
|
|
213924
213924
|
if (isProviderRateOrQuotaError(m)) return "The AI service is rate-limited or out of quota. Wait a moment and retry.";
|
|
213925
|
+
if (/_credential_reconnect_required\b/.test(m)) return "The workspace AI credential needs to be reconnected. Reconnect it in Settings > Providers, then retry.";
|
|
213925
213926
|
if (/\b(unauthorized|forbidden|authentication\s+(failed|error|required)|invalid\s+api\s+key|expired\s+(token|credential))\b/.test(m) || /\b40[13]\b/.test(m)) return "Workspace AI authentication failed. Reconnect the workspace and retry.";
|
|
213926
213927
|
if (/\b(timed?\s*out|timeout|overloaded|service\s+unavailable)\b/.test(m) || /\b50[234]\b/.test(m)) return "The AI service is temporarily overloaded. Retry in a moment.";
|
|
213927
213928
|
return null;
|
|
@@ -292537,6 +292538,52 @@ function applyThreadGoalPreamble(goal, input) {
|
|
|
292537
292538
|
return trimmedInput.length > 0 ? `${preamble}\n\n${trimmedInput}` : preamble;
|
|
292538
292539
|
}
|
|
292539
292540
|
//#endregion
|
|
292541
|
+
//#region src/orchestration/providerRetryableFailure.ts
|
|
292542
|
+
/**
|
|
292543
|
+
* Classifies a provider turn-start failure as a retryable rate-limit or
|
|
292544
|
+
* transient-availability failure (429/503), a terminal failure that names why
|
|
292545
|
+
* it will never clear on its own, or unrecognized (null).
|
|
292546
|
+
*
|
|
292547
|
+
* CMMD-Center/forge#3558: a 429 from CMMD's provider egress used to leave no
|
|
292548
|
+
* trace at all, because nothing in the turn-start path recognized it as a
|
|
292549
|
+
* distinct, retryable failure. `ProviderCommandReactor` uses this to decide
|
|
292550
|
+
* whether to retry a turn start with backoff, and to name the provider,
|
|
292551
|
+
* status and Retry-After in the `provider.turn.start.failed` activity CMMD's
|
|
292552
|
+
* watchdog reads once retries are exhausted.
|
|
292553
|
+
*
|
|
292554
|
+
* Reuses `isProviderRateOrQuotaError` (the same predicate the public-copy
|
|
292555
|
+
* sanitizer uses to recognize a rate-limit message) rather than restating its
|
|
292556
|
+
* word list here.
|
|
292557
|
+
*/
|
|
292558
|
+
const SERVICE_UNAVAILABLE_PATTERN = /\b(503|service\s+unavailable)\b/i;
|
|
292559
|
+
const RATE_LIMITED_PAST_TENSE_PATTERN = /\brate[\s-]?limit(?:ed|ing)?\b/i;
|
|
292560
|
+
const QUOTA_OR_USAGE_LIMIT_PATTERN = /\b(usage[ _-]limit|usage_limit_reached|quota|insufficient_quota)\b/i;
|
|
292561
|
+
const EXPLICIT_429_PATTERN = /\b429\b/;
|
|
292562
|
+
const RETRY_AFTER_SECONDS_PATTERN = /retry-after[^0-9]{0,10}(\d+)|(?:try again|retry)\s+in\s+(\d+)\s*(?:s\b|sec(?:onds?)?\b)?/i;
|
|
292563
|
+
function parseRetryAfterSeconds(detail) {
|
|
292564
|
+
const match = detail.match(RETRY_AFTER_SECONDS_PATTERN);
|
|
292565
|
+
if (!match) return null;
|
|
292566
|
+
const raw = match[1] ?? match[2];
|
|
292567
|
+
const seconds = raw ? Number(raw) : NaN;
|
|
292568
|
+
return Number.isFinite(seconds) ? seconds : null;
|
|
292569
|
+
}
|
|
292570
|
+
function classifyRetryableProviderFailure(detail) {
|
|
292571
|
+
if (!detail || detail.trim().length === 0) return null;
|
|
292572
|
+
if (QUOTA_OR_USAGE_LIMIT_PATTERN.test(detail) && !EXPLICIT_429_PATTERN.test(detail)) return {
|
|
292573
|
+
kind: "terminal",
|
|
292574
|
+
reason: "quota",
|
|
292575
|
+
message: detail
|
|
292576
|
+
};
|
|
292577
|
+
const isRateLimited = isProviderRateOrQuotaError(detail) || RATE_LIMITED_PAST_TENSE_PATTERN.test(detail);
|
|
292578
|
+
const isUnavailable = SERVICE_UNAVAILABLE_PATTERN.test(detail);
|
|
292579
|
+
if (!isRateLimited && !isUnavailable) return null;
|
|
292580
|
+
return {
|
|
292581
|
+
kind: "retryable",
|
|
292582
|
+
status: isRateLimited ? 429 : 503,
|
|
292583
|
+
retryAfterSeconds: parseRetryAfterSeconds(detail)
|
|
292584
|
+
};
|
|
292585
|
+
}
|
|
292586
|
+
//#endregion
|
|
292540
292587
|
//#region src/orchestration/Layers/ProviderCommandReactor.ts
|
|
292541
292588
|
function toNonEmptyProviderInput(value) {
|
|
292542
292589
|
const normalized = value?.trim();
|
|
@@ -292557,6 +292604,40 @@ const HANDLED_TURN_START_KEY_MAX = 1e4;
|
|
|
292557
292604
|
const HANDLED_TURN_START_KEY_TTL = require_Schema$1.minutes(30);
|
|
292558
292605
|
const DEFAULT_RUNTIME_MODE = "full-access";
|
|
292559
292606
|
const DEFAULT_THREAD_TITLE = "New thread";
|
|
292607
|
+
/**
|
|
292608
|
+
* Bounds retrying a turn-start ack (`turn/start`) that failed with a
|
|
292609
|
+
* retryable status (429/503). Kept well under `withProviderTurnStartTimeout`'s
|
|
292610
|
+
* 45 s budget: a Retry-After longer than fits here would otherwise blow that
|
|
292611
|
+
* timeout and end the turn as an unreplicated `provider.turn.start.timeout`
|
|
292612
|
+
* instead of the replicated `provider.turn.start.failed` CMMD's watchdog reads
|
|
292613
|
+
* (CMMD-Center/forge#3558).
|
|
292614
|
+
*/
|
|
292615
|
+
const PROVIDER_RETRY_MAX_ATTEMPTS = 3;
|
|
292616
|
+
const PROVIDER_RETRY_BASE_DELAY = require_Schema$1.millis(100);
|
|
292617
|
+
/** The longest this loop will ever wait for a single attempt's Retry-After. */
|
|
292618
|
+
const PROVIDER_RETRY_MAX_DELAY_PER_ATTEMPT = require_Schema$1.seconds(20);
|
|
292619
|
+
/**
|
|
292620
|
+
* The longest this loop will ever sleep in total, across every attempt,
|
|
292621
|
+
* counted from the first failure. Two sleeps at the per-attempt cap above
|
|
292622
|
+
* would already reach 40s on their own, leaving nothing for the
|
|
292623
|
+
* `ensureSessionForThread` preamble work and the `sendTurn` round trips that
|
|
292624
|
+
* share `withProviderTurnStartTimeout`'s 45s budget with this loop. 30s keeps
|
|
292625
|
+
* 15s of headroom for that other work. A Retry-After that would need more
|
|
292626
|
+
* than what remains fails the turn immediately and names the real ask,
|
|
292627
|
+
* rather than sleeping a truncated amount and 429ing again anyway once it
|
|
292628
|
+
* wakes up (CMMD-Center/forge#3558 review item 4).
|
|
292629
|
+
*/
|
|
292630
|
+
const PROVIDER_RETRY_TOTAL_SLEEP_BUDGET = require_Schema$1.seconds(30);
|
|
292631
|
+
/**
|
|
292632
|
+
* How long the reactor watches for `turn.started` (or a terminal failure)
|
|
292633
|
+
* after a turn-start ack succeeds, before treating silence as a failure.
|
|
292634
|
+
* Codex retries a 429 internally and reports every attempt as a bare `error`
|
|
292635
|
+
* notification with `willRetry`, none of which reach CMMD on their own
|
|
292636
|
+
* (mapped to `runtime.warning`, which does not replicate) — this is the
|
|
292637
|
+
* budget for those internal retries to either land or report a final failure
|
|
292638
|
+
* (CMMD-Center/forge#3558).
|
|
292639
|
+
*/
|
|
292640
|
+
const PROVIDER_TURN_START_WATCH_BUDGET = require_Schema$1.minutes(3);
|
|
292560
292641
|
const isProviderValidationError = require_Schema$1.is(ProviderValidationError);
|
|
292561
292642
|
const isProviderKind = require_Schema$1.is(ProviderKind);
|
|
292562
292643
|
function isPathInsideRoot(candidatePath, rootPath) {
|
|
@@ -292701,6 +292782,176 @@ function takeValidatedContinuationSession(input) {
|
|
|
292701
292782
|
function discardOwnedContinuationSession(staged, threadId) {
|
|
292702
292783
|
if (staged?.threadId === threadId) takeExactThreadContinuationSession(staged);
|
|
292703
292784
|
}
|
|
292785
|
+
/** The message text and, when the error carries one, the provider name it named. */
|
|
292786
|
+
function sendTurnFailureDetail(error) {
|
|
292787
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
292788
|
+
const provider = error !== null && typeof error === "object" && "provider" in error && typeof error.provider === "string" ? error.provider : void 0;
|
|
292789
|
+
return provider !== void 0 ? {
|
|
292790
|
+
message,
|
|
292791
|
+
provider
|
|
292792
|
+
} : { message };
|
|
292793
|
+
}
|
|
292794
|
+
/**
|
|
292795
|
+
* The delay before the next `sendTurn` attempt: the provider's own
|
|
292796
|
+
* Retry-After when it gave one, otherwise exponential backoff from
|
|
292797
|
+
* `PROVIDER_RETRY_BASE_DELAY`. Deliberately not capped here --
|
|
292798
|
+
* `sendTurnWithRetry` decides whether the desired delay fits the per-attempt
|
|
292799
|
+
* and total budgets, and fails outright rather than silently sleeping less
|
|
292800
|
+
* than the provider asked for (CMMD-Center/forge#3558 review item 4).
|
|
292801
|
+
*/
|
|
292802
|
+
function desiredRetryDelay(input) {
|
|
292803
|
+
if (input.retryAfterSeconds !== null) return require_Schema$1.seconds(input.retryAfterSeconds);
|
|
292804
|
+
return require_Schema$1.times(PROVIDER_RETRY_BASE_DELAY, 2 ** (input.attempt - 1));
|
|
292805
|
+
}
|
|
292806
|
+
/**
|
|
292807
|
+
* The `provider.turn.start.failed` detail once retries are exhausted. Names
|
|
292808
|
+
* the provider, the status every attempt answered, how many attempts ran, and
|
|
292809
|
+
* the Retry-After the provider asked for, so CMMD's watchdog (and whoever
|
|
292810
|
+
* reads the failed Work Session) knows this was a rate limit rather than an
|
|
292811
|
+
* unspecified refusal.
|
|
292812
|
+
*/
|
|
292813
|
+
function retryExhaustedFailureDetail(input) {
|
|
292814
|
+
const providerLabel = input.provider ? `The ${input.provider} provider` : "The provider";
|
|
292815
|
+
const retryAfter = input.classified.retryAfterSeconds !== null ? ` It asked to retry after ${input.classified.retryAfterSeconds}s.` : "";
|
|
292816
|
+
return `${providerLabel} responded ${input.classified.status} on every attempt (${input.attempts} of ${input.attempts}).${retryAfter} ${input.underlyingMessage}`;
|
|
292817
|
+
}
|
|
292818
|
+
/**
|
|
292819
|
+
* The `provider.turn.start.failed` detail when honoring the provider's own
|
|
292820
|
+
* Retry-After would need more time than `PROVIDER_RETRY_MAX_DELAY_PER_ATTEMPT`
|
|
292821
|
+
* or the loop's remaining `PROVIDER_RETRY_TOTAL_SLEEP_BUDGET` allow. Fails the
|
|
292822
|
+
* turn immediately and names the real ask, rather than sleeping a truncated
|
|
292823
|
+
* amount and 429ing again once it wakes up (CMMD-Center/forge#3558 review
|
|
292824
|
+
* item 4).
|
|
292825
|
+
*/
|
|
292826
|
+
function retryBudgetExceededFailureDetail(input) {
|
|
292827
|
+
return `${input.provider ? `The ${input.provider} provider` : "The provider"} asked to retry after ${input.classified.retryAfterSeconds !== null ? `${input.classified.retryAfterSeconds}s` : "an unspecified time"}, longer than Forge allows for a turn-start retry (stopped after ${input.attempts} of ${PROVIDER_RETRY_MAX_ATTEMPTS} attempts). ${input.underlyingMessage}`;
|
|
292828
|
+
}
|
|
292829
|
+
/**
|
|
292830
|
+
* The `provider.turn.start.failed` detail when the turn-start ack succeeded
|
|
292831
|
+
* but no `turn.started`, no terminal failure, and (past the watch budget) no
|
|
292832
|
+
* event at all ever arrived for it — Codex retrying a 429 with no observable
|
|
292833
|
+
* progress until it either recovers or gives up.
|
|
292834
|
+
*/
|
|
292835
|
+
function turnStartWatchTimedOutDetail(lastRetrySignal) {
|
|
292836
|
+
if (lastRetrySignal === null) return "The provider accepted the turn but never reported it starting, and reported no error either.";
|
|
292837
|
+
if (lastRetrySignal.kind === "terminal") return `The provider reported a failure that will not clear by retrying, and never started the turn. ${lastRetrySignal.message}`;
|
|
292838
|
+
const retryAfter = lastRetrySignal.retryAfterSeconds !== null ? ` It last asked to retry after ${lastRetrySignal.retryAfterSeconds}s.` : "";
|
|
292839
|
+
return `The provider kept answering ${lastRetrySignal.status} without ever starting the turn or reporting a final error.${retryAfter}`;
|
|
292840
|
+
}
|
|
292841
|
+
/**
|
|
292842
|
+
* The events that end the watch started after a turn-start ack: the turn
|
|
292843
|
+
* actually starting, that same turn completing (a Codex retry loop can settle
|
|
292844
|
+
* `turn/completed` with no `turn/started` in between), or a runtime error for
|
|
292845
|
+
* THIS turn (Codex's final `error` notification once `willRetry` turns false
|
|
292846
|
+
* — see `mapCodexRuntimeErrorEvent`).
|
|
292847
|
+
*
|
|
292848
|
+
* `runtime.error` matches when it carries no turn id at all (a genuine
|
|
292849
|
+
* session-level error) or carries this exact turn's id. This buffer starts
|
|
292850
|
+
* before the ack (`openTurnStartEventBuffer`), so a `runtime.error` for a
|
|
292851
|
+
* DIFFERENT, still-in-flight turn on the same thread is now visible to this
|
|
292852
|
+
* watch too; without the turn id check, that event would wrongly end this
|
|
292853
|
+
* turn's watch (forge-runtime-supervision's async-hold-and-refresh interleaving
|
|
292854
|
+
* review).
|
|
292855
|
+
*/
|
|
292856
|
+
function isTurnStartWatchTerminalEvent(event, turnId) {
|
|
292857
|
+
if (event.type === "turn.started") return event.turnId === turnId;
|
|
292858
|
+
if (event.type === "turn.completed") return event.turnId === turnId;
|
|
292859
|
+
return event.type === "runtime.error" && (event.turnId === void 0 || event.turnId === turnId);
|
|
292860
|
+
}
|
|
292861
|
+
/**
|
|
292862
|
+
* Retries `sendTurn` on a retryable ack failure (429/503), with backoff
|
|
292863
|
+
* bounded by `PROVIDER_RETRY_MAX_ATTEMPTS`, `PROVIDER_RETRY_MAX_DELAY_PER_ATTEMPT`
|
|
292864
|
+
* and `PROVIDER_RETRY_TOTAL_SLEEP_BUDGET`. A non-retryable failure (401, the
|
|
292865
|
+
* #3552 broker-authority miss, quota exhaustion, an unrelated adapter error)
|
|
292866
|
+
* fails on the first attempt.
|
|
292867
|
+
*
|
|
292868
|
+
* Never resubmits after an ack succeeds: codex has already created the turn
|
|
292869
|
+
* by then, and a second `turn/start` would duplicate it and resend the
|
|
292870
|
+
* user's message. Once retries are exhausted — or the provider's own
|
|
292871
|
+
* Retry-After would need more time than this loop allows — the failure this
|
|
292872
|
+
* raises names the provider, the status, and the real Retry-After, rather
|
|
292873
|
+
* than whatever the underlying adapter error happened to say.
|
|
292874
|
+
*
|
|
292875
|
+
* Exported and kept at module scope (out of `ProviderCommandReactor`'s
|
|
292876
|
+
* `make`) so it can be tested directly against a fake clock, without a real
|
|
292877
|
+
* 20-30s wait (CMMD-Center/forge#3558 review item 4).
|
|
292878
|
+
*/
|
|
292879
|
+
const sendTurnWithRetry = require_Schema$1.fn("sendTurnWithRetry")(function* (input) {
|
|
292880
|
+
let attempt = 1;
|
|
292881
|
+
let totalSlept = require_Schema$1.zero;
|
|
292882
|
+
while (true) {
|
|
292883
|
+
const outcome = yield* require_Schema$1.result(input.sendTurn(input.turnInput));
|
|
292884
|
+
if (outcome._tag === "Success") return outcome.success;
|
|
292885
|
+
const { message, provider } = sendTurnFailureDetail(outcome.failure);
|
|
292886
|
+
const classified = classifyRetryableProviderFailure(message);
|
|
292887
|
+
if (classified === null || classified.kind === "terminal") return yield* outcome.failure;
|
|
292888
|
+
const desired = desiredRetryDelay({
|
|
292889
|
+
attempt,
|
|
292890
|
+
retryAfterSeconds: classified.retryAfterSeconds
|
|
292891
|
+
});
|
|
292892
|
+
const remainingBudget = require_Schema$1.subtract(PROVIDER_RETRY_TOTAL_SLEEP_BUDGET, totalSlept);
|
|
292893
|
+
const exceedsBudget = require_Schema$1.isGreaterThan$1(desired, PROVIDER_RETRY_MAX_DELAY_PER_ATTEMPT) || require_Schema$1.isGreaterThan$1(desired, remainingBudget);
|
|
292894
|
+
if (attempt >= PROVIDER_RETRY_MAX_ATTEMPTS || exceedsBudget) return yield* new ProviderAdapterRequestError({
|
|
292895
|
+
provider: provider ?? "provider",
|
|
292896
|
+
method: "turn/start",
|
|
292897
|
+
detail: exceedsBudget ? retryBudgetExceededFailureDetail({
|
|
292898
|
+
provider,
|
|
292899
|
+
classified,
|
|
292900
|
+
attempts: attempt,
|
|
292901
|
+
underlyingMessage: message
|
|
292902
|
+
}) : retryExhaustedFailureDetail({
|
|
292903
|
+
provider,
|
|
292904
|
+
classified,
|
|
292905
|
+
attempts: attempt,
|
|
292906
|
+
underlyingMessage: message
|
|
292907
|
+
}),
|
|
292908
|
+
cause: outcome.failure
|
|
292909
|
+
});
|
|
292910
|
+
yield* require_Schema$1.sleep(desired);
|
|
292911
|
+
totalSlept = require_Schema$1.sum(totalSlept, desired);
|
|
292912
|
+
attempt += 1;
|
|
292913
|
+
}
|
|
292914
|
+
});
|
|
292915
|
+
/**
|
|
292916
|
+
* Runs the flow after a turn-start ack attempt. On a failed or skipped ack,
|
|
292917
|
+
* closes the event buffer and reports through `onFailure`/`onSkipped`; on a
|
|
292918
|
+
* successful ack, hands off to `onSent`, which forks the turn-start watch
|
|
292919
|
+
* that then owns the buffer for the rest of its life.
|
|
292920
|
+
*
|
|
292921
|
+
* Wrapped in its own `Effect.onInterrupt` covering `send` through `onSent`:
|
|
292922
|
+
* an interrupt landing anywhere in that span — mid-ack, mid the retry's own
|
|
292923
|
+
* backoff, or in the gap between a successful ack and the watch actually
|
|
292924
|
+
* being forked — still closes the buffer. Once `onSent` returns (the watch
|
|
292925
|
+
* has been forked), this effect has completed normally and the wrapper no
|
|
292926
|
+
* longer applies from there on; the watch's own `Effect.ensuring` owns the
|
|
292927
|
+
* buffer from that point (CodeRabbit on CMMD-Center/forge#3558: the buffer
|
|
292928
|
+
* never closed on a pre-fork interrupt).
|
|
292929
|
+
*
|
|
292930
|
+
* Deliberately does not release the turn-start hold: that stays the
|
|
292931
|
+
* caller's job. `recoverProviderTurnStartTimeout`'s `releaseHold` and
|
|
292932
|
+
* `processDomainEventSafely`'s own `Effect.onInterrupt` already cover every
|
|
292933
|
+
* path that can end this attempt (review items 3 and 7), and releasing here
|
|
292934
|
+
* too would let the hold go before a failure activity this same interrupt
|
|
292935
|
+
* is still writing has landed.
|
|
292936
|
+
*
|
|
292937
|
+
* Extracted to module scope — like `sendTurnWithRetry` above — so this
|
|
292938
|
+
* interrupt-safety claim can be exercised directly with `Deferred` and
|
|
292939
|
+
* `Fiber.interrupt`, without the full reactor test harness.
|
|
292940
|
+
*/
|
|
292941
|
+
const runTurnStartAckOutcome = (input) => require_Schema$1.gen(function* () {
|
|
292942
|
+
const sendExit = yield* input.send.pipe(require_Schema$1.exit);
|
|
292943
|
+
if (require_Schema$1.isFailure(sendExit)) {
|
|
292944
|
+
yield* input.close;
|
|
292945
|
+
yield* input.onFailure(sendExit.cause);
|
|
292946
|
+
return;
|
|
292947
|
+
}
|
|
292948
|
+
if (sendExit.value._tag === "skipped") {
|
|
292949
|
+
yield* input.close;
|
|
292950
|
+
yield* input.onSkipped;
|
|
292951
|
+
return;
|
|
292952
|
+
}
|
|
292953
|
+
yield* input.onSent(sendExit.value.turnId);
|
|
292954
|
+
}).pipe(require_Schema$1.onInterrupt(() => input.close));
|
|
292704
292955
|
function buildGeneratedWorktreeBranchName(raw) {
|
|
292705
292956
|
const normalized = raw.trim().toLowerCase().replace(/^refs\/heads\//, "").replace(/['"`]/g, "");
|
|
292706
292957
|
const branchFragment = (normalized.startsWith(`forge/`) ? normalized.slice(`${WORKTREE_BRANCH_PREFIX}/`.length) : normalized).replace(/[^a-z0-9/_-]+/g, "-").replace(/\/+/g, "/").replace(/-+/g, "-").replace(/^[./_-]+|[./_-]+$/g, "").slice(0, 64).replace(/[./_-]+$/g, "");
|
|
@@ -292715,12 +292966,48 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292715
292966
|
const serverSettingsService = yield* ServerSettingsService;
|
|
292716
292967
|
const serverConfig = yield* ServerConfig;
|
|
292717
292968
|
const runtimeDrainState = yield* RuntimeDrainState;
|
|
292969
|
+
const spriteTaskHoldReactor = yield* require_Schema$1.serviceOption(SpriteTaskHoldReactor);
|
|
292970
|
+
const holdTurnStart = (key) => require_Schema$1.isSome(spriteTaskHoldReactor) ? spriteTaskHoldReactor.value.holdTurnStart(key) : require_Schema$1.void_;
|
|
292971
|
+
const releaseTurnStart = (key) => require_Schema$1.isSome(spriteTaskHoldReactor) ? spriteTaskHoldReactor.value.releaseTurnStart(key) : require_Schema$1.void_;
|
|
292718
292972
|
const handledTurnStartKeys = yield* require_Cache.make({
|
|
292719
292973
|
capacity: HANDLED_TURN_START_KEY_MAX,
|
|
292720
292974
|
timeToLive: HANDLED_TURN_START_KEY_TTL,
|
|
292721
292975
|
lookup: () => require_Schema$1.succeed(true)
|
|
292722
292976
|
});
|
|
292723
292977
|
const hasHandledTurnStartRecently = (key) => require_Cache.getOption(handledTurnStartKeys, key).pipe(require_Schema$1.flatMap((cached) => require_Cache.set(handledTurnStartKeys, key, true).pipe(require_Schema$1.as(require_Schema$1.isSome(cached)))));
|
|
292978
|
+
/**
|
|
292979
|
+
* Per-attempt buffers of `providerService.streamEvents`, keyed by the same
|
|
292980
|
+
* turn-start attempt key `holdTurnStart` uses — not by thread id: two
|
|
292981
|
+
* overlapping turn-start attempts on one thread each need their own buffer,
|
|
292982
|
+
* fed from before each one's own ack (see the doc comment on `holdTurnStart`
|
|
292983
|
+
* calls below, and forge-runtime-supervision's interleaving review).
|
|
292984
|
+
* `startTurnStartEventRouter` is the one place that ever reads
|
|
292985
|
+
* `providerService.streamEvents` for this purpose, subscribed once for the
|
|
292986
|
+
* reactor's whole lifetime, exactly like `SpriteTaskHoldReactor.start`, so no
|
|
292987
|
+
* attempt ever races to open its own subscription in time for its ack
|
|
292988
|
+
* (CMMD-Center/forge#3558 review item 1).
|
|
292989
|
+
*/
|
|
292990
|
+
const turnStartEventBuffers = /* @__PURE__ */ new Map();
|
|
292991
|
+
const openTurnStartEventBuffer = (key, threadId) => require_Schema$1.gen(function* () {
|
|
292992
|
+
const queue = yield* require_Path.unbounded();
|
|
292993
|
+
turnStartEventBuffers.set(key, {
|
|
292994
|
+
threadId,
|
|
292995
|
+
queue
|
|
292996
|
+
});
|
|
292997
|
+
return queue;
|
|
292998
|
+
});
|
|
292999
|
+
const closeTurnStartEventBuffer = (key, queue) => require_Schema$1.sync(() => {
|
|
293000
|
+
if (turnStartEventBuffers.get(key)?.queue === queue) turnStartEventBuffers.delete(key);
|
|
293001
|
+
}).pipe(require_Schema$1.andThen(require_Path.shutdown(queue)));
|
|
293002
|
+
/**
|
|
293003
|
+
* Started once from `start()`. Fans out every provider event to every
|
|
293004
|
+
* currently-open buffer for its thread — plural, not a single lookup,
|
|
293005
|
+
* because two turn-start attempts can be in flight on the same thread at
|
|
293006
|
+
* once (CMMD-Center/forge#3558 review item 1).
|
|
293007
|
+
*/
|
|
293008
|
+
const startTurnStartEventRouter = require_Schema$1.fn("startTurnStartEventRouter")(function* () {
|
|
293009
|
+
yield* require_Schema$1.forkScoped(require_Path.runForEach(providerService.streamEvents, (event) => require_Schema$1.forEach(Array.from(turnStartEventBuffers.values()).filter((entry) => entry.threadId === event.threadId), (entry) => require_Path.offer(entry.queue, event), { discard: true })));
|
|
293010
|
+
});
|
|
292724
293011
|
const threadModelSelections = /* @__PURE__ */ new Map();
|
|
292725
293012
|
/**
|
|
292726
293013
|
* The providerAccountId each LIVE provider session was actually started with.
|
|
@@ -292877,9 +293164,87 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292877
293164
|
yield* bindSessionToThread(startedSession);
|
|
292878
293165
|
return startedSession.threadId;
|
|
292879
293166
|
});
|
|
293167
|
+
/**
|
|
293168
|
+
* Waits for a turn-start ack to either start (or complete) or fail, after
|
|
293169
|
+
* the provider has already accepted it. Codex's own 429 retries surface as
|
|
293170
|
+
* an `error` notification with `willRetry`, mapped to `runtime.warning`
|
|
293171
|
+
* while retries continue and to `runtime.error` only once they stop
|
|
293172
|
+
* (`mapCodexRuntimeErrorEvent`); `runtime.warning` never replicates to CMMD,
|
|
293173
|
+
* so without this a turn that fails this way never told CMMD anything
|
|
293174
|
+
* happened at all (CMMD-Center/forge#3558).
|
|
293175
|
+
*
|
|
293176
|
+
* Reads from `events`, a per-attempt buffer opened by
|
|
293177
|
+
* `openTurnStartEventBuffer` BEFORE the ack was sent, rather than
|
|
293178
|
+
* subscribing to `providerService.streamEvents` directly here: a fresh
|
|
293179
|
+
* subscription opened only now would miss a `turn.started` published in the
|
|
293180
|
+
* gap between the ack and this call (review item 1).
|
|
293181
|
+
*/
|
|
293182
|
+
const watchTurnStartOutcome = require_Schema$1.fn("watchTurnStartOutcome")(function* (input) {
|
|
293183
|
+
const lastRetrySignal = yield* require_HttpServer.make$3(null);
|
|
293184
|
+
const terminalEvent = yield* require_Path.fromQueue(input.events).pipe(require_Path.mapEffect((event) => require_Schema$1.gen(function* () {
|
|
293185
|
+
if (event.type === "runtime.warning") {
|
|
293186
|
+
const classified = classifyRetryableProviderFailure(event.payload.message);
|
|
293187
|
+
if (classified) yield* require_HttpServer.set(lastRetrySignal, classified);
|
|
293188
|
+
}
|
|
293189
|
+
return event;
|
|
293190
|
+
})), require_Path.filter((event) => isTurnStartWatchTerminalEvent(event, input.turnId)), require_Path.runHead).pipe(require_Schema$1.timeout(PROVIDER_TURN_START_WATCH_BUDGET), require_Schema$1.catchTag("TimeoutError", () => require_Schema$1.succeed(require_Schema$1.none())));
|
|
293191
|
+
if (require_Schema$1.isNone(terminalEvent)) return {
|
|
293192
|
+
_tag: "TimedOut",
|
|
293193
|
+
lastRetrySignal: yield* require_HttpServer.get$1(lastRetrySignal)
|
|
293194
|
+
};
|
|
293195
|
+
const event = terminalEvent.value;
|
|
293196
|
+
if (event.type === "turn.started") return { _tag: "Started" };
|
|
293197
|
+
if (event.type === "turn.completed") return {
|
|
293198
|
+
_tag: "Failed",
|
|
293199
|
+
detail: event.payload.errorMessage ?? "The provider ended the turn with no error detail."
|
|
293200
|
+
};
|
|
293201
|
+
return {
|
|
293202
|
+
_tag: "Failed",
|
|
293203
|
+
detail: event.payload.message
|
|
293204
|
+
};
|
|
293205
|
+
});
|
|
293206
|
+
/**
|
|
293207
|
+
* Finalizes a turn-start watch: releases the turn-start hold and closes its
|
|
293208
|
+
* event buffer, only AFTER the outcome is durably handled — either the turn
|
|
293209
|
+
* started, or the `provider.turn.start.failed` activity has actually been
|
|
293210
|
+
* persisted. No blanket `Effect.ensuring` for the release: if
|
|
293211
|
+
* `appendProviderFailureActivity` itself fails, the hold stays held rather
|
|
293212
|
+
* than being released with no durable record of why the turn failed
|
|
293213
|
+
* (review item 2). `Effect.onInterrupt` covers the one case that ordering
|
|
293214
|
+
* cannot: a shutdown while this watch is still waiting. A write mid-shutdown
|
|
293215
|
+
* is not reliable, so that path only releases the hold; the turn is left
|
|
293216
|
+
* for CMMD's own sweep rather than ended with an event here (review item 7).
|
|
293217
|
+
* The event buffer itself always closes, via `Effect.ensuring`: it is a
|
|
293218
|
+
* local resource with nothing durable riding on its cleanup order.
|
|
293219
|
+
*/
|
|
293220
|
+
const watchTurnStartAndFinalize = (input) => require_Schema$1.gen(function* () {
|
|
293221
|
+
const outcome = yield* watchTurnStartOutcome({
|
|
293222
|
+
threadId: input.threadId,
|
|
293223
|
+
turnId: input.turnId,
|
|
293224
|
+
events: input.events
|
|
293225
|
+
});
|
|
293226
|
+
if (outcome._tag === "Started") {
|
|
293227
|
+
yield* releaseTurnStart(input.holdKey);
|
|
293228
|
+
return;
|
|
293229
|
+
}
|
|
293230
|
+
const detail = outcome._tag === "Failed" ? outcome.detail : turnStartWatchTimedOutDetail(outcome.lastRetrySignal);
|
|
293231
|
+
yield* appendProviderFailureActivity({
|
|
293232
|
+
threadId: input.threadId,
|
|
293233
|
+
kind: "provider.turn.start.failed",
|
|
293234
|
+
summary: "Provider turn start failed",
|
|
293235
|
+
detail: publicProviderFailureDetail(detail),
|
|
293236
|
+
turnId: input.turnId,
|
|
293237
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
293238
|
+
});
|
|
293239
|
+
yield* releaseTurnStart(input.holdKey);
|
|
293240
|
+
}).pipe(require_Schema$1.catchCause((cause) => require_Schema$1.hasInterruptsOnly(cause) ? require_Schema$1.void_ : require_Schema$1.logWarning("provider command reactor turn-start watch failed", {
|
|
293241
|
+
threadId: input.threadId,
|
|
293242
|
+
turnId: input.turnId,
|
|
293243
|
+
cause: require_Schema$1.pretty(cause)
|
|
293244
|
+
})), require_Schema$1.onInterrupt(() => releaseTurnStart(input.holdKey)), require_Schema$1.ensuring(closeTurnStartEventBuffer(input.holdKey, input.events)));
|
|
292880
293245
|
const sendTurnForThread = require_Schema$1.fn("sendTurnForThread")(function* (input) {
|
|
292881
293246
|
const thread = yield* resolveThread(input.threadId);
|
|
292882
|
-
if (!thread) return;
|
|
293247
|
+
if (!thread) return { _tag: "skipped" };
|
|
292883
293248
|
if (yield* runtimeDrainState.isDraining) {
|
|
292884
293249
|
yield* appendProviderFailureActivity({
|
|
292885
293250
|
threadId: input.threadId,
|
|
@@ -292889,7 +293254,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292889
293254
|
turnId: null,
|
|
292890
293255
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292891
293256
|
});
|
|
292892
|
-
return;
|
|
293257
|
+
return { _tag: "skipped" };
|
|
292893
293258
|
}
|
|
292894
293259
|
yield* ensureSessionForThread(input.threadId, input.createdAt, input.modelSelection !== void 0 ? { modelSelection: input.modelSelection } : {});
|
|
292895
293260
|
if (input.modelSelection !== void 0) threadModelSelections.set(input.threadId, input.modelSelection);
|
|
@@ -292913,13 +293278,19 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292913
293278
|
...requestedModelSelection,
|
|
292914
293279
|
model: activeSession.model
|
|
292915
293280
|
} : requestedModelSelection : input.modelSelection;
|
|
292916
|
-
|
|
292917
|
-
|
|
292918
|
-
|
|
292919
|
-
|
|
292920
|
-
|
|
292921
|
-
|
|
292922
|
-
|
|
293281
|
+
return {
|
|
293282
|
+
_tag: "sent",
|
|
293283
|
+
turnId: (yield* sendTurnWithRetry({
|
|
293284
|
+
sendTurn: providerService.sendTurn,
|
|
293285
|
+
turnInput: {
|
|
293286
|
+
threadId: input.threadId,
|
|
293287
|
+
...normalizedInput ? { input: normalizedInput } : {},
|
|
293288
|
+
...normalizedAttachments.length > 0 ? { attachments: normalizedAttachments } : {},
|
|
293289
|
+
...modelForTurn !== void 0 ? { modelSelection: modelForTurn } : {},
|
|
293290
|
+
...input.interactionMode !== void 0 ? { interactionMode: input.interactionMode } : {}
|
|
293291
|
+
}
|
|
293292
|
+
})).turnId
|
|
293293
|
+
};
|
|
292923
293294
|
});
|
|
292924
293295
|
const maybeGenerateAndRenameWorktreeBranchForFirstTurn = require_Schema$1.fn("maybeGenerateAndRenameWorktreeBranchForFirstTurn")(function* (input) {
|
|
292925
293296
|
if (!input.branch || !input.worktreePath) return;
|
|
@@ -293099,7 +293470,8 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
293099
293470
|
});
|
|
293100
293471
|
});
|
|
293101
293472
|
const processTurnStartRequested = require_Schema$1.fn("processTurnStartRequested")(function* (event) {
|
|
293102
|
-
|
|
293473
|
+
const key = turnStartKeyForEvent(event);
|
|
293474
|
+
if (yield* hasHandledTurnStartRecently(key)) return;
|
|
293103
293475
|
const thread = yield* resolveThread(event.payload.threadId);
|
|
293104
293476
|
if (!thread) return;
|
|
293105
293477
|
const message = thread.messages.find((entry) => entry.id === event.payload.messageId);
|
|
@@ -293150,21 +293522,35 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
293150
293522
|
...generationInput
|
|
293151
293523
|
}).pipe(require_Schema$1.forkScoped);
|
|
293152
293524
|
}
|
|
293153
|
-
|
|
293154
|
-
|
|
293155
|
-
|
|
293156
|
-
|
|
293157
|
-
|
|
293158
|
-
|
|
293159
|
-
|
|
293160
|
-
|
|
293161
|
-
|
|
293162
|
-
|
|
293163
|
-
|
|
293164
|
-
|
|
293165
|
-
|
|
293166
|
-
|
|
293167
|
-
|
|
293525
|
+
const turnStartHoldKey = key;
|
|
293526
|
+
yield* require_Schema$1.uninterruptible(holdTurnStart(turnStartHoldKey));
|
|
293527
|
+
const turnStartEvents = yield* openTurnStartEventBuffer(turnStartHoldKey, event.payload.threadId);
|
|
293528
|
+
yield* runTurnStartAckOutcome({
|
|
293529
|
+
send: sendTurnForThread({
|
|
293530
|
+
threadId: event.payload.threadId,
|
|
293531
|
+
messageText: message.text,
|
|
293532
|
+
...message.attachments !== void 0 ? { attachments: message.attachments } : {},
|
|
293533
|
+
...event.payload.modelSelection !== void 0 ? { modelSelection: event.payload.modelSelection } : {},
|
|
293534
|
+
interactionMode: event.payload.interactionMode,
|
|
293535
|
+
createdAt: event.payload.createdAt
|
|
293536
|
+
}),
|
|
293537
|
+
close: closeTurnStartEventBuffer(turnStartHoldKey, turnStartEvents),
|
|
293538
|
+
onFailure: (cause) => appendProviderFailureActivity({
|
|
293539
|
+
threadId: event.payload.threadId,
|
|
293540
|
+
kind: "provider.turn.start.failed",
|
|
293541
|
+
summary: "Provider turn start failed",
|
|
293542
|
+
detail: publicProviderFailureDetailFromCause(cause),
|
|
293543
|
+
turnId: null,
|
|
293544
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
293545
|
+
}).pipe(require_Schema$1.andThen(releaseTurnStart(turnStartHoldKey))),
|
|
293546
|
+
onSkipped: releaseTurnStart(turnStartHoldKey),
|
|
293547
|
+
onSent: (turnId) => watchTurnStartAndFinalize({
|
|
293548
|
+
threadId: event.payload.threadId,
|
|
293549
|
+
turnId,
|
|
293550
|
+
holdKey: turnStartHoldKey,
|
|
293551
|
+
events: turnStartEvents
|
|
293552
|
+
}).pipe(require_Schema$1.forkScoped)
|
|
293553
|
+
});
|
|
293168
293554
|
});
|
|
293169
293555
|
const processTurnInterruptRequested = require_Schema$1.fn("processTurnInterruptRequested")(function* (event) {
|
|
293170
293556
|
const thread = yield* resolveThread(event.payload.threadId);
|
|
@@ -293386,6 +293772,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
293386
293772
|
});
|
|
293387
293773
|
const recoverTimedOutProviderTurnStart = (event) => recoverProviderTurnStartTimeout({
|
|
293388
293774
|
cleanup: stopTimedOutProviderSession(event),
|
|
293775
|
+
releaseHold: releaseTurnStart(turnStartKeyForEvent(event)),
|
|
293389
293776
|
markRecoverable: appendRecoverableTurnStartTimeout(event),
|
|
293390
293777
|
markCleanupFailed: (cause) => appendProviderFailureActivity({
|
|
293391
293778
|
threadId: event.payload.threadId,
|
|
@@ -293398,7 +293785,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
293398
293785
|
});
|
|
293399
293786
|
const processDomainEventSafely = (event) => {
|
|
293400
293787
|
const operation = processDomainEvent(event);
|
|
293401
|
-
return (event.type === "thread.turn-start-requested" ? withProviderTurnStartTimeout(operation, recoverTimedOutProviderTurnStart(event)) : operation).pipe(require_Schema$1.catchCause((cause) => {
|
|
293788
|
+
return (event.type === "thread.turn-start-requested" ? withProviderTurnStartTimeout(operation, recoverTimedOutProviderTurnStart(event)).pipe(require_Schema$1.onInterrupt(() => releaseTurnStart(turnStartKeyForEvent(event)))) : operation).pipe(require_Schema$1.catchCause((cause) => {
|
|
293402
293789
|
if (require_Schema$1.hasInterruptsOnly(cause)) return require_Schema$1.failCause(cause);
|
|
293403
293790
|
return require_Schema$1.logWarning("provider command reactor failed to process event", {
|
|
293404
293791
|
eventType: event.type,
|
|
@@ -293441,6 +293828,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
293441
293828
|
if (event.type === "thread.meta-updated" && event.payload.regenerateTitle === true) return yield* titleRegenerationWorker.enqueue(event);
|
|
293442
293829
|
});
|
|
293443
293830
|
yield* clearInterruptedThreadTitleRegenerations().pipe(require_Schema$1.catchCause((cause) => require_Schema$1.logWarning("provider command reactor failed to sweep interrupted title regenerations", { cause: require_Schema$1.pretty(cause) })));
|
|
293831
|
+
yield* startTurnStartEventRouter();
|
|
293444
293832
|
yield* require_Schema$1.forkScoped(require_Path.runForEach(orchestrationEngine.streamDomainEvents, processEvent));
|
|
293445
293833
|
}),
|
|
293446
293834
|
drain: require_Schema$1.all([worker.drain, titleRegenerationWorker.drain], {
|
|
@@ -293457,7 +293845,7 @@ function recoverProviderTurnStartTimeout(input) {
|
|
|
293457
293845
|
return input.cleanup.pipe(require_Schema$1.matchEffect({
|
|
293458
293846
|
onFailure: input.markCleanupFailed,
|
|
293459
293847
|
onSuccess: () => input.markRecoverable
|
|
293460
|
-
}));
|
|
293848
|
+
}), require_Schema$1.ensuring(input.releaseHold));
|
|
293461
293849
|
}
|
|
293462
293850
|
(() => {
|
|
293463
293851
|
try {
|
|
@@ -294703,6 +295091,16 @@ const CheckpointReactorLive = require_Schema$1.effect(CheckpointReactor, make$7)
|
|
|
294703
295091
|
const LAUNCH_HOLD_MAX_REFRESHES = 10;
|
|
294704
295092
|
/** The hold id of a pending turn start. The thread names it until turn.started arrives. */
|
|
294705
295093
|
const launchHoldId = (threadId) => `launch-${threadId}`;
|
|
295094
|
+
/**
|
|
295095
|
+
* Refresh budget for a turn-start hold (`holdTurnStart`): about ten minutes, matching the launch
|
|
295096
|
+
* hold's own budget. `ProviderCommandReactor` bounds its own retry-and-watch window well inside
|
|
295097
|
+
* this; the budget here is the backstop if that watch is itself interrupted (a server restart, a
|
|
295098
|
+
* bug) without releasing the hold.
|
|
295099
|
+
*/
|
|
295100
|
+
const TURN_START_HOLD_MAX_REFRESHES = 10;
|
|
295101
|
+
/** The hold id for `holdTurnStart` / `releaseTurnStart`. Distinct from `launchHoldId` on purpose:
|
|
295102
|
+
* see the shape's doc comment on `holdTurnStart`. */
|
|
295103
|
+
const turnStartHoldId = (key) => `turn-start-${key}`;
|
|
294706
295104
|
const make$6 = require_Schema$1.gen(function* () {
|
|
294707
295105
|
const providerService = yield* ProviderService;
|
|
294708
295106
|
const manager = makeSpriteTaskHoldManager(process.env, (message, cause) => {
|
|
@@ -294765,10 +295163,21 @@ const make$6 = require_Schema$1.gen(function* () {
|
|
|
294765
295163
|
launchCallers.delete(threadId);
|
|
294766
295164
|
await manager.release(launchHoldId(threadId));
|
|
294767
295165
|
});
|
|
295166
|
+
/** Takes a turn-start hold. Registered with no thread id: see the shape's doc comment. */
|
|
295167
|
+
const holdTurnStart = (key) => require_Schema$1.promise(async () => {
|
|
295168
|
+
if (manager === null) return;
|
|
295169
|
+
await manager.acquire(turnStartHoldId(key), null, { maxRefreshes: TURN_START_HOLD_MAX_REFRESHES });
|
|
295170
|
+
});
|
|
295171
|
+
const releaseTurnStart = (key) => require_Schema$1.promise(async () => {
|
|
295172
|
+
if (manager === null) return;
|
|
295173
|
+
await manager.release(turnStartHoldId(key));
|
|
295174
|
+
});
|
|
294768
295175
|
return {
|
|
294769
295176
|
start,
|
|
294770
295177
|
holdLaunch,
|
|
294771
295178
|
releaseLaunch,
|
|
295179
|
+
holdTurnStart,
|
|
295180
|
+
releaseTurnStart,
|
|
294772
295181
|
releaseAll: onManager((taskHolds) => taskHolds.releaseAll())
|
|
294773
295182
|
};
|
|
294774
295183
|
});
|
|
@@ -298914,7 +299323,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
298914
299323
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
298915
299324
|
*/
|
|
298916
299325
|
function readBakedBuildCommit() {
|
|
298917
|
-
return "
|
|
299326
|
+
return "61b334c338ab47d92fe5f27155bf40ab9c7e3fed";
|
|
298918
299327
|
}
|
|
298919
299328
|
async function resolveServerBuildCommit(input) {
|
|
298920
299329
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -322653,13 +323062,13 @@ const gitUrlCloneRouteLayer = add$1("POST", "/api/cloud/git/clone-url", require_
|
|
|
322653
323062
|
//#endregion
|
|
322654
323063
|
//#region src/server.ts
|
|
322655
323064
|
const PtyAdapterLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
322656
|
-
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunPTY-
|
|
322657
|
-
else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-
|
|
323065
|
+
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunPTY-DkR9y_Jg.cjs")))).layer;
|
|
323066
|
+
else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-DkaJNgpi.cjs")))).layer;
|
|
322658
323067
|
}));
|
|
322659
323068
|
const HttpServerLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
322660
323069
|
const config = yield* ServerConfig;
|
|
322661
|
-
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunHttpServer-
|
|
322662
|
-
const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-
|
|
323070
|
+
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunHttpServer-DTG5XWSi.cjs")))).layer(bunHttpServerOptions(config));
|
|
323071
|
+
const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-RV5QsVeb.cjs"))), require_Schema$1.promise(() => import("node:http"))]);
|
|
322663
323072
|
return NodeHttpServer.layer(NodeHttp.createServer, {
|
|
322664
323073
|
host: config.host,
|
|
322665
323074
|
port: config.port
|
|
@@ -322667,10 +323076,10 @@ const HttpServerLive = require_Schema$1.unwrap(require_Schema$1.gen(function* ()
|
|
|
322667
323076
|
}));
|
|
322668
323077
|
const PlatformServicesLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
322669
323078
|
if (typeof Bun !== "undefined") {
|
|
322670
|
-
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunServices-
|
|
323079
|
+
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunServices-blx5Lx2N.cjs")));
|
|
322671
323080
|
return layer;
|
|
322672
323081
|
} else {
|
|
322673
|
-
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeServices-
|
|
323082
|
+
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeServices-CocSeCvl.cjs")));
|
|
322674
323083
|
return layer;
|
|
322675
323084
|
}
|
|
322676
323085
|
}));
|