@cmmd-center/forge 0.13.63 → 0.13.65
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/bin.cjs +384 -210
- package/dist/bin.mjs +384 -210
- package/dist/client/assets/{DiffPanel-Mj56e5Vd.js → DiffPanel-DUE5CUPF.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-Bws9oedp.js → DiffPanel.logic-DkmY3FZZ.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-CfYAh40Z.js → DiffPanelShell-DH6nYRyX.js} +3 -3
- package/dist/client/assets/{DiffWorkerPoolProvider-CKLNoRVs.js → DiffWorkerPoolProvider-DTK8o_hI.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-7AnSD7oo.js → PullRequestCodePanel-BIjtTKuP.js} +2 -2
- package/dist/client/assets/{Virtualizer-nWFuwagg.js → Virtualizer-CPNxYGbV.js} +2 -2
- package/dist/client/assets/{index-DW9f_7Ly.js → index-Df09-gdQ.js} +6 -6
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -66257,7 +66257,7 @@ function normalizeNumberish(value) {
|
|
|
66257
66257
|
}
|
|
66258
66258
|
//#endregion
|
|
66259
66259
|
//#region package.json
|
|
66260
|
-
var version$1 = "0.13.
|
|
66260
|
+
var version$1 = "0.13.65";
|
|
66261
66261
|
//#endregion
|
|
66262
66262
|
//#region src/sentry.ts
|
|
66263
66263
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -89347,6 +89347,62 @@ var GitHubBroker = class {
|
|
|
89347
89347
|
runtimeResource: "status"
|
|
89348
89348
|
});
|
|
89349
89349
|
}
|
|
89350
|
+
/**
|
|
89351
|
+
* On a "github" scope cache miss, re-requests it once through the existing
|
|
89352
|
+
* CMMD client before the caller refuses (CMMD-Center/forge#3496,
|
|
89353
|
+
* CMMD-Center/forge#3498). CMMD delivers the grant proactively on
|
|
89354
|
+
* thread.create and the kick, so a miss on a woken runtime is a cache or
|
|
89355
|
+
* ticket TTL problem, not a missing request: this closes the gap where the
|
|
89356
|
+
* ticket lands in the broker authority cache mid-request, and logs the
|
|
89357
|
+
* outcome on failure so the cause is visible instead of a silent refusal.
|
|
89358
|
+
*
|
|
89359
|
+
* A CMMD cloud runtime (every sprite) must still make this round trip
|
|
89360
|
+
* itself: `status()`'s own grant gate fails closed before ever reaching
|
|
89361
|
+
* the network for that runtime class (ADR-015 — it must never fall
|
|
89362
|
+
* through to the human JWT), so this uses `probeRuntimeGithubGrantDelivery`
|
|
89363
|
+
* instead, which makes the identical round trip without that JWT
|
|
89364
|
+
* fallback.
|
|
89365
|
+
*/
|
|
89366
|
+
async requestRuntimeGithubGrant() {
|
|
89367
|
+
const cached = this.runtimeGrantAuthContext("github");
|
|
89368
|
+
if (cached) return cached;
|
|
89369
|
+
const runtimeId = process.env.FORGE_CMMD_RUNTIME_ID?.trim();
|
|
89370
|
+
if (!runtimeId) return null;
|
|
89371
|
+
if (this.isCmmdCloudRuntimeMode) await this.probeRuntimeGithubGrantDelivery(runtimeId);
|
|
89372
|
+
else await this.status({
|
|
89373
|
+
sessionId: AuthSessionId.make(`cmmd-runtime:${runtimeId}`),
|
|
89374
|
+
subject: `runtime:${runtimeId}`
|
|
89375
|
+
});
|
|
89376
|
+
const grant = this.runtimeGrantAuthContext("github");
|
|
89377
|
+
if (!grant) console.warn(`[github-broker] re-requested the github scope grant for runtime ${runtimeId}: still missing after one network round trip`);
|
|
89378
|
+
return grant;
|
|
89379
|
+
}
|
|
89380
|
+
/**
|
|
89381
|
+
* The cloud-runtime half of `requestRuntimeGithubGrant`'s re-request.
|
|
89382
|
+
*
|
|
89383
|
+
* CMMD's status route always requires a Bearer that already carries the
|
|
89384
|
+
* "github" scope (verified server-side against the presented ticket's own
|
|
89385
|
+
* claims — see `forge-runtime-grants.routes.ts` on CMMD main), and there is
|
|
89386
|
+
* no separate CMMD route that mints a "github"-scoped grant on demand. So
|
|
89387
|
+
* this call's own response can never carry a grant to install; its purpose
|
|
89388
|
+
* is the wait. CMMD delivers a fresh grant on this same runtime's inbound
|
|
89389
|
+
* thread.create/kick requests (`ServerAuth.authenticateHttpRequest` /
|
|
89390
|
+
* `authenticateWebSocketUpgrade`, which install it into the broker
|
|
89391
|
+
* authority cache as a side effect of authenticating each request), and a
|
|
89392
|
+
* delivery already in flight when the caller checked the cache gets this
|
|
89393
|
+
* round trip's worth of extra time to land before the caller gives up.
|
|
89394
|
+
* Bounded by the same timeout as every other runtime-grant call, and
|
|
89395
|
+
* deliberately unauthenticated: sending no Bearer is exactly as
|
|
89396
|
+
* informative to CMMD as sending one lacking the required scope, and
|
|
89397
|
+
* skips resolving one for a request whose own response is never used.
|
|
89398
|
+
*/
|
|
89399
|
+
async probeRuntimeGithubGrantDelivery(runtimeId) {
|
|
89400
|
+
const response = await this.fetchImpl(makeUrl(this.commandAppUrl, `/api/forge/runtimes/${encodeURIComponent(runtimeId)}/github/status`), {
|
|
89401
|
+
method: "GET",
|
|
89402
|
+
signal: AbortSignal.timeout(GITHUB_BROKER_REQUEST_TIMEOUT_MS)
|
|
89403
|
+
}).catch(() => null);
|
|
89404
|
+
if (response !== null) await parseResponseBody(response);
|
|
89405
|
+
}
|
|
89350
89406
|
async workspaceStatus(input) {
|
|
89351
89407
|
const cwdValidation = validateCwd(input.cwd);
|
|
89352
89408
|
if (cwdValidation) return cwdValidation;
|
|
@@ -97853,6 +97909,235 @@ function serializeProviderCatalog(providers) {
|
|
|
97853
97909
|
return catalog;
|
|
97854
97910
|
}
|
|
97855
97911
|
//#endregion
|
|
97912
|
+
//#region src/provider/personalClaudeInVm.ts
|
|
97913
|
+
/**
|
|
97914
|
+
* CMMD writes this marker when it activates a personal Claude subscription
|
|
97915
|
+
* inside a running sprite. Activation always happens after boot, so a
|
|
97916
|
+
* boot-time env var alone cannot reflect it (FOR-36249).
|
|
97917
|
+
*
|
|
97918
|
+
* CMMD names the file in the Service env (FORGE_PERSONAL_CLAUDE_MARKER_PATH).
|
|
97919
|
+
* Without that, the marker sits directly under FORGE_HOME, which is the same
|
|
97920
|
+
* guest home CMMD derives every sprite path from. A server with neither has no
|
|
97921
|
+
* sprite home to look in, and a desktop never has a marker, so it reads none.
|
|
97922
|
+
*/
|
|
97923
|
+
const PERSONAL_CLAUDE_IN_VM_MARKER_NAME = ".forge-personal-claude-in-vm";
|
|
97924
|
+
const PERSONAL_CLAUDE_GENERATION_FILE_NAME = ".forge-personal-claude-generation";
|
|
97925
|
+
function markerPath(env) {
|
|
97926
|
+
if (env.FORGE_PERSONAL_CLAUDE_MARKER_PATH) return env.FORGE_PERSONAL_CLAUDE_MARKER_PATH;
|
|
97927
|
+
if (env.FORGE_HOME) return (0, node_path.join)(env.FORGE_HOME, PERSONAL_CLAUDE_IN_VM_MARKER_NAME);
|
|
97928
|
+
return null;
|
|
97929
|
+
}
|
|
97930
|
+
/**
|
|
97931
|
+
* True when this runtime should offer the personal Claude subscription lane.
|
|
97932
|
+
*
|
|
97933
|
+
* Accepts either signal: the boot-time env var (backward compatible) or the
|
|
97934
|
+
* activation marker on disk. Reading the marker at call time lets the provider
|
|
97935
|
+
* catalog and the per-turn readiness guard self-heal once CMMD delivers,
|
|
97936
|
+
* without a sprite reboot. Callers still verify the credential is non-empty
|
|
97937
|
+
* before treating it as usable; this only decides whether the lane is active.
|
|
97938
|
+
*/
|
|
97939
|
+
function personalClaudeInVmEnabled(env = process.env) {
|
|
97940
|
+
if (env.FORGE_PERSONAL_CLAUDE_IN_VM === "true") return true;
|
|
97941
|
+
const marker = markerPath(env);
|
|
97942
|
+
if (!marker) return false;
|
|
97943
|
+
try {
|
|
97944
|
+
return (0, node_fs.existsSync)(marker);
|
|
97945
|
+
} catch {
|
|
97946
|
+
return false;
|
|
97947
|
+
}
|
|
97948
|
+
}
|
|
97949
|
+
/** Exported at boot. This is the restart proof CMMD waits on, so it must never read the file. */
|
|
97950
|
+
function bootExportedPersonalClaudeGenerationHash(env = process.env) {
|
|
97951
|
+
return env.FORGE_PERSONAL_CLAUDE_GENERATION_HASH?.trim() ?? "";
|
|
97952
|
+
}
|
|
97953
|
+
/**
|
|
97954
|
+
* The sha256 of the Claude credential generation this runtime holds, or "" when
|
|
97955
|
+
* it holds none. The readiness guard compares it with the generation CMMD
|
|
97956
|
+
* applied. The restart proof must not use it; it uses the boot-exported hash.
|
|
97957
|
+
*
|
|
97958
|
+
* The boot preload exports FORGE_PERSONAL_CLAUDE_GENERATION_HASH, but only when
|
|
97959
|
+
* the marker already exists at boot. Activation in place writes the generation
|
|
97960
|
+
* file and touches the marker without setting any env, so an empty variable
|
|
97961
|
+
* falls back to that file, hashed the way the preload hashes it. CMMD puts the
|
|
97962
|
+
* file beside the marker in the guest home and names no path for it. A marker
|
|
97963
|
+
* with no generation file is a legacy runtime, and gets no hash here either.
|
|
97964
|
+
*/
|
|
97965
|
+
function personalClaudeGenerationHash(env = process.env) {
|
|
97966
|
+
const exported = bootExportedPersonalClaudeGenerationHash(env);
|
|
97967
|
+
if (exported) return exported;
|
|
97968
|
+
const marker = markerPath(env);
|
|
97969
|
+
if (!marker) return "";
|
|
97970
|
+
try {
|
|
97971
|
+
if (!(0, node_fs.existsSync)(marker)) return "";
|
|
97972
|
+
const generation = (0, node_fs.readFileSync)((0, node_path.join)((0, node_path.dirname)(marker), PERSONAL_CLAUDE_GENERATION_FILE_NAME), "utf8").trim();
|
|
97973
|
+
return generation ? (0, node_crypto.createHash)("sha256").update(generation).digest("hex") : "";
|
|
97974
|
+
} catch {
|
|
97975
|
+
return "";
|
|
97976
|
+
}
|
|
97977
|
+
}
|
|
97978
|
+
//#endregion
|
|
97979
|
+
//#region src/provider/providerAuthFailureCopy.ts
|
|
97980
|
+
/**
|
|
97981
|
+
* Rewrite provider auth failures into an action the user can actually take.
|
|
97982
|
+
*
|
|
97983
|
+
* The provider SDKs assume they are running under a human at a terminal, so
|
|
97984
|
+
* their auth errors name CLI verbs: Claude's is literally
|
|
97985
|
+
* `Not logged in · Please run /login`. On Forge Cloud that reaches a browser
|
|
97986
|
+
* user who has no terminal, no shell, and no way to run `/login` — the turn
|
|
97987
|
+
* dies in ~3 s naming an action that does not exist for them (observed live
|
|
97988
|
+
* 2026-08-05).
|
|
97989
|
+
*
|
|
97990
|
+
* CMMD already writes the right shape for the Codex egress path — "Connect
|
|
97991
|
+
* ChatGPT in Settings > Providers, then retry" — which names a destination the
|
|
97992
|
+
* user can click. This gives the provider-SDK failures the same treatment.
|
|
97993
|
+
*
|
|
97994
|
+
* Scope is deliberately narrow. Only messages that clearly identify a
|
|
97995
|
+
* *credential* problem are rewritten; everything else passes through verbatim,
|
|
97996
|
+
* because a rewritten unknown error loses detail an operator needs and risks
|
|
97997
|
+
* telling the user to reconnect an account that was never the problem.
|
|
97998
|
+
*/
|
|
97999
|
+
/**
|
|
98000
|
+
* Signatures of "you have no usable credential", matched case-insensitively.
|
|
98001
|
+
*
|
|
98002
|
+
* Kept as explicit patterns rather than a broad /auth/i so that genuine
|
|
98003
|
+
* mid-turn failures mentioning authorization (a tool hitting a 403 on some
|
|
98004
|
+
* unrelated API, say) are not misreported as "your subscription is
|
|
98005
|
+
* disconnected".
|
|
98006
|
+
*/
|
|
98007
|
+
const CREDENTIAL_FAILURE_PATTERNS = [
|
|
98008
|
+
/\bnot logged in\b/i,
|
|
98009
|
+
/\bplease run\s*\/login\b/i,
|
|
98010
|
+
/\brun\s*\/login\b/i,
|
|
98011
|
+
/\blogin required\b/i,
|
|
98012
|
+
/\bnot authenticated\b/i,
|
|
98013
|
+
/\bno credentials? found\b/i,
|
|
98014
|
+
/\binvalid api key\b/i,
|
|
98015
|
+
/\bauthentication_error\b/i
|
|
98016
|
+
];
|
|
98017
|
+
/**
|
|
98018
|
+
* Claude's words for a delivered credential whose refresh chain is dead.
|
|
98019
|
+
*
|
|
98020
|
+
* Deliberately NOT in CREDENTIAL_FAILURE_PATTERNS. On a runtime whose Claude
|
|
98021
|
+
* credential CMMD delivers, this is not "you are signed out" and reconnecting
|
|
98022
|
+
* in Settings is not the fix: CMMD's authority rotates the refresh token on
|
|
98023
|
+
* every refresh, the snapshot in the guest dies at the next rotation, and the
|
|
98024
|
+
* repair is a re-delivery the next send triggers (FOR-36425, CMMD-36441).
|
|
98025
|
+
* Anywhere else the credential is the user's own local login, so the CLI text
|
|
98026
|
+
* still passes through untouched.
|
|
98027
|
+
*/
|
|
98028
|
+
const DELIVERED_CREDENTIAL_EXPIRED_PATTERN = /\boauth session expired\b/i;
|
|
98029
|
+
const DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY = "The delivered Claude credential expired. CMMD refreshes it on the next send, so retry this turn.";
|
|
98030
|
+
/** True when the message is about a missing/unusable credential, not a tool failure. */
|
|
98031
|
+
function isProviderCredentialFailure(message) {
|
|
98032
|
+
return CREDENTIAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message));
|
|
98033
|
+
}
|
|
98034
|
+
/**
|
|
98035
|
+
* Map a ProviderKind to the account name a user would recognise.
|
|
98036
|
+
*
|
|
98037
|
+
* NOTE the literal is `claudeAgent`, not `claude`. This read `"claude"` and so
|
|
98038
|
+
* never matched, silently degrading every Claude auth failure to the generic
|
|
98039
|
+
* fallback — "Forge Cloud has no **this provider** subscription connected."
|
|
98040
|
+
* Same provider-name drift class as the model-mapping fix (#2103/#2105).
|
|
98041
|
+
*
|
|
98042
|
+
* The names come from the shared PROVIDER_ACCOUNT_NAMES map rather than a local
|
|
98043
|
+
* switch: this file's copy was the ONLY correct provider→label mapping in the
|
|
98044
|
+
* codebase while four client call sites reimplemented it as binary ternaries.
|
|
98045
|
+
* One Record over ProviderKind means a new provider is a compile error in one
|
|
98046
|
+
* place instead of five sentences that read wrong in production.
|
|
98047
|
+
*
|
|
98048
|
+
* Providers that do not sell a personal subscription stay generic on purpose —
|
|
98049
|
+
* "Forge Cloud has no Ollama subscription connected" names something that does
|
|
98050
|
+
* not exist.
|
|
98051
|
+
*/
|
|
98052
|
+
function providerLabel(provider) {
|
|
98053
|
+
return supportsPersonalSubscription(provider) ? PROVIDER_ACCOUNT_NAMES[provider] : "this provider";
|
|
98054
|
+
}
|
|
98055
|
+
/**
|
|
98056
|
+
* Return the message to show for a failed turn.
|
|
98057
|
+
*
|
|
98058
|
+
* `surface` decides the destination: a cloud runtime has no terminal, so it is
|
|
98059
|
+
* pointed at Settings; a local/desktop runtime CAN run the CLI login, so its
|
|
98060
|
+
* copy still offers that as the concrete fix rather than sending someone to a
|
|
98061
|
+
* settings page that is not how they authenticate.
|
|
98062
|
+
*/
|
|
98063
|
+
function rewriteProviderAuthFailure(input) {
|
|
98064
|
+
const original = input.message ?? "";
|
|
98065
|
+
if (original.length === 0) return {
|
|
98066
|
+
message: original,
|
|
98067
|
+
rewritten: false
|
|
98068
|
+
};
|
|
98069
|
+
if (input.deliveredClaudeCredential === true && DELIVERED_CREDENTIAL_EXPIRED_PATTERN.test(original)) return {
|
|
98070
|
+
message: DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY,
|
|
98071
|
+
rewritten: true
|
|
98072
|
+
};
|
|
98073
|
+
if (!isProviderCredentialFailure(original)) return {
|
|
98074
|
+
message: original,
|
|
98075
|
+
rewritten: false
|
|
98076
|
+
};
|
|
98077
|
+
const who = providerLabel(input.provider);
|
|
98078
|
+
return {
|
|
98079
|
+
message: input.surface === "cloud" ? `Forge Cloud has no ${who} subscription connected for your account. Open Settings > Providers to connect one, then retry this turn.` : `This runtime is not signed in to ${who}. Connect it from Settings > Providers, then retry this turn.`,
|
|
98080
|
+
rewritten: true
|
|
98081
|
+
};
|
|
98082
|
+
}
|
|
98083
|
+
/**
|
|
98084
|
+
* Convenience for call sites that just want the string to show.
|
|
98085
|
+
*
|
|
98086
|
+
* Exists so adapters do not each re-derive the surface and re-handle the
|
|
98087
|
+
* undefined case inline — that inlining is what pushed
|
|
98088
|
+
* ClaudeAdapter.handleResultMessage over the cyclomatic gate. Returns
|
|
98089
|
+
* `undefined` unchanged so "no error" stays distinguishable from "empty error".
|
|
98090
|
+
*/
|
|
98091
|
+
function providerAuthFailureMessage(message, provider, env = process.env) {
|
|
98092
|
+
if (message === void 0) return void 0;
|
|
98093
|
+
return rewriteProviderAuthFailure({
|
|
98094
|
+
message,
|
|
98095
|
+
provider,
|
|
98096
|
+
surface: env.FORGE_CMMD_RUNTIME_MODE === "true" ? "cloud" : "local",
|
|
98097
|
+
deliveredClaudeCredential: provider === "claudeAgent" && personalClaudeInVmEnabled(env)
|
|
98098
|
+
}).message;
|
|
98099
|
+
}
|
|
98100
|
+
//#endregion
|
|
98101
|
+
//#region src/orchestration/turnFailureReason.ts
|
|
98102
|
+
/**
|
|
98103
|
+
* Machine-readable reasons for a turn that failed before it produced any
|
|
98104
|
+
* assistant output, replicated to CMMD on the `turn_status` event
|
|
98105
|
+
* (CMMD-Center/forge#3496, CMMD-Center/CMMD#5272). Codes match the catalog
|
|
98106
|
+
* CMMD renders actions from (CMMD-Center/CMMD#5289): a code CMMD does not
|
|
98107
|
+
* recognize degrades to its own generic fallback, so a new Forge failure
|
|
98108
|
+
* class is safe to ship before CMMD's catalog knows about it.
|
|
98109
|
+
*/
|
|
98110
|
+
const GITHUB_GRANT_DETAIL_PATTERN = /exact scoped runtime GitHub grant/i;
|
|
98111
|
+
/**
|
|
98112
|
+
* The exact-machine broker's own wording (`providerEgressToken.ts`) for all
|
|
98113
|
+
* three reasons it could not produce a provider token: an incomplete runtime
|
|
98114
|
+
* identity, no owner session bound to the request, or no live broker
|
|
98115
|
+
* authority. Every one of those is a real authentication refusal, not a
|
|
98116
|
+
* workspace or adapter failure, and the prefix is unique to that code path.
|
|
98117
|
+
*/
|
|
98118
|
+
const EXACT_MACHINE_BROKER_AUTHORITY_PATTERN = /provider token issuance requires a live exact-machine broker authority/i;
|
|
98119
|
+
/**
|
|
98120
|
+
* Classifies a `provider.turn.start.failed` activity's detail. This activity
|
|
98121
|
+
* kind also covers non-auth causes (a draining runtime, a missing user
|
|
98122
|
+
* message, a rejected workspace, a start-timeout cleanup failure), so only
|
|
98123
|
+
* text that names a known authentication refusal is reported as one; every
|
|
98124
|
+
* other detail degrades to `unspecified_failure` rather than telling CMMD to
|
|
98125
|
+
* offer a reconnect action credentials cannot fix.
|
|
98126
|
+
*/
|
|
98127
|
+
function classifyProviderTurnStartFailureReason(detail) {
|
|
98128
|
+
const message = detail.trim().length > 0 ? detail : "The provider could not start a session for this run.";
|
|
98129
|
+
return {
|
|
98130
|
+
code: GITHUB_GRANT_DETAIL_PATTERN.test(detail) ? "provider_start_refused_github_grant" : EXACT_MACHINE_BROKER_AUTHORITY_PATTERN.test(detail) || isProviderCredentialFailure(detail) ? "provider_start_refused_auth" : "unspecified_failure",
|
|
98131
|
+
message
|
|
98132
|
+
};
|
|
98133
|
+
}
|
|
98134
|
+
function checkpointTimeoutReason(detail) {
|
|
98135
|
+
return {
|
|
98136
|
+
code: "checkpoint_timeout",
|
|
98137
|
+
message: detail.trim().length > 0 ? detail : "The workspace checkpoint timed out before the turn could start."
|
|
98138
|
+
};
|
|
98139
|
+
}
|
|
98140
|
+
//#endregion
|
|
97856
98141
|
//#region src/runtimeReplication/replicaSerializer.ts
|
|
97857
98142
|
const replicaTurnStatuses = new Set([
|
|
97858
98143
|
"pending",
|
|
@@ -98193,6 +98478,53 @@ function serializeFailureActivity(activity) {
|
|
|
98193
98478
|
}
|
|
98194
98479
|
};
|
|
98195
98480
|
}
|
|
98481
|
+
/** Only the pre-turn baseline capture sets this; a post-turn capture failure
|
|
98482
|
+
* never blocked the turn from starting and must not fail it retroactively. */
|
|
98483
|
+
function blocksTurnStart(activity) {
|
|
98484
|
+
const payload = activity.payload;
|
|
98485
|
+
if (!payload || typeof payload !== "object") return false;
|
|
98486
|
+
return payload.blocksTurnStart === true;
|
|
98487
|
+
}
|
|
98488
|
+
function isCheckpointTimeoutDetail(detail) {
|
|
98489
|
+
return /timed out/i.test(detail);
|
|
98490
|
+
}
|
|
98491
|
+
/**
|
|
98492
|
+
* The structured reason a turn never started, when this activity is one of
|
|
98493
|
+
* the two known causes (CMMD-Center/forge#3496): the provider refused to
|
|
98494
|
+
* start a session, or the pre-turn checkpoint that must precede it timed out.
|
|
98495
|
+
* `null` for every other activity, including a checkpoint failure that did
|
|
98496
|
+
* not block turn start or was not a timeout.
|
|
98497
|
+
*/
|
|
98498
|
+
function turnStartFailureReason(activity) {
|
|
98499
|
+
const detail = failureDetail(activity);
|
|
98500
|
+
if (activity.kind === "provider.turn.start.failed") return classifyProviderTurnStartFailureReason(detail ?? "");
|
|
98501
|
+
if (activity.kind === "checkpoint.capture.failed" && blocksTurnStart(activity) && detail !== null && isCheckpointTimeoutDetail(detail)) return checkpointTimeoutReason(detail);
|
|
98502
|
+
return null;
|
|
98503
|
+
}
|
|
98504
|
+
/**
|
|
98505
|
+
* Replicates a turn-start failure as `turn_status: failed` with a structured
|
|
98506
|
+
* `reason`, alongside (not instead of) the free-text `error_summary` above:
|
|
98507
|
+
* CMMD fails the owning Work Session from this event
|
|
98508
|
+
* (CMMD-Center/CMMD#5272/#5286), which `error_summary` alone cannot drive.
|
|
98509
|
+
*/
|
|
98510
|
+
function serializeTurnStartFailureStatus(activity) {
|
|
98511
|
+
const reason = turnStartFailureReason(activity);
|
|
98512
|
+
if (reason === null) return null;
|
|
98513
|
+
return {
|
|
98514
|
+
sourceSequence: null,
|
|
98515
|
+
event: {
|
|
98516
|
+
eventId: `activity:${activity.id}:turn-status`,
|
|
98517
|
+
occurredAt: canonicalRuntimeEventTimestamp(activity.createdAt),
|
|
98518
|
+
...eventTurn(activity.turnId),
|
|
98519
|
+
kind: "turn_status",
|
|
98520
|
+
status: "failed",
|
|
98521
|
+
reason: {
|
|
98522
|
+
code: reason.code,
|
|
98523
|
+
message: text(reason.message, 2e3) ?? reason.message
|
|
98524
|
+
}
|
|
98525
|
+
}
|
|
98526
|
+
};
|
|
98527
|
+
}
|
|
98196
98528
|
const turnUsageKeys = [
|
|
98197
98529
|
"uncachedInputTokens",
|
|
98198
98530
|
"cachedInputTokens",
|
|
@@ -98541,7 +98873,7 @@ function serializeRuntimeThreadEvents(thread) {
|
|
|
98541
98873
|
});
|
|
98542
98874
|
const recordedTurns = serializeRecordedTurns(thread);
|
|
98543
98875
|
const textGenerations = serializeTextGenerations(thread);
|
|
98544
|
-
|
|
98876
|
+
const priorEvents = [
|
|
98545
98877
|
...messages,
|
|
98546
98878
|
...replicaActivities,
|
|
98547
98879
|
...toolActivities,
|
|
@@ -98550,7 +98882,15 @@ function serializeRuntimeThreadEvents(thread) {
|
|
|
98550
98882
|
...failureActivities,
|
|
98551
98883
|
...recordedTurns,
|
|
98552
98884
|
...textGenerations
|
|
98553
|
-
]
|
|
98885
|
+
];
|
|
98886
|
+
const latestPriorOccurredAt = priorEvents.reduce((latest, entry) => latest === null || entry.event.occurredAt > latest ? entry.event.occurredAt : latest, null);
|
|
98887
|
+
const turnStartFailureStatuses = thread.activities.flatMap((activity) => {
|
|
98888
|
+
const event = serializeTurnStartFailureStatus(activity);
|
|
98889
|
+
if (event === null) return [];
|
|
98890
|
+
if (latestPriorOccurredAt !== null && event.event.occurredAt < latestPriorOccurredAt) return [];
|
|
98891
|
+
return [event];
|
|
98892
|
+
});
|
|
98893
|
+
return [...priorEvents, ...turnStartFailureStatuses].toSorted((left, right) => {
|
|
98554
98894
|
if (left.sourceSequence !== null && right.sourceSequence !== null) return left.sourceSequence - right.sourceSequence;
|
|
98555
98895
|
if (left.sourceSequence !== null) return -1;
|
|
98556
98896
|
if (right.sourceSequence !== null) return 1;
|
|
@@ -152498,7 +152838,7 @@ async function prepareWorkspaceRepositoryDependencies(input) {
|
|
|
152498
152838
|
const dependencies = resolveWorkspaceEnvironmentConfig(input.cwd).repositoryDependencies;
|
|
152499
152839
|
if (dependencies.length === 0) return [];
|
|
152500
152840
|
const broker = input.broker ?? new GitHubBroker();
|
|
152501
|
-
const auth = broker.runtimeGrantAuthContext("github");
|
|
152841
|
+
const auth = broker.runtimeGrantAuthContext("github") ?? await broker.requestRuntimeGithubGrant?.() ?? null;
|
|
152502
152842
|
if (auth === null) throw new Error("Repository dependencies require an exact scoped runtime GitHub grant.");
|
|
152503
152843
|
const readOrigin = input.readOrigin ?? readOriginFromGit;
|
|
152504
152844
|
const resolved = resolveRepositoryDependencies(dependencies, input.cloudProjectsRoot);
|
|
@@ -213506,73 +213846,6 @@ function primaryClaudeCredentialMatchesPrepared(env, preparedDirectory) {
|
|
|
213506
213846
|
}
|
|
213507
213847
|
}
|
|
213508
213848
|
//#endregion
|
|
213509
|
-
//#region src/provider/personalClaudeInVm.ts
|
|
213510
|
-
/**
|
|
213511
|
-
* CMMD writes this marker when it activates a personal Claude subscription
|
|
213512
|
-
* inside a running sprite. Activation always happens after boot, so a
|
|
213513
|
-
* boot-time env var alone cannot reflect it (FOR-36249).
|
|
213514
|
-
*
|
|
213515
|
-
* CMMD names the file in the Service env (FORGE_PERSONAL_CLAUDE_MARKER_PATH).
|
|
213516
|
-
* Without that, the marker sits directly under FORGE_HOME, which is the same
|
|
213517
|
-
* guest home CMMD derives every sprite path from. A server with neither has no
|
|
213518
|
-
* sprite home to look in, and a desktop never has a marker, so it reads none.
|
|
213519
|
-
*/
|
|
213520
|
-
const PERSONAL_CLAUDE_IN_VM_MARKER_NAME = ".forge-personal-claude-in-vm";
|
|
213521
|
-
const PERSONAL_CLAUDE_GENERATION_FILE_NAME = ".forge-personal-claude-generation";
|
|
213522
|
-
function markerPath(env) {
|
|
213523
|
-
if (env.FORGE_PERSONAL_CLAUDE_MARKER_PATH) return env.FORGE_PERSONAL_CLAUDE_MARKER_PATH;
|
|
213524
|
-
if (env.FORGE_HOME) return (0, node_path.join)(env.FORGE_HOME, PERSONAL_CLAUDE_IN_VM_MARKER_NAME);
|
|
213525
|
-
return null;
|
|
213526
|
-
}
|
|
213527
|
-
/**
|
|
213528
|
-
* True when this runtime should offer the personal Claude subscription lane.
|
|
213529
|
-
*
|
|
213530
|
-
* Accepts either signal: the boot-time env var (backward compatible) or the
|
|
213531
|
-
* activation marker on disk. Reading the marker at call time lets the provider
|
|
213532
|
-
* catalog and the per-turn readiness guard self-heal once CMMD delivers,
|
|
213533
|
-
* without a sprite reboot. Callers still verify the credential is non-empty
|
|
213534
|
-
* before treating it as usable; this only decides whether the lane is active.
|
|
213535
|
-
*/
|
|
213536
|
-
function personalClaudeInVmEnabled(env = process.env) {
|
|
213537
|
-
if (env.FORGE_PERSONAL_CLAUDE_IN_VM === "true") return true;
|
|
213538
|
-
const marker = markerPath(env);
|
|
213539
|
-
if (!marker) return false;
|
|
213540
|
-
try {
|
|
213541
|
-
return (0, node_fs.existsSync)(marker);
|
|
213542
|
-
} catch {
|
|
213543
|
-
return false;
|
|
213544
|
-
}
|
|
213545
|
-
}
|
|
213546
|
-
/** Exported at boot. This is the restart proof CMMD waits on, so it must never read the file. */
|
|
213547
|
-
function bootExportedPersonalClaudeGenerationHash(env = process.env) {
|
|
213548
|
-
return env.FORGE_PERSONAL_CLAUDE_GENERATION_HASH?.trim() ?? "";
|
|
213549
|
-
}
|
|
213550
|
-
/**
|
|
213551
|
-
* The sha256 of the Claude credential generation this runtime holds, or "" when
|
|
213552
|
-
* it holds none. The readiness guard compares it with the generation CMMD
|
|
213553
|
-
* applied. The restart proof must not use it; it uses the boot-exported hash.
|
|
213554
|
-
*
|
|
213555
|
-
* The boot preload exports FORGE_PERSONAL_CLAUDE_GENERATION_HASH, but only when
|
|
213556
|
-
* the marker already exists at boot. Activation in place writes the generation
|
|
213557
|
-
* file and touches the marker without setting any env, so an empty variable
|
|
213558
|
-
* falls back to that file, hashed the way the preload hashes it. CMMD puts the
|
|
213559
|
-
* file beside the marker in the guest home and names no path for it. A marker
|
|
213560
|
-
* with no generation file is a legacy runtime, and gets no hash here either.
|
|
213561
|
-
*/
|
|
213562
|
-
function personalClaudeGenerationHash(env = process.env) {
|
|
213563
|
-
const exported = bootExportedPersonalClaudeGenerationHash(env);
|
|
213564
|
-
if (exported) return exported;
|
|
213565
|
-
const marker = markerPath(env);
|
|
213566
|
-
if (!marker) return "";
|
|
213567
|
-
try {
|
|
213568
|
-
if (!(0, node_fs.existsSync)(marker)) return "";
|
|
213569
|
-
const generation = (0, node_fs.readFileSync)((0, node_path.join)((0, node_path.dirname)(marker), PERSONAL_CLAUDE_GENERATION_FILE_NAME), "utf8").trim();
|
|
213570
|
-
return generation ? (0, node_crypto.createHash)("sha256").update(generation).digest("hex") : "";
|
|
213571
|
-
} catch {
|
|
213572
|
-
return "";
|
|
213573
|
-
}
|
|
213574
|
-
}
|
|
213575
|
-
//#endregion
|
|
213576
213849
|
//#region src/provider/claudeCredentialContainment.ts
|
|
213577
213850
|
/**
|
|
213578
213851
|
* Containment for provider credentials inherited by the Claude Agent SDK child.
|
|
@@ -215656,128 +215929,6 @@ function createGitHubMcpServer(runBrokeredTool, threadId, settings) {
|
|
|
215656
215929
|
});
|
|
215657
215930
|
}
|
|
215658
215931
|
//#endregion
|
|
215659
|
-
//#region src/provider/providerAuthFailureCopy.ts
|
|
215660
|
-
/**
|
|
215661
|
-
* Rewrite provider auth failures into an action the user can actually take.
|
|
215662
|
-
*
|
|
215663
|
-
* The provider SDKs assume they are running under a human at a terminal, so
|
|
215664
|
-
* their auth errors name CLI verbs: Claude's is literally
|
|
215665
|
-
* `Not logged in · Please run /login`. On Forge Cloud that reaches a browser
|
|
215666
|
-
* user who has no terminal, no shell, and no way to run `/login` — the turn
|
|
215667
|
-
* dies in ~3 s naming an action that does not exist for them (observed live
|
|
215668
|
-
* 2026-08-05).
|
|
215669
|
-
*
|
|
215670
|
-
* CMMD already writes the right shape for the Codex egress path — "Connect
|
|
215671
|
-
* ChatGPT in Settings > Providers, then retry" — which names a destination the
|
|
215672
|
-
* user can click. This gives the provider-SDK failures the same treatment.
|
|
215673
|
-
*
|
|
215674
|
-
* Scope is deliberately narrow. Only messages that clearly identify a
|
|
215675
|
-
* *credential* problem are rewritten; everything else passes through verbatim,
|
|
215676
|
-
* because a rewritten unknown error loses detail an operator needs and risks
|
|
215677
|
-
* telling the user to reconnect an account that was never the problem.
|
|
215678
|
-
*/
|
|
215679
|
-
/**
|
|
215680
|
-
* Signatures of "you have no usable credential", matched case-insensitively.
|
|
215681
|
-
*
|
|
215682
|
-
* Kept as explicit patterns rather than a broad /auth/i so that genuine
|
|
215683
|
-
* mid-turn failures mentioning authorization (a tool hitting a 403 on some
|
|
215684
|
-
* unrelated API, say) are not misreported as "your subscription is
|
|
215685
|
-
* disconnected".
|
|
215686
|
-
*/
|
|
215687
|
-
const CREDENTIAL_FAILURE_PATTERNS = [
|
|
215688
|
-
/\bnot logged in\b/i,
|
|
215689
|
-
/\bplease run\s*\/login\b/i,
|
|
215690
|
-
/\brun\s*\/login\b/i,
|
|
215691
|
-
/\blogin required\b/i,
|
|
215692
|
-
/\bnot authenticated\b/i,
|
|
215693
|
-
/\bno credentials? found\b/i,
|
|
215694
|
-
/\binvalid api key\b/i,
|
|
215695
|
-
/\bauthentication_error\b/i
|
|
215696
|
-
];
|
|
215697
|
-
/**
|
|
215698
|
-
* Claude's words for a delivered credential whose refresh chain is dead.
|
|
215699
|
-
*
|
|
215700
|
-
* Deliberately NOT in CREDENTIAL_FAILURE_PATTERNS. On a runtime whose Claude
|
|
215701
|
-
* credential CMMD delivers, this is not "you are signed out" and reconnecting
|
|
215702
|
-
* in Settings is not the fix: CMMD's authority rotates the refresh token on
|
|
215703
|
-
* every refresh, the snapshot in the guest dies at the next rotation, and the
|
|
215704
|
-
* repair is a re-delivery the next send triggers (FOR-36425, CMMD-36441).
|
|
215705
|
-
* Anywhere else the credential is the user's own local login, so the CLI text
|
|
215706
|
-
* still passes through untouched.
|
|
215707
|
-
*/
|
|
215708
|
-
const DELIVERED_CREDENTIAL_EXPIRED_PATTERN = /\boauth session expired\b/i;
|
|
215709
|
-
const DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY = "The delivered Claude credential expired. CMMD refreshes it on the next send, so retry this turn.";
|
|
215710
|
-
/** True when the message is about a missing/unusable credential, not a tool failure. */
|
|
215711
|
-
function isProviderCredentialFailure(message) {
|
|
215712
|
-
return CREDENTIAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message));
|
|
215713
|
-
}
|
|
215714
|
-
/**
|
|
215715
|
-
* Map a ProviderKind to the account name a user would recognise.
|
|
215716
|
-
*
|
|
215717
|
-
* NOTE the literal is `claudeAgent`, not `claude`. This read `"claude"` and so
|
|
215718
|
-
* never matched, silently degrading every Claude auth failure to the generic
|
|
215719
|
-
* fallback — "Forge Cloud has no **this provider** subscription connected."
|
|
215720
|
-
* Same provider-name drift class as the model-mapping fix (#2103/#2105).
|
|
215721
|
-
*
|
|
215722
|
-
* The names come from the shared PROVIDER_ACCOUNT_NAMES map rather than a local
|
|
215723
|
-
* switch: this file's copy was the ONLY correct provider→label mapping in the
|
|
215724
|
-
* codebase while four client call sites reimplemented it as binary ternaries.
|
|
215725
|
-
* One Record over ProviderKind means a new provider is a compile error in one
|
|
215726
|
-
* place instead of five sentences that read wrong in production.
|
|
215727
|
-
*
|
|
215728
|
-
* Providers that do not sell a personal subscription stay generic on purpose —
|
|
215729
|
-
* "Forge Cloud has no Ollama subscription connected" names something that does
|
|
215730
|
-
* not exist.
|
|
215731
|
-
*/
|
|
215732
|
-
function providerLabel(provider) {
|
|
215733
|
-
return supportsPersonalSubscription(provider) ? PROVIDER_ACCOUNT_NAMES[provider] : "this provider";
|
|
215734
|
-
}
|
|
215735
|
-
/**
|
|
215736
|
-
* Return the message to show for a failed turn.
|
|
215737
|
-
*
|
|
215738
|
-
* `surface` decides the destination: a cloud runtime has no terminal, so it is
|
|
215739
|
-
* pointed at Settings; a local/desktop runtime CAN run the CLI login, so its
|
|
215740
|
-
* copy still offers that as the concrete fix rather than sending someone to a
|
|
215741
|
-
* settings page that is not how they authenticate.
|
|
215742
|
-
*/
|
|
215743
|
-
function rewriteProviderAuthFailure(input) {
|
|
215744
|
-
const original = input.message ?? "";
|
|
215745
|
-
if (original.length === 0) return {
|
|
215746
|
-
message: original,
|
|
215747
|
-
rewritten: false
|
|
215748
|
-
};
|
|
215749
|
-
if (input.deliveredClaudeCredential === true && DELIVERED_CREDENTIAL_EXPIRED_PATTERN.test(original)) return {
|
|
215750
|
-
message: DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY,
|
|
215751
|
-
rewritten: true
|
|
215752
|
-
};
|
|
215753
|
-
if (!isProviderCredentialFailure(original)) return {
|
|
215754
|
-
message: original,
|
|
215755
|
-
rewritten: false
|
|
215756
|
-
};
|
|
215757
|
-
const who = providerLabel(input.provider);
|
|
215758
|
-
return {
|
|
215759
|
-
message: input.surface === "cloud" ? `Forge Cloud has no ${who} subscription connected for your account. Open Settings > Providers to connect one, then retry this turn.` : `This runtime is not signed in to ${who}. Connect it from Settings > Providers, then retry this turn.`,
|
|
215760
|
-
rewritten: true
|
|
215761
|
-
};
|
|
215762
|
-
}
|
|
215763
|
-
/**
|
|
215764
|
-
* Convenience for call sites that just want the string to show.
|
|
215765
|
-
*
|
|
215766
|
-
* Exists so adapters do not each re-derive the surface and re-handle the
|
|
215767
|
-
* undefined case inline — that inlining is what pushed
|
|
215768
|
-
* ClaudeAdapter.handleResultMessage over the cyclomatic gate. Returns
|
|
215769
|
-
* `undefined` unchanged so "no error" stays distinguishable from "empty error".
|
|
215770
|
-
*/
|
|
215771
|
-
function providerAuthFailureMessage(message, provider, env = process.env) {
|
|
215772
|
-
if (message === void 0) return void 0;
|
|
215773
|
-
return rewriteProviderAuthFailure({
|
|
215774
|
-
message,
|
|
215775
|
-
provider,
|
|
215776
|
-
surface: env.FORGE_CMMD_RUNTIME_MODE === "true" ? "cloud" : "local",
|
|
215777
|
-
deliveredClaudeCredential: provider === "claudeAgent" && personalClaudeInVmEnabled(env)
|
|
215778
|
-
}).message;
|
|
215779
|
-
}
|
|
215780
|
-
//#endregion
|
|
215781
215932
|
//#region src/provider/missingWorkspaceCopy.ts
|
|
215782
215933
|
/**
|
|
215783
215934
|
* Refuse a turn whose checkout folder is gone, and say so.
|
|
@@ -234138,6 +234289,8 @@ const CheckpointDiffQueryLive = require_Schema$1.effect(CheckpointDiffQuery, mak
|
|
|
234138
234289
|
* @module CheckpointStoreLive
|
|
234139
234290
|
*/
|
|
234140
234291
|
const CHECKPOINT_PATH_OUTPUT_MAX_BYTES = 16 * 1024 * 1024;
|
|
234292
|
+
const CheckpointCaptureTimeoutConfig = require_Config.number("FORGE_CHECKPOINT_CAPTURE_TIMEOUT_MS").pipe(require_Config.withDefault(6e4));
|
|
234293
|
+
const EFFECT_TIMER_MAX_MS = 2147483647;
|
|
234141
234294
|
function parseNullSeparatedPaths(output) {
|
|
234142
234295
|
return output.split("\0").filter((entry) => entry.length > 0);
|
|
234143
234296
|
}
|
|
@@ -234145,7 +234298,7 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234145
234298
|
const fs = yield* require_Path.FileSystem;
|
|
234146
234299
|
const path = yield* require_Path.Path;
|
|
234147
234300
|
const git = yield* GitCore;
|
|
234148
|
-
const resolveHeadCommit = (cwd) => git.execute({
|
|
234301
|
+
const resolveHeadCommit = (cwd, timeoutMs) => git.execute({
|
|
234149
234302
|
operation: "CheckpointStore.resolveHeadCommit",
|
|
234150
234303
|
cwd,
|
|
234151
234304
|
args: [
|
|
@@ -234154,7 +234307,8 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234154
234307
|
"--quiet",
|
|
234155
234308
|
"HEAD^{commit}"
|
|
234156
234309
|
],
|
|
234157
|
-
allowNonZeroExit: true
|
|
234310
|
+
allowNonZeroExit: true,
|
|
234311
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
234158
234312
|
}).pipe(require_Schema$1.map((result) => {
|
|
234159
234313
|
if (result.code !== 0) return null;
|
|
234160
234314
|
const commit = result.stdout.trim();
|
|
@@ -234193,7 +234347,9 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234193
234347
|
}).pipe(require_Schema$1.map((result) => result.code === 0 && result.stdout.trim() === "true"), require_Schema$1.catch_(() => require_Schema$1.succeed(false)));
|
|
234194
234348
|
const captureCheckpoint = require_Schema$1.fn("captureCheckpoint")(function* (input) {
|
|
234195
234349
|
const operation = "CheckpointStore.captureCheckpoint";
|
|
234196
|
-
const
|
|
234350
|
+
const captureTimeoutMs = yield* CheckpointCaptureTimeoutConfig.asEffect().pipe(require_Schema$1.orDie);
|
|
234351
|
+
if (!Number.isFinite(captureTimeoutMs) || captureTimeoutMs <= 0 || captureTimeoutMs > EFFECT_TIMER_MAX_MS) return yield* require_Schema$1.die(/* @__PURE__ */ new Error(`FORGE_CHECKPOINT_CAPTURE_TIMEOUT_MS must be a finite number greater than zero and at most ${EFFECT_TIMER_MAX_MS}, got ${captureTimeoutMs}.`));
|
|
234352
|
+
const headCommit = yield* resolveHeadCommit(input.cwd, captureTimeoutMs);
|
|
234197
234353
|
const [trackedPathResult, untrackedPathResult] = yield* require_Schema$1.all([git.execute({
|
|
234198
234354
|
operation,
|
|
234199
234355
|
cwd: input.cwd,
|
|
@@ -234210,7 +234366,8 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234210
234366
|
"-z",
|
|
234211
234367
|
"--"
|
|
234212
234368
|
],
|
|
234213
|
-
maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES
|
|
234369
|
+
maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES,
|
|
234370
|
+
timeoutMs: captureTimeoutMs
|
|
234214
234371
|
}), git.execute({
|
|
234215
234372
|
operation,
|
|
234216
234373
|
cwd: input.cwd,
|
|
@@ -234221,7 +234378,8 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234221
234378
|
"-z",
|
|
234222
234379
|
"--"
|
|
234223
234380
|
],
|
|
234224
|
-
maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES
|
|
234381
|
+
maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES,
|
|
234382
|
+
timeoutMs: captureTimeoutMs
|
|
234225
234383
|
})], { concurrency: 2 });
|
|
234226
234384
|
const changedPaths = [...new Set([...parseNullSeparatedPaths(trackedPathResult.stdout), ...parseNullSeparatedPaths(untrackedPathResult.stdout)])];
|
|
234227
234385
|
if (headCommit && changedPaths.length === 0) {
|
|
@@ -234232,7 +234390,8 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234232
234390
|
"update-ref",
|
|
234233
234391
|
input.checkpointRef,
|
|
234234
234392
|
headCommit
|
|
234235
|
-
]
|
|
234393
|
+
],
|
|
234394
|
+
timeoutMs: captureTimeoutMs
|
|
234236
234395
|
});
|
|
234237
234396
|
return;
|
|
234238
234397
|
}
|
|
@@ -234250,13 +234409,15 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234250
234409
|
operation,
|
|
234251
234410
|
cwd: input.cwd,
|
|
234252
234411
|
args: ["read-tree", headCommit],
|
|
234253
|
-
env: commitEnv
|
|
234412
|
+
env: commitEnv,
|
|
234413
|
+
timeoutMs: captureTimeoutMs
|
|
234254
234414
|
});
|
|
234255
234415
|
else yield* git.execute({
|
|
234256
234416
|
operation,
|
|
234257
234417
|
cwd: input.cwd,
|
|
234258
234418
|
args: ["read-tree", "--empty"],
|
|
234259
|
-
env: commitEnv
|
|
234419
|
+
env: commitEnv,
|
|
234420
|
+
timeoutMs: captureTimeoutMs
|
|
234260
234421
|
});
|
|
234261
234422
|
if (changedPaths.length > 0) yield* git.execute({
|
|
234262
234423
|
operation,
|
|
@@ -234269,13 +234430,15 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234269
234430
|
"--pathspec-file-nul"
|
|
234270
234431
|
],
|
|
234271
234432
|
stdin: changedPaths.map((changedPath) => `${changedPath}\0`).join(""),
|
|
234272
|
-
env: commitEnv
|
|
234433
|
+
env: commitEnv,
|
|
234434
|
+
timeoutMs: captureTimeoutMs
|
|
234273
234435
|
});
|
|
234274
234436
|
const treeOid = (yield* git.execute({
|
|
234275
234437
|
operation,
|
|
234276
234438
|
cwd: input.cwd,
|
|
234277
234439
|
args: ["write-tree"],
|
|
234278
|
-
env: commitEnv
|
|
234440
|
+
env: commitEnv,
|
|
234441
|
+
timeoutMs: captureTimeoutMs
|
|
234279
234442
|
})).stdout.trim();
|
|
234280
234443
|
if (treeOid.length === 0) return yield* new GitCommandError({
|
|
234281
234444
|
operation,
|
|
@@ -234293,7 +234456,8 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234293
234456
|
"-m",
|
|
234294
234457
|
message
|
|
234295
234458
|
],
|
|
234296
|
-
env: commitEnv
|
|
234459
|
+
env: commitEnv,
|
|
234460
|
+
timeoutMs: captureTimeoutMs
|
|
234297
234461
|
})).stdout.trim();
|
|
234298
234462
|
if (commitOid.length === 0) return yield* new GitCommandError({
|
|
234299
234463
|
operation,
|
|
@@ -234308,7 +234472,8 @@ const makeCheckpointStore = require_Schema$1.gen(function* () {
|
|
|
234308
234472
|
"update-ref",
|
|
234309
234473
|
input.checkpointRef,
|
|
234310
234474
|
commitOid
|
|
234311
|
-
]
|
|
234475
|
+
],
|
|
234476
|
+
timeoutMs: captureTimeoutMs
|
|
234312
234477
|
});
|
|
234313
234478
|
}), (tempDir) => fs.remove(tempDir, { recursive: true })).pipe(require_Schema$1.catchTags({ PlatformError: (error) => require_Schema$1.fail(new CheckpointInvariantError({
|
|
234314
234479
|
operation: "CheckpointStore.captureCheckpoint",
|
|
@@ -292201,7 +292366,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292201
292366
|
summary: "Agent turn blocked",
|
|
292202
292367
|
detail: "Forge runtime is updating after the current turn. New agent turns are paused until the runtime finishes draining.",
|
|
292203
292368
|
turnId: null,
|
|
292204
|
-
createdAt:
|
|
292369
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292205
292370
|
});
|
|
292206
292371
|
return;
|
|
292207
292372
|
}
|
|
@@ -292436,7 +292601,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292436
292601
|
summary: "Agent turn blocked",
|
|
292437
292602
|
detail: workspaceError,
|
|
292438
292603
|
turnId: null,
|
|
292439
|
-
createdAt:
|
|
292604
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292440
292605
|
});
|
|
292441
292606
|
return;
|
|
292442
292607
|
}
|
|
@@ -293528,7 +293693,10 @@ const make$7 = require_Schema$1.gen(function* () {
|
|
|
293528
293693
|
tone: "error",
|
|
293529
293694
|
kind: "checkpoint.capture.failed",
|
|
293530
293695
|
summary: "Checkpoint capture failed",
|
|
293531
|
-
payload: {
|
|
293696
|
+
payload: {
|
|
293697
|
+
detail: input.detail,
|
|
293698
|
+
...input.blocksTurnStart ? { blocksTurnStart: true } : {}
|
|
293699
|
+
},
|
|
293532
293700
|
turnId: input.turnId,
|
|
293533
293701
|
createdAt: input.createdAt
|
|
293534
293702
|
},
|
|
@@ -293814,7 +293982,13 @@ const make$7 = require_Schema$1.gen(function* () {
|
|
|
293814
293982
|
yield* checkpointStore.captureCheckpoint({
|
|
293815
293983
|
cwd: checkpointCwd,
|
|
293816
293984
|
checkpointRef: baselineCheckpointRef
|
|
293817
|
-
})
|
|
293985
|
+
}).pipe(require_Schema$1.catch_((error) => appendCaptureFailureActivity({
|
|
293986
|
+
threadId,
|
|
293987
|
+
turnId: null,
|
|
293988
|
+
detail: error.message,
|
|
293989
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
293990
|
+
blocksTurnStart: true
|
|
293991
|
+
}).pipe(require_Schema$1.catch_(() => require_Schema$1.void_))));
|
|
293818
293992
|
yield* receiptBus.publish({
|
|
293819
293993
|
type: "checkpoint.baseline.captured",
|
|
293820
293994
|
threadId,
|
|
@@ -298206,7 +298380,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
298206
298380
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
298207
298381
|
*/
|
|
298208
298382
|
function readBakedBuildCommit() {
|
|
298209
|
-
return "
|
|
298383
|
+
return "06c11aad2e4673f13061c7bed6d0a4cb63ff8122";
|
|
298210
298384
|
}
|
|
298211
298385
|
async function resolveServerBuildCommit(input) {
|
|
298212
298386
|
if (isFullCommitSha(input.baked)) return input.baked;
|