@cmmd-center/forge 0.13.63 → 0.13.64

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.mjs CHANGED
@@ -23,7 +23,7 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicK
23
23
  import * as FS$1 from "node:fs";
24
24
  import fs, { accessSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, mkdtempSync, promises, readFile, readFileSync, readdir, readdirSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
25
25
  import * as NodeOS from "node:os";
26
- import os, { homedir, networkInterfaces, tmpdir } from "node:os";
26
+ import $U, { homedir, networkInterfaces, tmpdir } from "node:os";
27
27
  import * as Path$1 from "node:path";
28
28
  import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
29
29
  import { pathToFileURL } from "node:url";
@@ -50573,7 +50573,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
50573
50573
  const min = forceColor || 0;
50574
50574
  if (env.TERM === "dumb") return min;
50575
50575
  if (process$1.platform === "win32") {
50576
- const osRelease = os.release().split(".");
50576
+ const osRelease = $U.release().split(".");
50577
50577
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
50578
50578
  return 1;
50579
50579
  }
@@ -53016,7 +53016,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53016
53016
  exports.processDetector = void 0;
53017
53017
  const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
53018
53018
  const semconv_1 = require_semconv$1();
53019
- const os$4 = __require("os");
53019
+ const os$3 = __require("os");
53020
53020
  /**
53021
53021
  * ProcessDetector will be used to detect the resources related current process running
53022
53022
  * and being instrumented from the NodeJS Process module.
@@ -53038,7 +53038,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53038
53038
  };
53039
53039
  if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
53040
53040
  try {
53041
- const userInfo = os$4.userInfo();
53041
+ const userInfo = os$3.userInfo();
53042
53042
  attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
53043
53043
  } catch (e) {
53044
53044
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -65961,7 +65961,7 @@ function normalizeNumberish(value) {
65961
65961
  }
65962
65962
  //#endregion
65963
65963
  //#region package.json
65964
- var version$1 = "0.13.63";
65964
+ var version$1 = "0.13.64";
65965
65965
  //#endregion
65966
65966
  //#region src/sentry.ts
65967
65967
  const SERVER_APP_NAME = "forge-server";
@@ -75844,7 +75844,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
75844
75844
  var fs$6 = __require("fs");
75845
75845
  var path$5 = __require("path");
75846
75846
  var url$1 = __require("url");
75847
- var os$3 = __require("os");
75847
+ var os$2 = __require("os");
75848
75848
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
75849
75849
  var vars = process.config && process.config.variables || {};
75850
75850
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
@@ -75852,8 +75852,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
75852
75852
  var abi = versions.modules;
75853
75853
  if (versions.deno || process.isBun) abi = "unsupported";
75854
75854
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
75855
- var arch = process.env.npm_config_arch || os$3.arch();
75856
- var platform = process.env.npm_config_platform || os$3.platform();
75855
+ var arch = process.env.npm_config_arch || os$2.arch();
75856
+ var platform = process.env.npm_config_platform || os$2.platform();
75857
75857
  var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
75858
75858
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
75859
75859
  var uv = (versions.uv || "").split(".")[0];
@@ -88991,6 +88991,28 @@ var GitHubBroker = class {
88991
88991
  runtimeResource: "status"
88992
88992
  });
88993
88993
  }
88994
+ /**
88995
+ * On a "github" scope cache miss, re-requests it once through the existing
88996
+ * CMMD client before the caller refuses (CMMD-Center/forge#3496). CMMD
88997
+ * delivers the grant proactively on thread.create and the kick, so a miss
88998
+ * on a woken runtime is a cache or ticket TTL problem, not a missing
88999
+ * request: this closes the gap where the ticket lands in the broker
89000
+ * authority cache mid-request, and logs the outcome either way so the
89001
+ * cause is visible instead of a silent refusal.
89002
+ */
89003
+ async requestRuntimeGithubGrant() {
89004
+ const cached = this.runtimeGrantAuthContext("github");
89005
+ if (cached) return cached;
89006
+ const runtimeId = process.env.FORGE_CMMD_RUNTIME_ID?.trim();
89007
+ if (!runtimeId) return null;
89008
+ const result = await this.status({
89009
+ sessionId: AuthSessionId.make(`cmmd-runtime:${runtimeId}`),
89010
+ subject: `runtime:${runtimeId}`
89011
+ });
89012
+ const grant = this.runtimeGrantAuthContext("github");
89013
+ console.warn(`[github-broker] re-requested the github scope grant for runtime ${runtimeId}: ${grant ? "cache populated" : `still missing (broker answered ${result.ok ? "ok" : result.code ?? "no grant"})`}`);
89014
+ return grant;
89015
+ }
88994
89016
  async workspaceStatus(input) {
88995
89017
  const cwdValidation = validateCwd(input.cwd);
88996
89018
  if (cwdValidation) return cwdValidation;
@@ -93606,7 +93628,7 @@ const launchDetached = (launch) => gen(function* () {
93606
93628
  });
93607
93629
  const OpenLive = effect(Open, gen(function* () {
93608
93630
  const open = yield* tryPromise({
93609
- try: () => import("./open-yHTUkd2Y.mjs"),
93631
+ try: () => import("./open-2-reBrpv.mjs"),
93610
93632
  catch: (cause) => new OpenError$1({
93611
93633
  message: "failed to load browser opener",
93612
93634
  cause
@@ -97458,6 +97480,235 @@ function serializeProviderCatalog(providers) {
97458
97480
  return catalog;
97459
97481
  }
97460
97482
  //#endregion
97483
+ //#region src/provider/personalClaudeInVm.ts
97484
+ /**
97485
+ * CMMD writes this marker when it activates a personal Claude subscription
97486
+ * inside a running sprite. Activation always happens after boot, so a
97487
+ * boot-time env var alone cannot reflect it (FOR-36249).
97488
+ *
97489
+ * CMMD names the file in the Service env (FORGE_PERSONAL_CLAUDE_MARKER_PATH).
97490
+ * Without that, the marker sits directly under FORGE_HOME, which is the same
97491
+ * guest home CMMD derives every sprite path from. A server with neither has no
97492
+ * sprite home to look in, and a desktop never has a marker, so it reads none.
97493
+ */
97494
+ const PERSONAL_CLAUDE_IN_VM_MARKER_NAME = ".forge-personal-claude-in-vm";
97495
+ const PERSONAL_CLAUDE_GENERATION_FILE_NAME = ".forge-personal-claude-generation";
97496
+ function markerPath(env) {
97497
+ if (env.FORGE_PERSONAL_CLAUDE_MARKER_PATH) return env.FORGE_PERSONAL_CLAUDE_MARKER_PATH;
97498
+ if (env.FORGE_HOME) return join(env.FORGE_HOME, PERSONAL_CLAUDE_IN_VM_MARKER_NAME);
97499
+ return null;
97500
+ }
97501
+ /**
97502
+ * True when this runtime should offer the personal Claude subscription lane.
97503
+ *
97504
+ * Accepts either signal: the boot-time env var (backward compatible) or the
97505
+ * activation marker on disk. Reading the marker at call time lets the provider
97506
+ * catalog and the per-turn readiness guard self-heal once CMMD delivers,
97507
+ * without a sprite reboot. Callers still verify the credential is non-empty
97508
+ * before treating it as usable; this only decides whether the lane is active.
97509
+ */
97510
+ function personalClaudeInVmEnabled(env = process.env) {
97511
+ if (env.FORGE_PERSONAL_CLAUDE_IN_VM === "true") return true;
97512
+ const marker = markerPath(env);
97513
+ if (!marker) return false;
97514
+ try {
97515
+ return existsSync(marker);
97516
+ } catch {
97517
+ return false;
97518
+ }
97519
+ }
97520
+ /** Exported at boot. This is the restart proof CMMD waits on, so it must never read the file. */
97521
+ function bootExportedPersonalClaudeGenerationHash(env = process.env) {
97522
+ return env.FORGE_PERSONAL_CLAUDE_GENERATION_HASH?.trim() ?? "";
97523
+ }
97524
+ /**
97525
+ * The sha256 of the Claude credential generation this runtime holds, or "" when
97526
+ * it holds none. The readiness guard compares it with the generation CMMD
97527
+ * applied. The restart proof must not use it; it uses the boot-exported hash.
97528
+ *
97529
+ * The boot preload exports FORGE_PERSONAL_CLAUDE_GENERATION_HASH, but only when
97530
+ * the marker already exists at boot. Activation in place writes the generation
97531
+ * file and touches the marker without setting any env, so an empty variable
97532
+ * falls back to that file, hashed the way the preload hashes it. CMMD puts the
97533
+ * file beside the marker in the guest home and names no path for it. A marker
97534
+ * with no generation file is a legacy runtime, and gets no hash here either.
97535
+ */
97536
+ function personalClaudeGenerationHash(env = process.env) {
97537
+ const exported = bootExportedPersonalClaudeGenerationHash(env);
97538
+ if (exported) return exported;
97539
+ const marker = markerPath(env);
97540
+ if (!marker) return "";
97541
+ try {
97542
+ if (!existsSync(marker)) return "";
97543
+ const generation = readFileSync(join(dirname(marker), PERSONAL_CLAUDE_GENERATION_FILE_NAME), "utf8").trim();
97544
+ return generation ? createHash("sha256").update(generation).digest("hex") : "";
97545
+ } catch {
97546
+ return "";
97547
+ }
97548
+ }
97549
+ //#endregion
97550
+ //#region src/provider/providerAuthFailureCopy.ts
97551
+ /**
97552
+ * Rewrite provider auth failures into an action the user can actually take.
97553
+ *
97554
+ * The provider SDKs assume they are running under a human at a terminal, so
97555
+ * their auth errors name CLI verbs: Claude's is literally
97556
+ * `Not logged in · Please run /login`. On Forge Cloud that reaches a browser
97557
+ * user who has no terminal, no shell, and no way to run `/login` — the turn
97558
+ * dies in ~3 s naming an action that does not exist for them (observed live
97559
+ * 2026-08-05).
97560
+ *
97561
+ * CMMD already writes the right shape for the Codex egress path — "Connect
97562
+ * ChatGPT in Settings > Providers, then retry" — which names a destination the
97563
+ * user can click. This gives the provider-SDK failures the same treatment.
97564
+ *
97565
+ * Scope is deliberately narrow. Only messages that clearly identify a
97566
+ * *credential* problem are rewritten; everything else passes through verbatim,
97567
+ * because a rewritten unknown error loses detail an operator needs and risks
97568
+ * telling the user to reconnect an account that was never the problem.
97569
+ */
97570
+ /**
97571
+ * Signatures of "you have no usable credential", matched case-insensitively.
97572
+ *
97573
+ * Kept as explicit patterns rather than a broad /auth/i so that genuine
97574
+ * mid-turn failures mentioning authorization (a tool hitting a 403 on some
97575
+ * unrelated API, say) are not misreported as "your subscription is
97576
+ * disconnected".
97577
+ */
97578
+ const CREDENTIAL_FAILURE_PATTERNS = [
97579
+ /\bnot logged in\b/i,
97580
+ /\bplease run\s*\/login\b/i,
97581
+ /\brun\s*\/login\b/i,
97582
+ /\blogin required\b/i,
97583
+ /\bnot authenticated\b/i,
97584
+ /\bno credentials? found\b/i,
97585
+ /\binvalid api key\b/i,
97586
+ /\bauthentication_error\b/i
97587
+ ];
97588
+ /**
97589
+ * Claude's words for a delivered credential whose refresh chain is dead.
97590
+ *
97591
+ * Deliberately NOT in CREDENTIAL_FAILURE_PATTERNS. On a runtime whose Claude
97592
+ * credential CMMD delivers, this is not "you are signed out" and reconnecting
97593
+ * in Settings is not the fix: CMMD's authority rotates the refresh token on
97594
+ * every refresh, the snapshot in the guest dies at the next rotation, and the
97595
+ * repair is a re-delivery the next send triggers (FOR-36425, CMMD-36441).
97596
+ * Anywhere else the credential is the user's own local login, so the CLI text
97597
+ * still passes through untouched.
97598
+ */
97599
+ const DELIVERED_CREDENTIAL_EXPIRED_PATTERN = /\boauth session expired\b/i;
97600
+ const DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY = "The delivered Claude credential expired. CMMD refreshes it on the next send, so retry this turn.";
97601
+ /** True when the message is about a missing/unusable credential, not a tool failure. */
97602
+ function isProviderCredentialFailure(message) {
97603
+ return CREDENTIAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message));
97604
+ }
97605
+ /**
97606
+ * Map a ProviderKind to the account name a user would recognise.
97607
+ *
97608
+ * NOTE the literal is `claudeAgent`, not `claude`. This read `"claude"` and so
97609
+ * never matched, silently degrading every Claude auth failure to the generic
97610
+ * fallback — "Forge Cloud has no **this provider** subscription connected."
97611
+ * Same provider-name drift class as the model-mapping fix (#2103/#2105).
97612
+ *
97613
+ * The names come from the shared PROVIDER_ACCOUNT_NAMES map rather than a local
97614
+ * switch: this file's copy was the ONLY correct provider→label mapping in the
97615
+ * codebase while four client call sites reimplemented it as binary ternaries.
97616
+ * One Record over ProviderKind means a new provider is a compile error in one
97617
+ * place instead of five sentences that read wrong in production.
97618
+ *
97619
+ * Providers that do not sell a personal subscription stay generic on purpose —
97620
+ * "Forge Cloud has no Ollama subscription connected" names something that does
97621
+ * not exist.
97622
+ */
97623
+ function providerLabel(provider) {
97624
+ return supportsPersonalSubscription(provider) ? PROVIDER_ACCOUNT_NAMES[provider] : "this provider";
97625
+ }
97626
+ /**
97627
+ * Return the message to show for a failed turn.
97628
+ *
97629
+ * `surface` decides the destination: a cloud runtime has no terminal, so it is
97630
+ * pointed at Settings; a local/desktop runtime CAN run the CLI login, so its
97631
+ * copy still offers that as the concrete fix rather than sending someone to a
97632
+ * settings page that is not how they authenticate.
97633
+ */
97634
+ function rewriteProviderAuthFailure(input) {
97635
+ const original = input.message ?? "";
97636
+ if (original.length === 0) return {
97637
+ message: original,
97638
+ rewritten: false
97639
+ };
97640
+ if (input.deliveredClaudeCredential === true && DELIVERED_CREDENTIAL_EXPIRED_PATTERN.test(original)) return {
97641
+ message: DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY,
97642
+ rewritten: true
97643
+ };
97644
+ if (!isProviderCredentialFailure(original)) return {
97645
+ message: original,
97646
+ rewritten: false
97647
+ };
97648
+ const who = providerLabel(input.provider);
97649
+ return {
97650
+ 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.`,
97651
+ rewritten: true
97652
+ };
97653
+ }
97654
+ /**
97655
+ * Convenience for call sites that just want the string to show.
97656
+ *
97657
+ * Exists so adapters do not each re-derive the surface and re-handle the
97658
+ * undefined case inline — that inlining is what pushed
97659
+ * ClaudeAdapter.handleResultMessage over the cyclomatic gate. Returns
97660
+ * `undefined` unchanged so "no error" stays distinguishable from "empty error".
97661
+ */
97662
+ function providerAuthFailureMessage(message, provider, env = process.env) {
97663
+ if (message === void 0) return void 0;
97664
+ return rewriteProviderAuthFailure({
97665
+ message,
97666
+ provider,
97667
+ surface: env.FORGE_CMMD_RUNTIME_MODE === "true" ? "cloud" : "local",
97668
+ deliveredClaudeCredential: provider === "claudeAgent" && personalClaudeInVmEnabled(env)
97669
+ }).message;
97670
+ }
97671
+ //#endregion
97672
+ //#region src/orchestration/turnFailureReason.ts
97673
+ /**
97674
+ * Machine-readable reasons for a turn that failed before it produced any
97675
+ * assistant output, replicated to CMMD on the `turn_status` event
97676
+ * (CMMD-Center/forge#3496, CMMD-Center/CMMD#5272). Codes match the catalog
97677
+ * CMMD renders actions from (CMMD-Center/CMMD#5289): a code CMMD does not
97678
+ * recognize degrades to its own generic fallback, so a new Forge failure
97679
+ * class is safe to ship before CMMD's catalog knows about it.
97680
+ */
97681
+ const GITHUB_GRANT_DETAIL_PATTERN = /exact scoped runtime GitHub grant/i;
97682
+ /**
97683
+ * The exact-machine broker's own wording (`providerEgressToken.ts`) for all
97684
+ * three reasons it could not produce a provider token: an incomplete runtime
97685
+ * identity, no owner session bound to the request, or no live broker
97686
+ * authority. Every one of those is a real authentication refusal, not a
97687
+ * workspace or adapter failure, and the prefix is unique to that code path.
97688
+ */
97689
+ const EXACT_MACHINE_BROKER_AUTHORITY_PATTERN = /provider token issuance requires a live exact-machine broker authority/i;
97690
+ /**
97691
+ * Classifies a `provider.turn.start.failed` activity's detail. This activity
97692
+ * kind also covers non-auth causes (a draining runtime, a missing user
97693
+ * message, a rejected workspace, a start-timeout cleanup failure), so only
97694
+ * text that names a known authentication refusal is reported as one; every
97695
+ * other detail degrades to `unspecified_failure` rather than telling CMMD to
97696
+ * offer a reconnect action credentials cannot fix.
97697
+ */
97698
+ function classifyProviderTurnStartFailureReason(detail) {
97699
+ const message = detail.trim().length > 0 ? detail : "The provider could not start a session for this run.";
97700
+ return {
97701
+ 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",
97702
+ message
97703
+ };
97704
+ }
97705
+ function checkpointTimeoutReason(detail) {
97706
+ return {
97707
+ code: "checkpoint_timeout",
97708
+ message: detail.trim().length > 0 ? detail : "The workspace checkpoint timed out before the turn could start."
97709
+ };
97710
+ }
97711
+ //#endregion
97461
97712
  //#region src/runtimeReplication/replicaSerializer.ts
97462
97713
  const replicaTurnStatuses = new Set([
97463
97714
  "pending",
@@ -97798,6 +98049,53 @@ function serializeFailureActivity(activity) {
97798
98049
  }
97799
98050
  };
97800
98051
  }
98052
+ /** Only the pre-turn baseline capture sets this; a post-turn capture failure
98053
+ * never blocked the turn from starting and must not fail it retroactively. */
98054
+ function blocksTurnStart(activity) {
98055
+ const payload = activity.payload;
98056
+ if (!payload || typeof payload !== "object") return false;
98057
+ return payload.blocksTurnStart === true;
98058
+ }
98059
+ function isCheckpointTimeoutDetail(detail) {
98060
+ return /timed out/i.test(detail);
98061
+ }
98062
+ /**
98063
+ * The structured reason a turn never started, when this activity is one of
98064
+ * the two known causes (CMMD-Center/forge#3496): the provider refused to
98065
+ * start a session, or the pre-turn checkpoint that must precede it timed out.
98066
+ * `null` for every other activity, including a checkpoint failure that did
98067
+ * not block turn start or was not a timeout.
98068
+ */
98069
+ function turnStartFailureReason(activity) {
98070
+ const detail = failureDetail(activity);
98071
+ if (activity.kind === "provider.turn.start.failed") return classifyProviderTurnStartFailureReason(detail ?? "");
98072
+ if (activity.kind === "checkpoint.capture.failed" && blocksTurnStart(activity) && detail !== null && isCheckpointTimeoutDetail(detail)) return checkpointTimeoutReason(detail);
98073
+ return null;
98074
+ }
98075
+ /**
98076
+ * Replicates a turn-start failure as `turn_status: failed` with a structured
98077
+ * `reason`, alongside (not instead of) the free-text `error_summary` above:
98078
+ * CMMD fails the owning Work Session from this event
98079
+ * (CMMD-Center/CMMD#5272/#5286), which `error_summary` alone cannot drive.
98080
+ */
98081
+ function serializeTurnStartFailureStatus(activity) {
98082
+ const reason = turnStartFailureReason(activity);
98083
+ if (reason === null) return null;
98084
+ return {
98085
+ sourceSequence: null,
98086
+ event: {
98087
+ eventId: `activity:${activity.id}:turn-status`,
98088
+ occurredAt: canonicalRuntimeEventTimestamp(activity.createdAt),
98089
+ ...eventTurn(activity.turnId),
98090
+ kind: "turn_status",
98091
+ status: "failed",
98092
+ reason: {
98093
+ code: reason.code,
98094
+ message: text(reason.message, 2e3) ?? reason.message
98095
+ }
98096
+ }
98097
+ };
98098
+ }
97801
98099
  const turnUsageKeys = [
97802
98100
  "uncachedInputTokens",
97803
98101
  "cachedInputTokens",
@@ -98146,7 +98444,7 @@ function serializeRuntimeThreadEvents(thread) {
98146
98444
  });
98147
98445
  const recordedTurns = serializeRecordedTurns(thread);
98148
98446
  const textGenerations = serializeTextGenerations(thread);
98149
- return [
98447
+ const priorEvents = [
98150
98448
  ...messages,
98151
98449
  ...replicaActivities,
98152
98450
  ...toolActivities,
@@ -98155,7 +98453,15 @@ function serializeRuntimeThreadEvents(thread) {
98155
98453
  ...failureActivities,
98156
98454
  ...recordedTurns,
98157
98455
  ...textGenerations
98158
- ].toSorted((left, right) => {
98456
+ ];
98457
+ const latestPriorOccurredAt = priorEvents.reduce((latest, entry) => latest === null || entry.event.occurredAt > latest ? entry.event.occurredAt : latest, null);
98458
+ const turnStartFailureStatuses = thread.activities.flatMap((activity) => {
98459
+ const event = serializeTurnStartFailureStatus(activity);
98460
+ if (event === null) return [];
98461
+ if (latestPriorOccurredAt !== null && event.event.occurredAt < latestPriorOccurredAt) return [];
98462
+ return [event];
98463
+ });
98464
+ return [...priorEvents, ...turnStartFailureStatuses].toSorted((left, right) => {
98159
98465
  if (left.sourceSequence !== null && right.sourceSequence !== null) return left.sourceSequence - right.sourceSequence;
98160
98466
  if (left.sourceSequence !== null) return -1;
98161
98467
  if (right.sourceSequence !== null) return 1;
@@ -152071,7 +152377,7 @@ async function prepareWorkspaceRepositoryDependencies(input) {
152071
152377
  const dependencies = resolveWorkspaceEnvironmentConfig(input.cwd).repositoryDependencies;
152072
152378
  if (dependencies.length === 0) return [];
152073
152379
  const broker = input.broker ?? new GitHubBroker();
152074
- const auth = broker.runtimeGrantAuthContext("github");
152380
+ const auth = broker.runtimeGrantAuthContext("github") ?? await broker.requestRuntimeGithubGrant?.() ?? null;
152075
152381
  if (auth === null) throw new Error("Repository dependencies require an exact scoped runtime GitHub grant.");
152076
152382
  const readOrigin = input.readOrigin ?? readOriginFromGit;
152077
152383
  const resolved = resolveRepositoryDependencies(dependencies, input.cloudProjectsRoot);
@@ -191200,7 +191506,7 @@ var Vce = {
191200
191506
  function Ve() {
191201
191507
  return Vce;
191202
191508
  }
191203
- var Us = os.homedir(), lk = os.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
191509
+ var Us = $U.homedir(), lk = $U.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
191204
191510
  let t = path.join(Us, "Library");
191205
191511
  return {
191206
191512
  data: path.join(t, "Application Support", e),
@@ -201400,12 +201706,12 @@ function qZ(e, t) {
201400
201706
  }
201401
201707
  return n;
201402
201708
  }
201403
- var os$2 = "https://code.claude.com/docs/en", kKe = [
201709
+ var os$1 = "https://code.claude.com/docs/en", kKe = [
201404
201710
  {
201405
201711
  matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
201406
201712
  tip: {
201407
201713
  suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
201408
- docLink: `${os$2}/iam#permission-modes`
201714
+ docLink: `${os$1}/iam#permission-modes`
201409
201715
  }
201410
201716
  },
201411
201717
  {
@@ -201420,7 +201726,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
201420
201726
  matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
201421
201727
  tip: {
201422
201728
  suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
201423
- docLink: `${os$2}/settings#environment-variables`
201729
+ docLink: `${os$1}/settings#environment-variables`
201424
201730
  }
201425
201731
  },
201426
201732
  {
@@ -201431,14 +201737,14 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
201431
201737
  matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
201432
201738
  tip: {
201433
201739
  suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
201434
- docLink: `${os$2}/hooks`
201740
+ docLink: `${os$1}/hooks`
201435
201741
  }
201436
201742
  },
201437
201743
  {
201438
201744
  matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
201439
201745
  tip: {
201440
201746
  suggestion: "Command hooks require `command`. For exec form (no shell), set `command` to the executable and `args` to its arguments: {\"type\": \"command\", \"command\": \"echo\", \"args\": [\"hi\"]}. For shell form, set `command` to the full shell string: {\"type\": \"command\", \"command\": \"echo hi\"}.",
201441
- docLink: `${os$2}/hooks#exec-form-and-shell-form`
201747
+ docLink: `${os$1}/hooks#exec-form-and-shell-form`
201442
201748
  }
201443
201749
  },
201444
201750
  {
@@ -201453,7 +201759,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
201453
201759
  matches: (e) => e.code === "unrecognized_keys",
201454
201760
  tip: {
201455
201761
  suggestion: "Check for typos or refer to the documentation for valid fields",
201456
- docLink: `${os$2}/settings`
201762
+ docLink: `${os$1}/settings`
201457
201763
  }
201458
201764
  },
201459
201765
  {
@@ -201468,13 +201774,13 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
201468
201774
  matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
201469
201775
  tip: {
201470
201776
  suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
201471
- docLink: `${os$2}/iam#working-directories`
201777
+ docLink: `${os$1}/iam#working-directories`
201472
201778
  }
201473
201779
  }
201474
201780
  ], AKe = {
201475
- permissions: `${os$2}/iam#configuring-permissions`,
201476
- env: `${os$2}/settings#environment-variables`,
201477
- hooks: `${os$2}/hooks`
201781
+ permissions: `${os$1}/iam#configuring-permissions`,
201782
+ env: `${os$1}/settings#environment-variables`,
201783
+ hooks: `${os$1}/hooks`
201478
201784
  };
201479
201785
  function GZ(e) {
201480
201786
  let t = kKe.find((r) => r.matches(e));
@@ -213077,73 +213383,6 @@ function primaryClaudeCredentialMatchesPrepared(env, preparedDirectory) {
213077
213383
  }
213078
213384
  }
213079
213385
  //#endregion
213080
- //#region src/provider/personalClaudeInVm.ts
213081
- /**
213082
- * CMMD writes this marker when it activates a personal Claude subscription
213083
- * inside a running sprite. Activation always happens after boot, so a
213084
- * boot-time env var alone cannot reflect it (FOR-36249).
213085
- *
213086
- * CMMD names the file in the Service env (FORGE_PERSONAL_CLAUDE_MARKER_PATH).
213087
- * Without that, the marker sits directly under FORGE_HOME, which is the same
213088
- * guest home CMMD derives every sprite path from. A server with neither has no
213089
- * sprite home to look in, and a desktop never has a marker, so it reads none.
213090
- */
213091
- const PERSONAL_CLAUDE_IN_VM_MARKER_NAME = ".forge-personal-claude-in-vm";
213092
- const PERSONAL_CLAUDE_GENERATION_FILE_NAME = ".forge-personal-claude-generation";
213093
- function markerPath(env) {
213094
- if (env.FORGE_PERSONAL_CLAUDE_MARKER_PATH) return env.FORGE_PERSONAL_CLAUDE_MARKER_PATH;
213095
- if (env.FORGE_HOME) return join(env.FORGE_HOME, PERSONAL_CLAUDE_IN_VM_MARKER_NAME);
213096
- return null;
213097
- }
213098
- /**
213099
- * True when this runtime should offer the personal Claude subscription lane.
213100
- *
213101
- * Accepts either signal: the boot-time env var (backward compatible) or the
213102
- * activation marker on disk. Reading the marker at call time lets the provider
213103
- * catalog and the per-turn readiness guard self-heal once CMMD delivers,
213104
- * without a sprite reboot. Callers still verify the credential is non-empty
213105
- * before treating it as usable; this only decides whether the lane is active.
213106
- */
213107
- function personalClaudeInVmEnabled(env = process.env) {
213108
- if (env.FORGE_PERSONAL_CLAUDE_IN_VM === "true") return true;
213109
- const marker = markerPath(env);
213110
- if (!marker) return false;
213111
- try {
213112
- return existsSync(marker);
213113
- } catch {
213114
- return false;
213115
- }
213116
- }
213117
- /** Exported at boot. This is the restart proof CMMD waits on, so it must never read the file. */
213118
- function bootExportedPersonalClaudeGenerationHash(env = process.env) {
213119
- return env.FORGE_PERSONAL_CLAUDE_GENERATION_HASH?.trim() ?? "";
213120
- }
213121
- /**
213122
- * The sha256 of the Claude credential generation this runtime holds, or "" when
213123
- * it holds none. The readiness guard compares it with the generation CMMD
213124
- * applied. The restart proof must not use it; it uses the boot-exported hash.
213125
- *
213126
- * The boot preload exports FORGE_PERSONAL_CLAUDE_GENERATION_HASH, but only when
213127
- * the marker already exists at boot. Activation in place writes the generation
213128
- * file and touches the marker without setting any env, so an empty variable
213129
- * falls back to that file, hashed the way the preload hashes it. CMMD puts the
213130
- * file beside the marker in the guest home and names no path for it. A marker
213131
- * with no generation file is a legacy runtime, and gets no hash here either.
213132
- */
213133
- function personalClaudeGenerationHash(env = process.env) {
213134
- const exported = bootExportedPersonalClaudeGenerationHash(env);
213135
- if (exported) return exported;
213136
- const marker = markerPath(env);
213137
- if (!marker) return "";
213138
- try {
213139
- if (!existsSync(marker)) return "";
213140
- const generation = readFileSync(join(dirname(marker), PERSONAL_CLAUDE_GENERATION_FILE_NAME), "utf8").trim();
213141
- return generation ? createHash("sha256").update(generation).digest("hex") : "";
213142
- } catch {
213143
- return "";
213144
- }
213145
- }
213146
- //#endregion
213147
213386
  //#region src/provider/claudeCredentialContainment.ts
213148
213387
  /**
213149
213388
  * Containment for provider credentials inherited by the Claude Agent SDK child.
@@ -215224,128 +215463,6 @@ function createGitHubMcpServer(runBrokeredTool, threadId, settings) {
215224
215463
  });
215225
215464
  }
215226
215465
  //#endregion
215227
- //#region src/provider/providerAuthFailureCopy.ts
215228
- /**
215229
- * Rewrite provider auth failures into an action the user can actually take.
215230
- *
215231
- * The provider SDKs assume they are running under a human at a terminal, so
215232
- * their auth errors name CLI verbs: Claude's is literally
215233
- * `Not logged in · Please run /login`. On Forge Cloud that reaches a browser
215234
- * user who has no terminal, no shell, and no way to run `/login` — the turn
215235
- * dies in ~3 s naming an action that does not exist for them (observed live
215236
- * 2026-08-05).
215237
- *
215238
- * CMMD already writes the right shape for the Codex egress path — "Connect
215239
- * ChatGPT in Settings > Providers, then retry" — which names a destination the
215240
- * user can click. This gives the provider-SDK failures the same treatment.
215241
- *
215242
- * Scope is deliberately narrow. Only messages that clearly identify a
215243
- * *credential* problem are rewritten; everything else passes through verbatim,
215244
- * because a rewritten unknown error loses detail an operator needs and risks
215245
- * telling the user to reconnect an account that was never the problem.
215246
- */
215247
- /**
215248
- * Signatures of "you have no usable credential", matched case-insensitively.
215249
- *
215250
- * Kept as explicit patterns rather than a broad /auth/i so that genuine
215251
- * mid-turn failures mentioning authorization (a tool hitting a 403 on some
215252
- * unrelated API, say) are not misreported as "your subscription is
215253
- * disconnected".
215254
- */
215255
- const CREDENTIAL_FAILURE_PATTERNS = [
215256
- /\bnot logged in\b/i,
215257
- /\bplease run\s*\/login\b/i,
215258
- /\brun\s*\/login\b/i,
215259
- /\blogin required\b/i,
215260
- /\bnot authenticated\b/i,
215261
- /\bno credentials? found\b/i,
215262
- /\binvalid api key\b/i,
215263
- /\bauthentication_error\b/i
215264
- ];
215265
- /**
215266
- * Claude's words for a delivered credential whose refresh chain is dead.
215267
- *
215268
- * Deliberately NOT in CREDENTIAL_FAILURE_PATTERNS. On a runtime whose Claude
215269
- * credential CMMD delivers, this is not "you are signed out" and reconnecting
215270
- * in Settings is not the fix: CMMD's authority rotates the refresh token on
215271
- * every refresh, the snapshot in the guest dies at the next rotation, and the
215272
- * repair is a re-delivery the next send triggers (FOR-36425, CMMD-36441).
215273
- * Anywhere else the credential is the user's own local login, so the CLI text
215274
- * still passes through untouched.
215275
- */
215276
- const DELIVERED_CREDENTIAL_EXPIRED_PATTERN = /\boauth session expired\b/i;
215277
- const DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY = "The delivered Claude credential expired. CMMD refreshes it on the next send, so retry this turn.";
215278
- /** True when the message is about a missing/unusable credential, not a tool failure. */
215279
- function isProviderCredentialFailure(message) {
215280
- return CREDENTIAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message));
215281
- }
215282
- /**
215283
- * Map a ProviderKind to the account name a user would recognise.
215284
- *
215285
- * NOTE the literal is `claudeAgent`, not `claude`. This read `"claude"` and so
215286
- * never matched, silently degrading every Claude auth failure to the generic
215287
- * fallback — "Forge Cloud has no **this provider** subscription connected."
215288
- * Same provider-name drift class as the model-mapping fix (#2103/#2105).
215289
- *
215290
- * The names come from the shared PROVIDER_ACCOUNT_NAMES map rather than a local
215291
- * switch: this file's copy was the ONLY correct provider→label mapping in the
215292
- * codebase while four client call sites reimplemented it as binary ternaries.
215293
- * One Record over ProviderKind means a new provider is a compile error in one
215294
- * place instead of five sentences that read wrong in production.
215295
- *
215296
- * Providers that do not sell a personal subscription stay generic on purpose —
215297
- * "Forge Cloud has no Ollama subscription connected" names something that does
215298
- * not exist.
215299
- */
215300
- function providerLabel(provider) {
215301
- return supportsPersonalSubscription(provider) ? PROVIDER_ACCOUNT_NAMES[provider] : "this provider";
215302
- }
215303
- /**
215304
- * Return the message to show for a failed turn.
215305
- *
215306
- * `surface` decides the destination: a cloud runtime has no terminal, so it is
215307
- * pointed at Settings; a local/desktop runtime CAN run the CLI login, so its
215308
- * copy still offers that as the concrete fix rather than sending someone to a
215309
- * settings page that is not how they authenticate.
215310
- */
215311
- function rewriteProviderAuthFailure(input) {
215312
- const original = input.message ?? "";
215313
- if (original.length === 0) return {
215314
- message: original,
215315
- rewritten: false
215316
- };
215317
- if (input.deliveredClaudeCredential === true && DELIVERED_CREDENTIAL_EXPIRED_PATTERN.test(original)) return {
215318
- message: DELIVERED_CLAUDE_CREDENTIAL_EXPIRED_COPY,
215319
- rewritten: true
215320
- };
215321
- if (!isProviderCredentialFailure(original)) return {
215322
- message: original,
215323
- rewritten: false
215324
- };
215325
- const who = providerLabel(input.provider);
215326
- return {
215327
- 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.`,
215328
- rewritten: true
215329
- };
215330
- }
215331
- /**
215332
- * Convenience for call sites that just want the string to show.
215333
- *
215334
- * Exists so adapters do not each re-derive the surface and re-handle the
215335
- * undefined case inline — that inlining is what pushed
215336
- * ClaudeAdapter.handleResultMessage over the cyclomatic gate. Returns
215337
- * `undefined` unchanged so "no error" stays distinguishable from "empty error".
215338
- */
215339
- function providerAuthFailureMessage(message, provider, env = process.env) {
215340
- if (message === void 0) return void 0;
215341
- return rewriteProviderAuthFailure({
215342
- message,
215343
- provider,
215344
- surface: env.FORGE_CMMD_RUNTIME_MODE === "true" ? "cloud" : "local",
215345
- deliveredClaudeCredential: provider === "claudeAgent" && personalClaudeInVmEnabled(env)
215346
- }).message;
215347
- }
215348
- //#endregion
215349
215466
  //#region src/provider/missingWorkspaceCopy.ts
215350
215467
  /**
215351
215468
  * Refuse a turn whose checkout folder is gone, and say so.
@@ -233674,6 +233791,8 @@ const CheckpointDiffQueryLive = effect(CheckpointDiffQuery, gen(function* () {
233674
233791
  * @module CheckpointStoreLive
233675
233792
  */
233676
233793
  const CHECKPOINT_PATH_OUTPUT_MAX_BYTES = 16 * 1024 * 1024;
233794
+ const CheckpointCaptureTimeoutConfig = number$2("FORGE_CHECKPOINT_CAPTURE_TIMEOUT_MS").pipe(withDefault$3(6e4));
233795
+ const EFFECT_TIMER_MAX_MS = 2147483647;
233677
233796
  function parseNullSeparatedPaths(output) {
233678
233797
  return output.split("\0").filter((entry) => entry.length > 0);
233679
233798
  }
@@ -233681,7 +233800,7 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233681
233800
  const fs = yield* FileSystem;
233682
233801
  const path = yield* Path$2;
233683
233802
  const git = yield* GitCore;
233684
- const resolveHeadCommit = (cwd) => git.execute({
233803
+ const resolveHeadCommit = (cwd, timeoutMs) => git.execute({
233685
233804
  operation: "CheckpointStore.resolveHeadCommit",
233686
233805
  cwd,
233687
233806
  args: [
@@ -233690,7 +233809,8 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233690
233809
  "--quiet",
233691
233810
  "HEAD^{commit}"
233692
233811
  ],
233693
- allowNonZeroExit: true
233812
+ allowNonZeroExit: true,
233813
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
233694
233814
  }).pipe(map$3((result) => {
233695
233815
  if (result.code !== 0) return null;
233696
233816
  const commit = result.stdout.trim();
@@ -233729,7 +233849,9 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233729
233849
  }).pipe(map$3((result) => result.code === 0 && result.stdout.trim() === "true"), catch_(() => succeed(false)));
233730
233850
  const captureCheckpoint = fn$1("captureCheckpoint")(function* (input) {
233731
233851
  const operation = "CheckpointStore.captureCheckpoint";
233732
- const headCommit = yield* resolveHeadCommit(input.cwd);
233852
+ const captureTimeoutMs = yield* CheckpointCaptureTimeoutConfig.asEffect().pipe(orDie);
233853
+ if (!Number.isFinite(captureTimeoutMs) || captureTimeoutMs <= 0 || captureTimeoutMs > EFFECT_TIMER_MAX_MS) return yield* die$1(/* @__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}.`));
233854
+ const headCommit = yield* resolveHeadCommit(input.cwd, captureTimeoutMs);
233733
233855
  const [trackedPathResult, untrackedPathResult] = yield* all([git.execute({
233734
233856
  operation,
233735
233857
  cwd: input.cwd,
@@ -233746,7 +233868,8 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233746
233868
  "-z",
233747
233869
  "--"
233748
233870
  ],
233749
- maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES
233871
+ maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES,
233872
+ timeoutMs: captureTimeoutMs
233750
233873
  }), git.execute({
233751
233874
  operation,
233752
233875
  cwd: input.cwd,
@@ -233757,7 +233880,8 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233757
233880
  "-z",
233758
233881
  "--"
233759
233882
  ],
233760
- maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES
233883
+ maxOutputBytes: CHECKPOINT_PATH_OUTPUT_MAX_BYTES,
233884
+ timeoutMs: captureTimeoutMs
233761
233885
  })], { concurrency: 2 });
233762
233886
  const changedPaths = [...new Set([...parseNullSeparatedPaths(trackedPathResult.stdout), ...parseNullSeparatedPaths(untrackedPathResult.stdout)])];
233763
233887
  if (headCommit && changedPaths.length === 0) {
@@ -233768,7 +233892,8 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233768
233892
  "update-ref",
233769
233893
  input.checkpointRef,
233770
233894
  headCommit
233771
- ]
233895
+ ],
233896
+ timeoutMs: captureTimeoutMs
233772
233897
  });
233773
233898
  return;
233774
233899
  }
@@ -233786,13 +233911,15 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233786
233911
  operation,
233787
233912
  cwd: input.cwd,
233788
233913
  args: ["read-tree", headCommit],
233789
- env: commitEnv
233914
+ env: commitEnv,
233915
+ timeoutMs: captureTimeoutMs
233790
233916
  });
233791
233917
  else yield* git.execute({
233792
233918
  operation,
233793
233919
  cwd: input.cwd,
233794
233920
  args: ["read-tree", "--empty"],
233795
- env: commitEnv
233921
+ env: commitEnv,
233922
+ timeoutMs: captureTimeoutMs
233796
233923
  });
233797
233924
  if (changedPaths.length > 0) yield* git.execute({
233798
233925
  operation,
@@ -233805,13 +233932,15 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233805
233932
  "--pathspec-file-nul"
233806
233933
  ],
233807
233934
  stdin: changedPaths.map((changedPath) => `${changedPath}\0`).join(""),
233808
- env: commitEnv
233935
+ env: commitEnv,
233936
+ timeoutMs: captureTimeoutMs
233809
233937
  });
233810
233938
  const treeOid = (yield* git.execute({
233811
233939
  operation,
233812
233940
  cwd: input.cwd,
233813
233941
  args: ["write-tree"],
233814
- env: commitEnv
233942
+ env: commitEnv,
233943
+ timeoutMs: captureTimeoutMs
233815
233944
  })).stdout.trim();
233816
233945
  if (treeOid.length === 0) return yield* new GitCommandError({
233817
233946
  operation,
@@ -233829,7 +233958,8 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233829
233958
  "-m",
233830
233959
  message
233831
233960
  ],
233832
- env: commitEnv
233961
+ env: commitEnv,
233962
+ timeoutMs: captureTimeoutMs
233833
233963
  })).stdout.trim();
233834
233964
  if (commitOid.length === 0) return yield* new GitCommandError({
233835
233965
  operation,
@@ -233844,7 +233974,8 @@ const CheckpointStoreLive = effect(CheckpointStore, gen(function* () {
233844
233974
  "update-ref",
233845
233975
  input.checkpointRef,
233846
233976
  commitOid
233847
- ]
233977
+ ],
233978
+ timeoutMs: captureTimeoutMs
233848
233979
  });
233849
233980
  }), (tempDir) => fs.remove(tempDir, { recursive: true })).pipe(catchTags({ PlatformError: (error) => fail(new CheckpointInvariantError({
233850
233981
  operation: "CheckpointStore.captureCheckpoint",
@@ -269601,7 +269732,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
269601
269732
  var dgram = __require("dgram");
269602
269733
  var thunky = require_thunky();
269603
269734
  var events = __require("events");
269604
- var os$1 = __require("os");
269735
+ var os = __require("os");
269605
269736
  var noop = function() {};
269606
269737
  module.exports = function(opts) {
269607
269738
  if (!opts) opts = {};
@@ -269732,14 +269863,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
269732
269863
  return that;
269733
269864
  };
269734
269865
  function defaultInterface() {
269735
- var networks = os$1.networkInterfaces();
269866
+ var networks = os.networkInterfaces();
269736
269867
  var names = Object.keys(networks);
269737
269868
  for (var i = 0; i < names.length; i++) {
269738
269869
  var net = networks[names[i]];
269739
269870
  for (var j = 0; j < net.length; j++) {
269740
269871
  var iface = net[j];
269741
269872
  if (isIPv4(iface.family) && !iface.internal) {
269742
- if (os$1.platform() === "darwin" && names[i] === "en0") return iface.address;
269873
+ if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
269743
269874
  return "0.0.0.0";
269744
269875
  }
269745
269876
  }
@@ -269747,7 +269878,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
269747
269878
  return "127.0.0.1";
269748
269879
  }
269749
269880
  function allInterfaces() {
269750
- var networks = os$1.networkInterfaces();
269881
+ var networks = os.networkInterfaces();
269751
269882
  var names = Object.keys(networks);
269752
269883
  var res = [];
269753
269884
  for (var i = 0; i < names.length; i++) {
@@ -291680,7 +291811,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
291680
291811
  summary: "Agent turn blocked",
291681
291812
  detail: "Forge runtime is updating after the current turn. New agent turns are paused until the runtime finishes draining.",
291682
291813
  turnId: null,
291683
- createdAt: input.createdAt
291814
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
291684
291815
  });
291685
291816
  return;
291686
291817
  }
@@ -291914,7 +292045,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
291914
292045
  summary: "Agent turn blocked",
291915
292046
  detail: workspaceError,
291916
292047
  turnId: null,
291917
- createdAt: event.payload.createdAt
292048
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
291918
292049
  });
291919
292050
  return;
291920
292051
  }
@@ -293005,7 +293136,10 @@ const CheckpointReactorLive = effect(CheckpointReactor, gen(function* () {
293005
293136
  tone: "error",
293006
293137
  kind: "checkpoint.capture.failed",
293007
293138
  summary: "Checkpoint capture failed",
293008
- payload: { detail: input.detail },
293139
+ payload: {
293140
+ detail: input.detail,
293141
+ ...input.blocksTurnStart ? { blocksTurnStart: true } : {}
293142
+ },
293009
293143
  turnId: input.turnId,
293010
293144
  createdAt: input.createdAt
293011
293145
  },
@@ -293291,7 +293425,13 @@ const CheckpointReactorLive = effect(CheckpointReactor, gen(function* () {
293291
293425
  yield* checkpointStore.captureCheckpoint({
293292
293426
  cwd: checkpointCwd,
293293
293427
  checkpointRef: baselineCheckpointRef
293294
- });
293428
+ }).pipe(catch_((error) => appendCaptureFailureActivity({
293429
+ threadId,
293430
+ turnId: null,
293431
+ detail: error.message,
293432
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
293433
+ blocksTurnStart: true
293434
+ }).pipe(catch_(() => void_$1))));
293295
293435
  yield* receiptBus.publish({
293296
293436
  type: "checkpoint.baseline.captured",
293297
293437
  threadId,
@@ -297651,7 +297791,7 @@ function resolveBuildCommitFromEnv(env) {
297651
297791
  * environment descriptor down instead of reporting an honest "unknown".
297652
297792
  */
297653
297793
  function readBakedBuildCommit() {
297654
- return "63701057dccde6ea0a120ebb0a1e1ae8c461b056";
297794
+ return "589c5f6a52f5cd82c598399da0ece13acd43d50d";
297655
297795
  }
297656
297796
  async function resolveServerBuildCommit(input) {
297657
297797
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -322247,7 +322387,7 @@ const connectLoginCommand = make$34("login", {
322247
322387
  })).json();
322248
322388
  },
322249
322389
  openBrowser: async (url) => {
322250
- const open = (await import("./open-yHTUkd2Y.mjs")).default;
322390
+ const open = (await import("./open-2-reBrpv.mjs")).default;
322251
322391
  await open(url);
322252
322392
  return true;
322253
322393
  },