@cmmd-center/forge 0.13.14 → 0.13.16

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 CHANGED
@@ -12,7 +12,7 @@ const require_Cache = require("./Cache-BmGc_2BZ.cjs");
12
12
  const require_SqlError = require("./SqlError-g4pLzNWR.cjs");
13
13
  const require_Mime = require("./Mime-Bilnh8zE.cjs");
14
14
  const require_esm = require("./esm-BZhXwX5p.cjs");
15
- const require_runtimeControlAuthority = require("./runtimeControlAuthority-BkVB0uAA.cjs");
15
+ const require_runtimeControlAuthority = require("./runtimeControlAuthority-DCitZFTk.cjs");
16
16
  const require_PTY = require("./PTY-B4ZctbwQ.cjs");
17
17
  let node_child_process = require("node:child_process");
18
18
  node_child_process = require_chunk.__toESM(node_child_process);
@@ -27208,6 +27208,44 @@ function encodeCmmdRuntimeWorktreeLaunchActivityPayload(kind, payload) {
27208
27208
  function isCmmdRuntimeWorktreeLaunchActivityKind(kind) {
27209
27209
  return WORKTREE_LAUNCH_ACTIVITY_KINDS.has(kind);
27210
27210
  }
27211
+ /**
27212
+ * A plan update rides the `tool_call` event kind because the forge-context
27213
+ * contract has no plan event kind, the same trade the launch rows make. The
27214
+ * steps are carried verbatim under a versioned marker so a cloud client can
27215
+ * rebuild the composer plan banner from the durable replica. (#3319)
27216
+ */
27217
+ const CMMD_RUNTIME_PLAN_ACTIVITY_KIND = "turn.plan.updated";
27218
+ function encodeCmmdRuntimePlanActivityPayload(kind, payload) {
27219
+ return {
27220
+ ...isRecord$23(payload) ? payload : { sourcePayload: payload },
27221
+ forgePlanActivity: {
27222
+ schemaVersion: 1,
27223
+ source: "native",
27224
+ kind
27225
+ }
27226
+ };
27227
+ }
27228
+ function decodeCmmdRuntimePlanActivity(payload) {
27229
+ if (!isRecord$23(payload)) return null;
27230
+ const metadata = payload["forgePlanActivity"];
27231
+ if (!isRecord$23(metadata)) return null;
27232
+ if (metadata["schemaVersion"] !== 1 || metadata["source"] !== "native" || metadata["kind"] !== "turn.plan.updated") return null;
27233
+ const rawPlan = payload["plan"];
27234
+ return {
27235
+ kind: CMMD_RUNTIME_PLAN_ACTIVITY_KIND,
27236
+ plan: Array.isArray(rawPlan) ? rawPlan.flatMap((entry) => {
27237
+ if (!isRecord$23(entry)) return [];
27238
+ const step = entry["step"];
27239
+ const status = entry["status"];
27240
+ if (typeof step !== "string" || step.trim().length === 0) return [];
27241
+ if (status !== "pending" && status !== "inProgress" && status !== "completed") return [];
27242
+ return [{
27243
+ step,
27244
+ status
27245
+ }];
27246
+ }) : []
27247
+ };
27248
+ }
27211
27249
  //#endregion
27212
27250
  //#region ../../packages/contracts/src/cmmdProjectPatch.ts
27213
27251
  const CMMD_PROJECT_PATCH_FIELDS = {
@@ -66380,7 +66418,7 @@ function normalizeNumberish(value) {
66380
66418
  }
66381
66419
  //#endregion
66382
66420
  //#region package.json
66383
- var version$1 = "0.13.14";
66421
+ var version$1 = "0.13.16";
66384
66422
  //#endregion
66385
66423
  //#region src/sentry.ts
66386
66424
  const SERVER_APP_NAME = "forge-server";
@@ -67260,6 +67298,121 @@ function verifyCmmdForgeCloudOwnerAssertion(token, publicKeyPem, expectedKeyId,
67260
67298
  };
67261
67299
  }
67262
67300
  //#endregion
67301
+ //#region src/observability/Attributes.ts
67302
+ function isPlainObject$1(value) {
67303
+ return typeof value === "object" && value !== null && !Array.isArray(value);
67304
+ }
67305
+ function markSeen(value, seen) {
67306
+ if (seen.has(value)) return true;
67307
+ seen.add(value);
67308
+ return false;
67309
+ }
67310
+ function normalizeJsonValue(value, seen = /* @__PURE__ */ new WeakSet()) {
67311
+ if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value ?? null;
67312
+ if (typeof value === "bigint") return value.toString();
67313
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
67314
+ if (value instanceof Error) return {
67315
+ name: value.name,
67316
+ message: value.message,
67317
+ ...value.stack ? { stack: value.stack } : {}
67318
+ };
67319
+ if (Array.isArray(value)) {
67320
+ if (markSeen(value, seen)) return "[Circular]";
67321
+ return value.map((entry) => normalizeJsonValue(entry, seen));
67322
+ }
67323
+ if (value instanceof Map) {
67324
+ if (markSeen(value, seen)) return "[Circular]";
67325
+ return Object.fromEntries(Array.from(value.entries(), ([key, entryValue]) => [String(key), normalizeJsonValue(entryValue, seen)]));
67326
+ }
67327
+ if (value instanceof Set) {
67328
+ if (markSeen(value, seen)) return "[Circular]";
67329
+ return Array.from(value.values(), (entry) => normalizeJsonValue(entry, seen));
67330
+ }
67331
+ if (!isPlainObject$1(value)) return String(value);
67332
+ if (markSeen(value, seen)) return "[Circular]";
67333
+ return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue, seen)]));
67334
+ }
67335
+ function compactTraceAttributes(attributes) {
67336
+ return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => [key, normalizeJsonValue(value)]));
67337
+ }
67338
+ function compactMetricAttributes(attributes) {
67339
+ return Object.fromEntries(Object.entries(attributes).flatMap(([key, value]) => {
67340
+ if (value === void 0 || value === null) return [];
67341
+ if (typeof value === "string") return [[key, value]];
67342
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return [[key, String(value)]];
67343
+ return [];
67344
+ }));
67345
+ }
67346
+ function outcomeFromExit(exit) {
67347
+ if (require_Schema$1.isSuccess(exit)) return "success";
67348
+ return require_Schema$1.hasInterruptsOnly(exit.cause) ? "interrupt" : "failure";
67349
+ }
67350
+ function normalizeModelMetricLabel(model) {
67351
+ const normalized = model?.trim().toLowerCase();
67352
+ if (!normalized) return;
67353
+ if (normalized.includes("gpt")) return "gpt";
67354
+ if (normalized.includes("claude")) return "claude";
67355
+ if (normalized.includes("gemini")) return "gemini";
67356
+ return "other";
67357
+ }
67358
+ //#endregion
67359
+ //#region src/observability/Metrics.ts
67360
+ const rpcRequestsTotal = require_Schema$1.counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server." });
67361
+ const rpcRequestDuration = require_Schema$1.timer("t3_rpc_request_duration", { description: "RPC request handling duration." });
67362
+ const orchestrationCommandsTotal = require_Schema$1.counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched." });
67363
+ const orchestrationCommandDuration = require_Schema$1.timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration." });
67364
+ const orchestrationCommandAckDuration = require_Schema$1.timer("t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command." });
67365
+ const orchestrationEventsProcessedTotal = require_Schema$1.counter("t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors." });
67366
+ const providerSessionsTotal = require_Schema$1.counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations." });
67367
+ const providerTurnsTotal = require_Schema$1.counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations." });
67368
+ const providerTurnDuration = require_Schema$1.timer("t3_provider_turn_duration", { description: "Provider turn request duration." });
67369
+ const providerRuntimeEventsTotal = require_Schema$1.counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed." });
67370
+ const gitCommandsTotal = require_Schema$1.counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime." });
67371
+ const gitCommandDuration = require_Schema$1.timer("t3_git_command_duration", { description: "Git command execution duration." });
67372
+ const terminalSessionsTotal = require_Schema$1.counter("t3_terminal_sessions_total", { description: "Total terminal sessions started." });
67373
+ const browserSessionsReapedTotal = require_Schema$1.counter("browser_sessions_reaped_total", { description: "Browser sessions and Chromium processes reclaimed by the orphan reaper, by reason (orphan | ttl | stale_profile)." });
67374
+ const terminalRestartsTotal = require_Schema$1.counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled." });
67375
+ const runtimeReplicationOutboxDepth = require_Schema$1.gauge("t3_runtime_replication_outbox_depth", { description: "Current runtime replication outbox items by durable state." });
67376
+ const runtimeReplicationOutboxOldestEventAge = require_Schema$1.gauge("t3_runtime_replication_outbox_oldest_event_age_seconds", { description: "Age in seconds of the oldest undelivered runtime replication payload." });
67377
+ const runtimeReplicationPublicationAttemptsTotal = require_Schema$1.counter("t3_runtime_replication_publication_attempts_total", { description: "Total canonical runtime replication publication attempts." });
67378
+ const runtimeReplicationPublicationRetriesTotal = require_Schema$1.counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
67379
+ const runtimeReplicationPublicationLatency = require_Schema$1.timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
67380
+ const cmmdCredentialRefreshTotal = require_Schema$1.counter("t3_cmmd_credential_refresh_total", { description: "CMMD JWT refresh attempts for a Forge session, by rung (self-refresh | grant-redemption) and outcome (attempted | succeeded)." });
67381
+ const cmmdRotationFenceAppliedTotal = require_Schema$1.counter("t3_cmmd_rotation_fence_applied_total", { description: "Sessions Forge refused to rotate CMMD credentials for, because the rotation fence applied." });
67382
+ const cmmdCredentialRowDeletedTotal = require_Schema$1.counter("t3_cmmd_credential_row_deleted_total", { description: "command_access_tokens rows actually deleted by clearUnrecoverableCmmdAuthForSession, by reason." });
67383
+ const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
67384
+ const increment = (metric, attributes, amount = 1) => require_Schema$1.update(require_Schema$1.withAttributes(metric, metricAttributes(attributes)), amount);
67385
+ const withMetricsImpl = (effect, options) => require_Schema$1.gen(function* () {
67386
+ const startedAt = Date.now();
67387
+ const exit$2 = yield* require_Schema$1.exit(effect);
67388
+ const duration = require_Schema$1.millis(Math.max(0, Date.now() - startedAt));
67389
+ const baseAttributes = typeof options.attributes === "function" ? options.attributes() : options.attributes ?? {};
67390
+ if (options.timer) yield* require_Schema$1.update(require_Schema$1.withAttributes(options.timer, metricAttributes(baseAttributes)), duration);
67391
+ if (options.counter) {
67392
+ const outcome = outcomeFromExit(exit$2);
67393
+ yield* require_Schema$1.update(require_Schema$1.withAttributes(options.counter, metricAttributes({
67394
+ ...baseAttributes,
67395
+ outcome,
67396
+ ...options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}
67397
+ })), 1);
67398
+ }
67399
+ if (require_Schema$1.isSuccess(exit$2)) return exit$2.value;
67400
+ return yield* require_Schema$1.failCause(exit$2.cause);
67401
+ });
67402
+ const withMetrics = require_Schema$1.dual(2, withMetricsImpl);
67403
+ const providerMetricAttributes = (provider, extra) => compactMetricAttributes({
67404
+ provider,
67405
+ ...extra
67406
+ });
67407
+ const providerTurnMetricAttributes = (input) => {
67408
+ const modelFamily = normalizeModelMetricLabel(input.model);
67409
+ return compactMetricAttributes({
67410
+ provider: input.provider,
67411
+ ...modelFamily ? { modelFamily } : {},
67412
+ ...input.extra
67413
+ });
67414
+ };
67415
+ //#endregion
67263
67416
  //#region src/persistence/Errors.ts
67264
67417
  var PersistenceSqlError = class extends require_Schema$1.TaggedErrorClass()("PersistenceSqlError", {
67265
67418
  operation: require_Schema$1.String,
@@ -67950,8 +68103,14 @@ const scheduleExpiredForgeCloudOwnerCredentialMaintenance = require_Schema$1.sle
67950
68103
  *
67951
68104
  * Clearing every active owner row unconditionally (the pre-CMMD-1871 behaviour)
67952
68105
  * turned a single expired tab into a sign-out on every browser and device.
68106
+ *
68107
+ * Destroys a refresh token good for months, so every call is logged with a
68108
+ * `reason` naming the caller's own decision (never the credential itself) and
68109
+ * the actual row count deleted, at info level so it shows up without raising
68110
+ * the server's log level. `reason` is optional only so a caller that has not
68111
+ * been updated yet still compiles; every current caller passes one.
67953
68112
  */
67954
- const clearUnrecoverableCmmdAuthForSession = (sessionId) => require_Schema$1.gen(function* () {
68113
+ const clearUnrecoverableCmmdAuthForSession = (sessionId, reason) => require_Schema$1.gen(function* () {
67955
68114
  const sql = yield* require_SqlError.SqlClient;
67956
68115
  const rows = yield* sql`
67957
68116
  DELETE FROM command_access_tokens
@@ -67976,6 +68135,14 @@ const clearUnrecoverableCmmdAuthForSession = (sessionId) => require_Schema$1.gen
67976
68135
  `.pipe(require_Schema$1.mapError(toPersistenceSqlError("CommandAccessTokenStore.clearUnrecoverableCmmdAuth:query")));
67977
68136
  forgetCommandAccessTokenCachesForSession(sessionId);
67978
68137
  for (const row of rows) forgetCommandAccessTokenCachesForSession(row.sessionId);
68138
+ if (rows.length > 0) {
68139
+ yield* require_Schema$1.logInfo("CMMD credential row deleted.", {
68140
+ sessionId,
68141
+ reason: reason ?? "unspecified",
68142
+ rowsDeleted: rows.length
68143
+ });
68144
+ yield* require_Schema$1.update(require_Schema$1.withAttributes(cmmdCredentialRowDeletedTotal, metricAttributes({ reason: reason ?? "unspecified" })), rows.length);
68145
+ }
67979
68146
  });
67980
68147
  /**
67981
68148
  * Retire refresh credentials copied into historical desktop bearer sessions.
@@ -68031,6 +68198,23 @@ const isDesktopBearerAuthSession = (sessionId) => require_Schema$1.gen(function*
68031
68198
  `.pipe(require_Schema$1.mapError(toPersistenceSqlError("CommandAccessTokenStore.isDesktopBearerSession:query")));
68032
68199
  return rows[0]?.method === "bearer-session-token" && rows[0].clientDeviceType === "desktop";
68033
68200
  });
68201
+ /**
68202
+ * The device type a session's bearer bootstrap declared, for log lines only.
68203
+ *
68204
+ * Mirrors `isDesktopBearerAuthSession`'s read but returns the raw column
68205
+ * instead of collapsing it into a rotation verdict — a log line wants to say
68206
+ * what made the request, not whether Forge may rotate for it. Never fails:
68207
+ * an unreadable or missing row logs as `"unknown"` rather than losing the
68208
+ * decision-point log line it is attached to.
68209
+ */
68210
+ const readClientDeviceTypeForSession = (sessionId) => require_Schema$1.gen(function* () {
68211
+ return (yield* (yield* require_SqlError.SqlClient)`
68212
+ SELECT client_device_type AS "clientDeviceType"
68213
+ FROM auth_sessions
68214
+ WHERE session_id = ${sessionId}
68215
+ LIMIT 1
68216
+ `.pipe(require_Schema$1.mapError(toPersistenceSqlError("CommandAccessTokenStore.readClientDeviceType:query"))))[0]?.clientDeviceType ?? "unknown";
68217
+ }).pipe(require_Schema$1.orElseSucceed(() => "unknown"));
68034
68218
  //#endregion
68035
68219
  //#region src/auth/http/oauthCookies.ts
68036
68220
  const OAUTH_STATE_COOKIE = "forge_oauth_state";
@@ -68233,9 +68417,21 @@ const authLogoutRouteLayer = add$1("POST", "/api/auth/logout", require_Schema$1.
68233
68417
  onSuccess: (session) => session.sessionId
68234
68418
  }));
68235
68419
  if (sessionId) {
68420
+ const deviceType = yield* readClientDeviceTypeForSession(sessionId);
68421
+ yield* require_Schema$1.logInfo("Auth logout.", {
68422
+ sessionId,
68423
+ deviceType,
68424
+ outcome: "session-cleared",
68425
+ reason: cloudShell ? "forge-cloud-shell-session" : "local-session"
68426
+ });
68236
68427
  if (!cloudShell) yield* sessions.revoke(sessionId).pipe(require_Schema$1.ignore({ log: false }));
68237
68428
  yield* clearCommandAccessTokenForSession(sessionId).pipe(require_Schema$1.ignore({ log: false }));
68238
- }
68429
+ } else yield* require_Schema$1.logInfo("Auth logout.", {
68430
+ sessionId: null,
68431
+ deviceType: "unknown",
68432
+ outcome: "no-op",
68433
+ reason: "no authenticated session presented"
68434
+ });
68239
68435
  const secure = shouldUseSecureCookie(forgePublicUrlForRequest(request));
68240
68436
  let response = yield* require_HttpServer.jsonUnsafe({ loggedOut: true }, { status: 200 }).pipe(require_HttpServer.setCookie(sessions.cookieName, "", {
68241
68437
  expires: /* @__PURE__ */ new Date(0),
@@ -68834,16 +69030,32 @@ const authCommandBearerBootstrapRouteLayer = add$1("POST", "/api/auth/bootstrap/
68834
69030
  if (limited) return limited;
68835
69031
  const serverAuth = yield* ServerAuth;
68836
69032
  const sessions = yield* SessionCredentialService;
68837
- if (serverAuth.isForgeCloudShell?.() === true) return yield* new AuthError({
68838
- message: "Forge Cloud accepts owner assertions from CMMD only.",
68839
- status: 403
68840
- });
69033
+ if (serverAuth.isForgeCloudShell?.() === true) {
69034
+ yield* require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
69035
+ deviceType: "unknown",
69036
+ outcome: "failed",
69037
+ reason: "forge-cloud-shell-rejects-non-cmmd-bootstrap"
69038
+ });
69039
+ return yield* new AuthError({
69040
+ message: "Forge Cloud accepts owner assertions from CMMD only.",
69041
+ status: 403
69042
+ });
69043
+ }
68841
69044
  const payload = yield* require_HttpServer.schemaBodyJson(CommandBootstrapInput).pipe(require_Schema$1.mapError((cause) => new AuthError({
68842
69045
  message: "Invalid CMMD OAuth bearer bootstrap payload.",
68843
69046
  status: 400,
68844
69047
  cause
69048
+ })), require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
69049
+ deviceType: "unknown",
69050
+ outcome: "failed",
69051
+ reason: "invalid-payload"
69052
+ })));
69053
+ const deviceType = payload.clientDeviceType ?? "desktop";
69054
+ const validated = yield* validateCommandBootstrapPayload(payload).pipe(require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
69055
+ deviceType,
69056
+ outcome: "failed",
69057
+ reason: "invalid-command-bootstrap-payload"
68845
69058
  })));
68846
- const validated = yield* validateCommandBootstrapPayload(payload);
68847
69059
  const sessionSubject = resolveCommandOAuthSubject(validated.subjectUserId);
68848
69060
  const issued = yield* sessions.issue({
68849
69061
  method: "bearer-session-token",
@@ -68856,20 +69068,34 @@ const authCommandBearerBootstrapRouteLayer = add$1("POST", "/api/auth/bootstrap/
68856
69068
  }).pipe(require_Schema$1.mapError((cause) => new AuthError({
68857
69069
  message: "Failed to issue authenticated bearer session.",
68858
69070
  cause
69071
+ })), require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
69072
+ deviceType,
69073
+ outcome: "failed",
69074
+ reason: "session-issuance-failed"
68859
69075
  })));
68860
69076
  const cmmdIdentity = yield* persistCommandBootstrapSessionArtifacts({
68861
69077
  sessionId: issued.sessionId,
68862
69078
  payload,
68863
69079
  cmmdIdentity: validated.cmmdIdentity
68864
69080
  });
68865
- if ((payload.clientDeviceType ?? "desktop") === "desktop" && !payload.refreshToken?.trim()) yield* clearDesktopBearerRefreshTokensForSubject({
69081
+ if (deviceType === "desktop" && !payload.refreshToken?.trim()) yield* clearDesktopBearerRefreshTokensForSubject({
68866
69082
  subject: sessionSubject,
68867
69083
  clientUserAgent: request.headers["user-agent"]
68868
69084
  }).pipe(require_Schema$1.mapError((cause) => new AuthError({
68869
69085
  message: "Failed to retire legacy desktop refresh credentials.",
68870
69086
  status: 500,
68871
69087
  cause
69088
+ })), require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
69089
+ sessionId: issued.sessionId,
69090
+ deviceType,
69091
+ outcome: "failed",
69092
+ reason: "legacy-desktop-refresh-retirement-failed"
68872
69093
  })));
69094
+ yield* require_Schema$1.logInfo("CMMD bearer bootstrap succeeded.", {
69095
+ sessionId: issued.sessionId,
69096
+ deviceType,
69097
+ outcome: "succeeded"
69098
+ });
68873
69099
  return require_HttpServer.jsonUnsafe({
68874
69100
  authenticated: true,
68875
69101
  role: issued.role,
@@ -70177,64 +70403,6 @@ function parseAttachmentIdFromRelativePath(relativePath) {
70177
70403
  return id.length > 0 && !id.includes(".") ? id : null;
70178
70404
  }
70179
70405
  //#endregion
70180
- //#region src/observability/Attributes.ts
70181
- function isPlainObject$1(value) {
70182
- return typeof value === "object" && value !== null && !Array.isArray(value);
70183
- }
70184
- function markSeen(value, seen) {
70185
- if (seen.has(value)) return true;
70186
- seen.add(value);
70187
- return false;
70188
- }
70189
- function normalizeJsonValue(value, seen = /* @__PURE__ */ new WeakSet()) {
70190
- if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value ?? null;
70191
- if (typeof value === "bigint") return value.toString();
70192
- if (value instanceof Date) return Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
70193
- if (value instanceof Error) return {
70194
- name: value.name,
70195
- message: value.message,
70196
- ...value.stack ? { stack: value.stack } : {}
70197
- };
70198
- if (Array.isArray(value)) {
70199
- if (markSeen(value, seen)) return "[Circular]";
70200
- return value.map((entry) => normalizeJsonValue(entry, seen));
70201
- }
70202
- if (value instanceof Map) {
70203
- if (markSeen(value, seen)) return "[Circular]";
70204
- return Object.fromEntries(Array.from(value.entries(), ([key, entryValue]) => [String(key), normalizeJsonValue(entryValue, seen)]));
70205
- }
70206
- if (value instanceof Set) {
70207
- if (markSeen(value, seen)) return "[Circular]";
70208
- return Array.from(value.values(), (entry) => normalizeJsonValue(entry, seen));
70209
- }
70210
- if (!isPlainObject$1(value)) return String(value);
70211
- if (markSeen(value, seen)) return "[Circular]";
70212
- return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue, seen)]));
70213
- }
70214
- function compactTraceAttributes(attributes) {
70215
- return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => [key, normalizeJsonValue(value)]));
70216
- }
70217
- function compactMetricAttributes(attributes) {
70218
- return Object.fromEntries(Object.entries(attributes).flatMap(([key, value]) => {
70219
- if (value === void 0 || value === null) return [];
70220
- if (typeof value === "string") return [[key, value]];
70221
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return [[key, String(value)]];
70222
- return [];
70223
- }));
70224
- }
70225
- function outcomeFromExit(exit) {
70226
- if (require_Schema$1.isSuccess(exit)) return "success";
70227
- return require_Schema$1.hasInterruptsOnly(exit.cause) ? "interrupt" : "failure";
70228
- }
70229
- function normalizeModelMetricLabel(model) {
70230
- const normalized = model?.trim().toLowerCase();
70231
- if (!normalized) return;
70232
- if (normalized.includes("gpt")) return "gpt";
70233
- if (normalized.includes("claude")) return "claude";
70234
- if (normalized.includes("gemini")) return "gemini";
70235
- return "other";
70236
- }
70237
- //#endregion
70238
70406
  //#region src/observability/TraceRecord.ts
70239
70407
  function formatTraceExit(exit) {
70240
70408
  if (require_Schema$1.isSuccess(exit)) return { _tag: "Success" };
@@ -71297,6 +71465,22 @@ async function readOAuthErrorCode(response) {
71297
71465
  //#endregion
71298
71466
  //#region src/command/cmmdRotationFence.ts
71299
71467
  /**
71468
+ * Log and count a fence application, without changing the verdict it reports.
71469
+ *
71470
+ * The two facts named in the log line are exactly the ones the predicate
71471
+ * above the caller already computed — whether this session ever deposited a
71472
+ * refresh token of its own, and (only when that was false) whether the
71473
+ * session row reads as a desktop bearer session. Neither fact nor the log
71474
+ * line changes what the caller returns.
71475
+ */
71476
+ function logRotationFenceApplied(input) {
71477
+ return require_Schema$1.logInfo("CMMD rotation fence applied: session may not rotate its own credential.", {
71478
+ sessionId: input.sessionId,
71479
+ hasStoredRefreshToken: input.hasStoredRefreshToken,
71480
+ isDesktopBearerSession: input.isDesktopBearerSession
71481
+ }).pipe(require_Schema$1.andThen(() => require_Schema$1.update(require_Schema$1.withAttributes(cmmdRotationFenceAppliedTotal, metricAttributes({})), 1)));
71482
+ }
71483
+ /**
71300
71484
  * True when Forge must not rotate this session's CMMD credentials.
71301
71485
  *
71302
71486
  * Fails closed: if the session row cannot be read, assume the fence applies.
@@ -71304,8 +71488,22 @@ async function readOAuthErrorCode(response) {
71304
71488
  * reconnect.
71305
71489
  */
71306
71490
  function isRotationFencedSession(sessionId) {
71307
- if (readCommandRefreshTokenForSession(sessionId)) return require_Schema$1.succeed(false);
71308
- return isDesktopBearerAuthSession(sessionId).pipe(require_Schema$1.orElseSucceed(() => true));
71491
+ const hasStoredRefreshToken = readCommandRefreshTokenForSession(sessionId) !== null;
71492
+ if (hasStoredRefreshToken) return require_Schema$1.succeed(false);
71493
+ return isDesktopBearerAuthSession(sessionId).pipe(require_Schema$1.match({
71494
+ onFailure: () => ({
71495
+ fenced: true,
71496
+ isDesktopBearerSession: null
71497
+ }),
71498
+ onSuccess: (isDesktopBearerSession) => ({
71499
+ fenced: isDesktopBearerSession,
71500
+ isDesktopBearerSession
71501
+ })
71502
+ }), require_Schema$1.tap(({ fenced, isDesktopBearerSession }) => fenced ? logRotationFenceApplied({
71503
+ sessionId,
71504
+ hasStoredRefreshToken,
71505
+ isDesktopBearerSession
71506
+ }) : require_Schema$1.void_), require_Schema$1.map(({ fenced }) => fenced));
71309
71507
  }
71310
71508
  //#endregion
71311
71509
  //#region src/cmmdConnection/cmmdCredentialRejection.ts
@@ -71342,7 +71540,7 @@ function forgetCmmdCredentialRejection(sessionId) {
71342
71540
  */
71343
71541
  const clearCmmdAuthAfterCmmdRejection = (sessionId) => require_Schema$1.gen(function* () {
71344
71542
  if (!hasCmmdCredentialRejection(sessionId)) return;
71345
- yield* clearUnrecoverableCmmdAuthForSession(sessionId);
71543
+ yield* clearUnrecoverableCmmdAuthForSession(sessionId, "cmmd-rejected-credential");
71346
71544
  forgetCmmdCredentialRejection(sessionId);
71347
71545
  });
71348
71546
  //#endregion
@@ -71353,6 +71551,23 @@ function usableCmmdUserJwt(token) {
71353
71551
  function storeResolvedJwt(sessionId, accessToken, refreshToken) {
71354
71552
  return setCommandJwtForSession(sessionId, accessToken, refreshToken ? { refreshToken } : {}).pipe(require_Schema$1.ignore({ log: true }));
71355
71553
  }
71554
+ function logCmmdJwtRung(context, rung, outcome, reason) {
71555
+ return (outcome === "failed" ? require_Schema$1.logWarning : require_Schema$1.logInfo)("CMMD JWT resolver rung.", {
71556
+ sessionId: context.sessionId,
71557
+ subject: context.subject,
71558
+ deviceType: context.deviceType,
71559
+ caller: context.caller,
71560
+ rung,
71561
+ outcome,
71562
+ reason
71563
+ });
71564
+ }
71565
+ function recordCmmdCredentialRefreshMetric(rung, outcome) {
71566
+ return require_Schema$1.update(require_Schema$1.withAttributes(cmmdCredentialRefreshTotal, metricAttributes({
71567
+ rung,
71568
+ outcome
71569
+ })), 1);
71570
+ }
71356
71571
  const inFlightRefreshes = /* @__PURE__ */ new Map();
71357
71572
  function refreshOnceForSession(sessionId, refreshToken) {
71358
71573
  return require_Schema$1.promise(() => {
@@ -71374,15 +71589,21 @@ function refreshOnceForSession(sessionId, refreshToken) {
71374
71589
  * sibling session's token silently invalidates that sibling's stored copy and
71375
71590
  * signs it out.
71376
71591
  */
71377
- function trySelfRefresh(sessionId, logPrefix) {
71592
+ function trySelfRefresh(sessionId, context) {
71378
71593
  const currentRefreshToken = readCommandRefreshTokenForSession(sessionId);
71379
- if (!currentRefreshToken) return require_Schema$1.succeed(null);
71594
+ if (!currentRefreshToken) return logCmmdJwtRung(context, "self-refresh", "skipped", "no refresh token stored for this session").pipe(require_Schema$1.as(null));
71380
71595
  return require_Schema$1.gen(function* () {
71596
+ yield* logCmmdJwtRung(context, "self-refresh", "attempted", "rotating this session's own stored refresh token");
71597
+ yield* recordCmmdCredentialRefreshMetric("self-refresh", "attempted");
71381
71598
  const refreshed = yield* refreshOnceForSession(sessionId, currentRefreshToken);
71382
71599
  const accessToken = usableCmmdUserJwt(refreshed?.accessToken);
71383
- if (!accessToken) return null;
71600
+ if (!accessToken) {
71601
+ yield* logCmmdJwtRung(context, "self-refresh", "failed", "CMMD refresh did not return a usable user access JWT");
71602
+ return null;
71603
+ }
71384
71604
  yield* storeResolvedJwt(sessionId, accessToken, refreshed?.refreshToken);
71385
- console.log(`[${logPrefix}] CMMD JWT refreshed for session ${sessionId}`);
71605
+ yield* recordCmmdCredentialRefreshMetric("self-refresh", "succeeded");
71606
+ yield* logCmmdJwtRung(context, "self-refresh", "succeeded", "CMMD rotated the stored refresh token");
71386
71607
  return accessToken;
71387
71608
  });
71388
71609
  }
@@ -71399,16 +71620,22 @@ function trySelfRefresh(sessionId, logPrefix) {
71399
71620
  * CMMD owns its lifetime, and a transient network failure must not throw away
71400
71621
  * the one credential that can still recover the session.
71401
71622
  */
71402
- function tryGrantRedemption(sessionId, logPrefix) {
71623
+ function tryGrantRedemption(sessionId, context) {
71403
71624
  const grantToken = readCommandSessionGrantForSession(sessionId);
71404
- if (!grantToken) return require_Schema$1.succeed(null);
71625
+ if (!grantToken) return logCmmdJwtRung(context, "grant-redemption", "skipped", "no session grant stored for this session").pipe(require_Schema$1.as(null));
71405
71626
  return require_Schema$1.gen(function* () {
71627
+ yield* logCmmdJwtRung(context, "grant-redemption", "attempted", "redeeming this session's durable CMMD session grant");
71628
+ yield* recordCmmdCredentialRefreshMetric("grant-redemption", "attempted");
71406
71629
  const redeemed = yield* require_Schema$1.promise(() => redeemCmmdSessionGrant(grantToken)).pipe(require_Schema$1.orElseSucceed(() => null));
71407
71630
  const accessToken = usableCmmdUserJwt(redeemed?.accessToken);
71408
- if (!accessToken) return null;
71631
+ if (!accessToken) {
71632
+ yield* logCmmdJwtRung(context, "grant-redemption", "failed", "session grant redemption did not return a usable user access JWT");
71633
+ return null;
71634
+ }
71409
71635
  if (redeemed?.mcpToken) yield* setCommandAccessTokenForSession(sessionId, redeemed.mcpToken).pipe(require_Schema$1.ignore({ log: true }));
71410
71636
  yield* storeResolvedJwt(sessionId, accessToken, redeemed?.refreshToken);
71411
- console.log(`[${logPrefix}] CMMD credentials recovered via session grant for ${sessionId}`);
71637
+ yield* recordCmmdCredentialRefreshMetric("grant-redemption", "succeeded");
71638
+ yield* logCmmdJwtRung(context, "grant-redemption", "succeeded", "session grant redeemed");
71412
71639
  return accessToken;
71413
71640
  });
71414
71641
  }
@@ -71419,23 +71646,28 @@ function tryGrantRedemption(sessionId, logPrefix) {
71419
71646
  * fallback for when that rotation is already gone. Neither touches another
71420
71647
  * session's credentials, so this ladder is safe to run from any path.
71421
71648
  */
71422
- function tryOwnCredentialRecovery(sessionId, logPrefix) {
71649
+ function tryOwnCredentialRecovery(sessionId, context) {
71423
71650
  return require_Schema$1.gen(function* () {
71424
- const refreshed = yield* trySelfRefresh(sessionId, logPrefix);
71651
+ const refreshed = yield* trySelfRefresh(sessionId, context);
71425
71652
  if (refreshed) return refreshed;
71426
- return yield* tryGrantRedemption(sessionId, logPrefix);
71653
+ return yield* tryGrantRedemption(sessionId, context);
71427
71654
  });
71428
71655
  }
71429
- function tryPromoteBootstrap(sessionId, subject, forceRefresh) {
71430
- if (subject !== "owner-bootstrap" && subject !== "desktop-bootstrap") return require_Schema$1.succeed(null);
71656
+ function tryPromoteBootstrap(sessionId, subject, forceRefresh, context) {
71657
+ if (subject !== "owner-bootstrap" && subject !== "desktop-bootstrap") return logCmmdJwtRung(context, "promote-bootstrap", "skipped", "subject is not a bootstrap subject").pipe(require_Schema$1.as(null));
71431
71658
  return require_Schema$1.gen(function* () {
71659
+ yield* logCmmdJwtRung(context, "promote-bootstrap", "attempted", "promoting the most recent owner CMMD auth to this bootstrap session");
71432
71660
  yield* promoteMostRecentOwnerCommandAuthToSession(sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to promote owner CMMD auth for REST JWT resolution.", {
71433
71661
  cause: error,
71434
71662
  sessionId,
71435
71663
  subject
71436
71664
  })));
71437
71665
  const promotedJwt = usableCmmdUserJwt(readCommandJwtForSession(sessionId));
71438
- if (!forceRefresh && promotedJwt && !isJwtExpiredForSession(sessionId)) return promotedJwt;
71666
+ if (!forceRefresh && promotedJwt && !isJwtExpiredForSession(sessionId)) {
71667
+ yield* logCmmdJwtRung(context, "promote-bootstrap", "succeeded", "promoted owner JWT is usable");
71668
+ return promotedJwt;
71669
+ }
71670
+ yield* logCmmdJwtRung(context, "promote-bootstrap", "failed", forceRefresh ? "forced refresh: a promoted JWT is never reused as-is" : "no promoted JWT available, or it is already expired");
71439
71671
  return null;
71440
71672
  });
71441
71673
  }
@@ -71448,17 +71680,50 @@ function tryPromoteBootstrap(sessionId, subject, forceRefresh) {
71448
71680
  * must stay private to the session it was issued to (see `trySelfRefresh`).
71449
71681
  * Copying it is what let one expired tab sign the user out everywhere.
71450
71682
  */
71451
- function trySubjectFallback(sessionId, subject, logPrefix) {
71683
+ function trySubjectFallback(sessionId, subject, context) {
71452
71684
  return require_Schema$1.gen(function* () {
71453
- const fallback = yield* readMostRecentJwtForSubject(subject).pipe(require_Schema$1.orElseSucceed(() => null));
71454
- const fallbackJwt = usableCmmdUserJwt(fallback?.jwt);
71455
- if (!fallbackJwt) return null;
71685
+ yield* logCmmdJwtRung(context, "subject-fallback", "attempted", "looking for a sibling session's still-valid JWT for this subject");
71686
+ const fallbackJwt = usableCmmdUserJwt((yield* readMostRecentJwtForSubject(subject).pipe(require_Schema$1.orElseSucceed(() => null)))?.jwt);
71687
+ if (!fallbackJwt) {
71688
+ yield* logCmmdJwtRung(context, "subject-fallback", "failed", "no sibling session holds a still-valid JWT for this subject");
71689
+ return null;
71690
+ }
71456
71691
  yield* storeResolvedJwt(sessionId, fallbackJwt, null);
71457
- console.log(`[${logPrefix}] JWT subject-fallback promoted for session ${sessionId} (owner: ${fallback?.sessionId})`);
71692
+ yield* logCmmdJwtRung(context, "subject-fallback", "succeeded", "borrowed a sibling session's still-valid JWT");
71458
71693
  return fallbackJwt;
71459
71694
  });
71460
71695
  }
71461
71696
  /**
71697
+ * The rungs below the rotation fence: forced own-credential recovery,
71698
+ * bootstrap promotion, subject fallback, then own-credential recovery again
71699
+ * as the last resort. Split out of `resolveCmmdJwtForSession` so each half of
71700
+ * the ladder stays independently readable (and independently testable) —
71701
+ * the fence check above is the one decision the split does not touch.
71702
+ */
71703
+ function runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context) {
71704
+ return require_Schema$1.gen(function* () {
71705
+ if (forceRefresh) {
71706
+ const forced = yield* tryOwnCredentialRecovery(sessionId, context);
71707
+ if (forced) return forced;
71708
+ }
71709
+ if (normalizedSubject) {
71710
+ const promoted = yield* tryPromoteBootstrap(sessionId, normalizedSubject, forceRefresh, context);
71711
+ if (promoted) return promoted;
71712
+ }
71713
+ if (!normalizedSubject) {
71714
+ yield* logCmmdJwtRung(context, "cache", "failed", "no subject to resolve and forced recovery already exhausted");
71715
+ return null;
71716
+ }
71717
+ if (!forceRefresh) {
71718
+ const fallback = yield* trySubjectFallback(sessionId, normalizedSubject, context);
71719
+ if (fallback) return fallback;
71720
+ }
71721
+ const recovered = yield* tryOwnCredentialRecovery(sessionId, context);
71722
+ if (!recovered) yield* logCmmdJwtRung(context, "cache", "failed", "recovery ladder exhausted: no rung produced a usable JWT");
71723
+ return recovered;
71724
+ });
71725
+ }
71726
+ /**
71462
71727
  * Resolve the CMMD REST JWT for a Forge session.
71463
71728
  *
71464
71729
  * Forge session auth is long-lived compared with the short-lived CMMD JWT.
@@ -71472,26 +71737,30 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
71472
71737
  const cached = usableCmmdUserJwt(readCommandJwtForSession(sessionId));
71473
71738
  if (cached && !isJwtExpiredForSession(sessionId)) {
71474
71739
  if (!forceRefresh) return require_Schema$1.succeed(cached);
71475
- if (staleAccessToken && cached !== staleAccessToken) return require_Schema$1.succeed(cached);
71740
+ if (staleAccessToken && cached !== staleAccessToken) return require_Schema$1.succeed(cached).pipe(require_Schema$1.tap(() => require_Schema$1.gen(function* () {
71741
+ const deviceType = yield* readClientDeviceTypeForSession(sessionId);
71742
+ yield* logCmmdJwtRung({
71743
+ sessionId,
71744
+ subject: subject?.trim() || null,
71745
+ deviceType,
71746
+ caller: logPrefix
71747
+ }, "cache", "skipped", "forced refresh skipped: cache already holds a token another caller rotated");
71748
+ })));
71476
71749
  }
71477
71750
  const normalizedSubject = subject?.trim() || null;
71478
71751
  if (!normalizedSubject && !forceRefresh) return require_Schema$1.succeed(null);
71479
71752
  return require_Schema$1.gen(function* () {
71480
- if (yield* isRotationFencedSession(sessionId)) return null;
71481
- if (forceRefresh) {
71482
- const forced = yield* tryOwnCredentialRecovery(sessionId, logPrefix);
71483
- if (forced) return forced;
71484
- }
71485
- if (normalizedSubject) {
71486
- const promoted = yield* tryPromoteBootstrap(sessionId, normalizedSubject, forceRefresh);
71487
- if (promoted) return promoted;
71488
- }
71489
- if (!normalizedSubject) return null;
71490
- if (!forceRefresh) {
71491
- const fallback = yield* trySubjectFallback(sessionId, normalizedSubject, logPrefix);
71492
- if (fallback) return fallback;
71753
+ const context = {
71754
+ sessionId,
71755
+ subject: normalizedSubject,
71756
+ deviceType: yield* readClientDeviceTypeForSession(sessionId),
71757
+ caller: logPrefix
71758
+ };
71759
+ if (yield* isRotationFencedSession(sessionId)) {
71760
+ yield* logCmmdJwtRung(context, "rotation-fence", "failed", "session is fenced from rotating CMMD credentials");
71761
+ return null;
71493
71762
  }
71494
- return yield* tryOwnCredentialRecovery(sessionId, logPrefix);
71763
+ return yield* runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context);
71495
71764
  });
71496
71765
  }
71497
71766
  //#endregion
@@ -72228,9 +72497,14 @@ function buildProxyErrorResponse(error) {
72228
72497
  * warning rather than failing the request if the clear itself errors — a
72229
72498
  * session that already has no usable credential must still get its 401,
72230
72499
  * not a 500 from a failed best-effort cleanup.
72500
+ *
72501
+ * Reached only after CMMD itself rejected the request with both the original
72502
+ * and (if retried) the refreshed JWT, so — unlike `respondCmmdConnectionRequired`
72503
+ * below — this always deletes the row. `clearUnrecoverableCmmdAuthForSession`
72504
+ * logs the row count and the reason itself; this only names the reason.
72231
72505
  */
72232
72506
  function clearCmmdAuthAfterFailure(sessionId) {
72233
- return clearUnrecoverableCmmdAuthForSession(sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear unrecoverable CMMD auth after CMMD auth failure.", {
72507
+ return clearUnrecoverableCmmdAuthForSession(sessionId, "cmmd-proxy-auth-error").pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear unrecoverable CMMD auth after CMMD auth failure.", {
72234
72508
  cause: error,
72235
72509
  sessionId
72236
72510
  })));
@@ -72251,7 +72525,7 @@ function clearCmmdAuthAfterFailure(sessionId) {
72251
72525
  * refused the credential, which `clearCmmdAuthAfterCmmdRejection` checks.
72252
72526
  */
72253
72527
  function respondCmmdConnectionRequired(sessionId) {
72254
- return clearCmmdAuthAfterCmmdRejection(sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear CMMD auth that CMMD itself rejected.", {
72528
+ return require_Schema$1.logInfo("CMMD proxy request answered CMMD_AUTH_REQUIRED.", { sessionId }).pipe(require_Schema$1.andThen(() => clearCmmdAuthAfterCmmdRejection(sessionId)), require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear CMMD auth that CMMD itself rejected.", {
72255
72529
  cause: error,
72256
72530
  sessionId
72257
72531
  })), require_Schema$1.as(buildProxyErrorResponse(new CmmdProxyAuthenticationRequiredError("CMMD connection required. Reconnect CMMD to continue."))));
@@ -95034,60 +95308,6 @@ const dispatchBootstrapTurnStart = (input, command) => require_Schema$1.gen(func
95034
95308
  */
95035
95309
  var OrchestrationEngineService = class extends require_Schema$1.Service()("forge/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {};
95036
95310
  //#endregion
95037
- //#region src/observability/Metrics.ts
95038
- const rpcRequestsTotal = require_Schema$1.counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server." });
95039
- const rpcRequestDuration = require_Schema$1.timer("t3_rpc_request_duration", { description: "RPC request handling duration." });
95040
- const orchestrationCommandsTotal = require_Schema$1.counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched." });
95041
- const orchestrationCommandDuration = require_Schema$1.timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration." });
95042
- const orchestrationCommandAckDuration = require_Schema$1.timer("t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command." });
95043
- const orchestrationEventsProcessedTotal = require_Schema$1.counter("t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors." });
95044
- const providerSessionsTotal = require_Schema$1.counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations." });
95045
- const providerTurnsTotal = require_Schema$1.counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations." });
95046
- const providerTurnDuration = require_Schema$1.timer("t3_provider_turn_duration", { description: "Provider turn request duration." });
95047
- const providerRuntimeEventsTotal = require_Schema$1.counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed." });
95048
- const gitCommandsTotal = require_Schema$1.counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime." });
95049
- const gitCommandDuration = require_Schema$1.timer("t3_git_command_duration", { description: "Git command execution duration." });
95050
- const terminalSessionsTotal = require_Schema$1.counter("t3_terminal_sessions_total", { description: "Total terminal sessions started." });
95051
- const browserSessionsReapedTotal = require_Schema$1.counter("browser_sessions_reaped_total", { description: "Browser sessions and Chromium processes reclaimed by the orphan reaper, by reason (orphan | ttl | stale_profile)." });
95052
- const terminalRestartsTotal = require_Schema$1.counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled." });
95053
- const runtimeReplicationOutboxDepth = require_Schema$1.gauge("t3_runtime_replication_outbox_depth", { description: "Current runtime replication outbox items by durable state." });
95054
- const runtimeReplicationOutboxOldestEventAge = require_Schema$1.gauge("t3_runtime_replication_outbox_oldest_event_age_seconds", { description: "Age in seconds of the oldest undelivered runtime replication payload." });
95055
- const runtimeReplicationPublicationAttemptsTotal = require_Schema$1.counter("t3_runtime_replication_publication_attempts_total", { description: "Total canonical runtime replication publication attempts." });
95056
- const runtimeReplicationPublicationRetriesTotal = require_Schema$1.counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
95057
- const runtimeReplicationPublicationLatency = require_Schema$1.timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
95058
- const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
95059
- const increment = (metric, attributes, amount = 1) => require_Schema$1.update(require_Schema$1.withAttributes(metric, metricAttributes(attributes)), amount);
95060
- const withMetricsImpl = (effect, options) => require_Schema$1.gen(function* () {
95061
- const startedAt = Date.now();
95062
- const exit$2 = yield* require_Schema$1.exit(effect);
95063
- const duration = require_Schema$1.millis(Math.max(0, Date.now() - startedAt));
95064
- const baseAttributes = typeof options.attributes === "function" ? options.attributes() : options.attributes ?? {};
95065
- if (options.timer) yield* require_Schema$1.update(require_Schema$1.withAttributes(options.timer, metricAttributes(baseAttributes)), duration);
95066
- if (options.counter) {
95067
- const outcome = outcomeFromExit(exit$2);
95068
- yield* require_Schema$1.update(require_Schema$1.withAttributes(options.counter, metricAttributes({
95069
- ...baseAttributes,
95070
- outcome,
95071
- ...options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}
95072
- })), 1);
95073
- }
95074
- if (require_Schema$1.isSuccess(exit$2)) return exit$2.value;
95075
- return yield* require_Schema$1.failCause(exit$2.cause);
95076
- });
95077
- const withMetrics = require_Schema$1.dual(2, withMetricsImpl);
95078
- const providerMetricAttributes = (provider, extra) => compactMetricAttributes({
95079
- provider,
95080
- ...extra
95081
- });
95082
- const providerTurnMetricAttributes = (input) => {
95083
- const modelFamily = normalizeModelMetricLabel(input.model);
95084
- return compactMetricAttributes({
95085
- provider: input.provider,
95086
- ...modelFamily ? { modelFamily } : {},
95087
- ...input.extra
95088
- });
95089
- };
95090
- //#endregion
95091
95311
  //#region src/observability/RpcInstrumentation.ts
95092
95312
  const annotateRpcSpan = (method, traceAttributes) => require_Schema$1.annotateCurrentSpan({
95093
95313
  "rpc.method": method,
@@ -96856,6 +97076,7 @@ const registerCloudRuntimeForReplicaHydration = require_Schema$1.gen(function* (
96856
97076
  credentialEpoch,
96857
97077
  activeTurnCount: 0,
96858
97078
  activeSessionCount: 0,
97079
+ acceptingTurns: false,
96859
97080
  ...forgeGitCommit !== void 0 ? { forgeGitCommit } : {},
96860
97081
  forgeServerVersion: installedForgeServerVersion
96861
97082
  }),
@@ -96892,6 +97113,128 @@ const hydrateCloudRuntimeReplica = require_Schema$1.gen(function* () {
96892
97113
  return yield* hydrateRuntimeReplica({ expectedRuntimeId: grant.runtimeId }, dependencies);
96893
97114
  });
96894
97115
  //#endregion
97116
+ //#region src/runtimeDrain/RuntimeDrainState.ts
97117
+ /**
97118
+ * The positive facts this process must observe before it can claim a turn.
97119
+ *
97120
+ * Every entry is required, so adding one here is the whole change: the
97121
+ * derivation below reads this list rather than naming fields, and the record
97122
+ * it indexes cannot be built with a member missing.
97123
+ */
97124
+ const RUNTIME_EXECUTION_PRECONDITIONS = ["orchestrationEngine", "providerLayer"];
97125
+ /**
97126
+ * The single definition of the accepting-turns fact.
97127
+ *
97128
+ * Exported so test stubs of this service derive it exactly as the live state
97129
+ * does. A stub that recomputed it by hand could disagree with production and
97130
+ * still pass.
97131
+ */
97132
+ function deriveAcceptingTurns(input) {
97133
+ return RUNTIME_EXECUTION_PRECONDITIONS.every((precondition) => input.preconditions[precondition]) && !input.draining && !input.shutdownRequested;
97134
+ }
97135
+ const initialState = {
97136
+ activeTurns: /* @__PURE__ */ new Set(),
97137
+ activeTurnCount: 0,
97138
+ activeSessionCount: 0,
97139
+ draining: false,
97140
+ drainReason: null,
97141
+ drainTargetImageRef: null,
97142
+ drainTargetImageVersion: null,
97143
+ drainForceRequired: false,
97144
+ preconditions: {
97145
+ orchestrationEngine: false,
97146
+ providerLayer: false
97147
+ },
97148
+ shutdownRequested: false
97149
+ };
97150
+ function snapshotFromState(state) {
97151
+ return {
97152
+ activeTurnCount: state.activeTurnCount,
97153
+ activeSessionCount: state.activeSessionCount,
97154
+ draining: state.draining,
97155
+ drainReason: state.drainReason,
97156
+ drainTargetImageRef: state.drainTargetImageRef,
97157
+ drainTargetImageVersion: state.drainTargetImageVersion,
97158
+ drainForceRequired: state.drainForceRequired,
97159
+ acceptingTurns: deriveAcceptingTurns(state)
97160
+ };
97161
+ }
97162
+ function turnKey(input) {
97163
+ return `${input.threadId}:${input.turnId ?? "unknown"}`;
97164
+ }
97165
+ function normalizeCount(count) {
97166
+ return Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
97167
+ }
97168
+ const makeRuntimeDrainState = require_Schema$1.gen(function* () {
97169
+ const stateRef = yield* require_HttpServer.make$3(initialState);
97170
+ const update = (f) => require_HttpServer.updateAndGet(stateRef, f).pipe(require_Schema$1.map(snapshotFromState));
97171
+ return {
97172
+ beginTurn: (input) => update((state) => {
97173
+ const activeTurns = new Set(state.activeTurns);
97174
+ activeTurns.add(turnKey(input));
97175
+ return {
97176
+ ...state,
97177
+ activeTurns,
97178
+ activeTurnCount: activeTurns.size
97179
+ };
97180
+ }),
97181
+ completeTurn: (input) => update((state) => {
97182
+ const activeTurns = new Set(state.activeTurns);
97183
+ activeTurns.delete(turnKey(input));
97184
+ return {
97185
+ ...state,
97186
+ activeTurns,
97187
+ activeTurnCount: activeTurns.size
97188
+ };
97189
+ }),
97190
+ clearActiveTurns: update((state) => ({
97191
+ ...state,
97192
+ activeTurns: /* @__PURE__ */ new Set(),
97193
+ activeTurnCount: 0
97194
+ })),
97195
+ setActiveSessionCount: (count) => update((state) => ({
97196
+ ...state,
97197
+ activeSessionCount: normalizeCount(count)
97198
+ })),
97199
+ reconcileProviderSessions: (sessions) => update((state) => {
97200
+ const activeSessions = sessions.filter((session) => session.status !== "closed");
97201
+ const activeTurns = new Set(activeSessions.flatMap((session) => session.activeTurnId === void 0 ? [] : [turnKey({
97202
+ threadId: session.threadId,
97203
+ turnId: session.activeTurnId
97204
+ })]));
97205
+ return {
97206
+ ...state,
97207
+ activeTurns,
97208
+ activeTurnCount: activeTurns.size,
97209
+ activeSessionCount: activeSessions.length
97210
+ };
97211
+ }),
97212
+ applyControlPlaneState: (input) => update((state) => ({
97213
+ ...state,
97214
+ draining: input.draining ?? state.draining,
97215
+ drainReason: input.drainReason ?? state.drainReason,
97216
+ drainTargetImageRef: input.drainTargetImageRef ?? state.drainTargetImageRef,
97217
+ drainTargetImageVersion: input.drainTargetImageVersion ?? state.drainTargetImageVersion,
97218
+ drainForceRequired: input.drainForceRequired ?? state.drainForceRequired
97219
+ })),
97220
+ markExecutionPrecondition: (precondition) => update((state) => ({
97221
+ ...state,
97222
+ preconditions: {
97223
+ ...state.preconditions,
97224
+ [precondition]: true
97225
+ }
97226
+ })),
97227
+ requestShutdown: update((state) => ({
97228
+ ...state,
97229
+ shutdownRequested: true
97230
+ })),
97231
+ snapshot: require_HttpServer.get$1(stateRef).pipe(require_Schema$1.map(snapshotFromState)),
97232
+ isDraining: require_HttpServer.get$1(stateRef).pipe(require_Schema$1.map((state) => state.draining))
97233
+ };
97234
+ });
97235
+ var RuntimeDrainState = class extends require_Schema$1.Service()("forge/runtimeDrain/RuntimeDrainState") {};
97236
+ const RuntimeDrainStateLive = require_Schema$1.effect(RuntimeDrainState, makeRuntimeDrainState);
97237
+ //#endregion
96895
97238
  //#region src/persistence/Services/LocalAutomations.ts
96896
97239
  const LocalAutomationStatus = require_Schema$1.Literals([
96897
97240
  "active",
@@ -98299,6 +98642,28 @@ function serializeWorktreeLaunchActivity(activity) {
98299
98642
  function decodeWorktreeLaunchActivityKind(kind) {
98300
98643
  return isCmmdRuntimeWorktreeLaunchActivityKind(kind) ? kind : null;
98301
98644
  }
98645
+ function planStepsOf(activity) {
98646
+ const decoded = decodeCmmdRuntimePlanActivity(encodeCmmdRuntimePlanActivityPayload(activity.kind, activity.payload));
98647
+ return decoded !== null && decoded.plan.length > 0 ? decoded.plan : null;
98648
+ }
98649
+ function serializePlanActivity(activity) {
98650
+ if (activity.kind !== "turn.plan.updated") return null;
98651
+ const plan = planStepsOf(activity);
98652
+ if (plan === null) return null;
98653
+ const completed = plan.filter((step) => step.status === "completed").length;
98654
+ return {
98655
+ sourceSequence: null,
98656
+ event: {
98657
+ eventId: `activity:${activity.id}`,
98658
+ occurredAt: canonicalRuntimeEventTimestamp(activity.createdAt),
98659
+ ...eventTurn(activity.turnId),
98660
+ kind: "tool_call",
98661
+ summary: `Plan: ${completed} of ${plan.length} steps done`,
98662
+ status: "completed",
98663
+ payload: encodeCmmdRuntimePlanActivityPayload(activity.kind, activity.payload)
98664
+ }
98665
+ };
98666
+ }
98302
98667
  function isReplicatedFailureActivity(activity) {
98303
98668
  return activity.kind === "runtime.error" || activity.kind.startsWith("provider.") && activity.kind.endsWith(".failed");
98304
98669
  }
@@ -98683,6 +99048,10 @@ function serializeRuntimeThreadEvents(thread) {
98683
99048
  const event = serializeWorktreeLaunchActivity(activity);
98684
99049
  return event === null ? [] : [event];
98685
99050
  });
99051
+ const planActivities = thread.activities.flatMap((activity) => {
99052
+ const event = serializePlanActivity(activity);
99053
+ return event === null ? [] : [event];
99054
+ });
98686
99055
  const recordedTurns = serializeRecordedTurns(thread);
98687
99056
  const textGenerations = serializeTextGenerations(thread);
98688
99057
  return [
@@ -98690,6 +99059,7 @@ function serializeRuntimeThreadEvents(thread) {
98690
99059
  ...replicaActivities,
98691
99060
  ...toolActivities,
98692
99061
  ...worktreeLaunchActivities,
99062
+ ...planActivities,
98693
99063
  ...failureActivities,
98694
99064
  ...recordedTurns,
98695
99065
  ...textGenerations
@@ -100501,6 +100871,30 @@ const makeCommandGate = require_Schema$1.gen(function* () {
100501
100871
  })
100502
100872
  };
100503
100873
  });
100874
+ /**
100875
+ * Settle the command gate for a finished startup, and report whether commands
100876
+ * are now accepted.
100877
+ *
100878
+ * The success branch is the moment the orchestration engine can take work, so
100879
+ * it is where the engine half of the accepting-turns fact is marked. A failed
100880
+ * startup accepts nothing and must leave the fact untouched: absent must not
100881
+ * read as healthy.
100882
+ */
100883
+ const settleCommandReadiness = (input) => require_Schema$1.gen(function* () {
100884
+ if (require_Schema$1.isFailure(input.startupExit)) {
100885
+ const error = new ServerRuntimeStartupError({
100886
+ message: startupFailureMessage(input.startupExit.cause),
100887
+ cause: input.startupExit.cause
100888
+ });
100889
+ yield* require_Schema$1.logError("server runtime startup failed", { cause: describeStartupFailure(input.startupExit.cause) });
100890
+ yield* input.commandGate.failCommandReady(error);
100891
+ return false;
100892
+ }
100893
+ yield* require_Schema$1.logDebug("Accepting commands");
100894
+ yield* input.commandGate.signalCommandReady;
100895
+ yield* input.drainState.markExecutionPrecondition("orchestrationEngine");
100896
+ return true;
100897
+ });
100504
100898
  const launchStartupHeartbeat = require_Schema$1.gen(function* () {
100505
100899
  const analytics = yield* AnalyticsService;
100506
100900
  const { threadCount, projectCount } = yield* (yield* ProjectionSnapshotQuery).getCounts().pipe(require_Schema$1.catch_((cause) => require_Schema$1.logWarning("failed to gather startup projection counts for telemetry", { cause }).pipe(require_Schema$1.as({
@@ -100682,6 +101076,7 @@ const makeServerRuntimeStartup = require_Schema$1.gen(function* () {
100682
101076
  const serverSettings = yield* ServerSettingsService;
100683
101077
  const serverEnvironment = yield* ServerEnvironment;
100684
101078
  const usage = yield* UsageService;
101079
+ const drainState = yield* RuntimeDrainState;
100685
101080
  const commandGate = yield* makeCommandGate;
100686
101081
  const httpListening = yield* require_Schema$1.make$4();
100687
101082
  const reactorScope = yield* require_Schema$1.make$3("sequential");
@@ -100764,18 +101159,11 @@ const makeServerRuntimeStartup = require_Schema$1.gen(function* () {
100764
101159
  root: true
100765
101160
  }));
100766
101161
  yield* require_Schema$1.forkScoped(require_Schema$1.gen(function* () {
100767
- const startupExit = yield* require_Schema$1.exit(startup);
100768
- if (require_Schema$1.isFailure(startupExit)) {
100769
- const error = new ServerRuntimeStartupError({
100770
- message: startupFailureMessage(startupExit.cause),
100771
- cause: startupExit.cause
100772
- });
100773
- yield* require_Schema$1.logError("server runtime startup failed", { cause: describeStartupFailure(startupExit.cause) });
100774
- yield* commandGate.failCommandReady(error);
100775
- return;
100776
- }
100777
- yield* require_Schema$1.logDebug("Accepting commands");
100778
- yield* commandGate.signalCommandReady;
101162
+ if (!(yield* settleCommandReadiness({
101163
+ startupExit: yield* require_Schema$1.exit(startup),
101164
+ commandGate,
101165
+ drainState
101166
+ }))) return;
100779
101167
  yield* require_Schema$1.logDebug("startup phase: starting the usage scan warm-up");
100780
101168
  yield* require_Schema$1.forkScoped(runUsageScanWarmup({ readSummary: usage.readSummary }));
100781
101169
  yield* require_Schema$1.logDebug("startup phase: waiting for http listener");
@@ -153387,7 +153775,9 @@ var CodexAppServerManager = class extends node_events.EventEmitter {
153387
153775
  scheduleProviderEgressTokenRefresh(context, delayMs) {
153388
153776
  if (context.tokenRefreshTimer) clearTimeout(context.tokenRefreshTimer);
153389
153777
  const tokenRefreshTimer = setTimeout(() => {
153390
- this.handleProviderEgressTokenRefresh(context.session.threadId).catch(() => void 0);
153778
+ this.handleProviderEgressTokenRefresh(context.session.threadId).catch((error) => {
153779
+ this.emitErrorEvent(context, "session/tokenRefreshFailed", error instanceof Error ? error.message : String(error));
153780
+ });
153391
153781
  }, delayMs);
153392
153782
  tokenRefreshTimer.unref?.();
153393
153783
  context.tokenRefreshTimer = tokenRefreshTimer;
@@ -290741,93 +291131,6 @@ async function maybeRefreshRuntimeProvisioningGrant(input) {
290741
291131
  return "refreshed";
290742
291132
  }
290743
291133
  //#endregion
290744
- //#region src/runtimeDrain/RuntimeDrainState.ts
290745
- const initialState = {
290746
- activeTurns: /* @__PURE__ */ new Set(),
290747
- activeTurnCount: 0,
290748
- activeSessionCount: 0,
290749
- draining: false,
290750
- drainReason: null,
290751
- drainTargetImageRef: null,
290752
- drainTargetImageVersion: null,
290753
- drainForceRequired: false
290754
- };
290755
- function snapshotFromState(state) {
290756
- return {
290757
- activeTurnCount: state.activeTurnCount,
290758
- activeSessionCount: state.activeSessionCount,
290759
- draining: state.draining,
290760
- drainReason: state.drainReason,
290761
- drainTargetImageRef: state.drainTargetImageRef,
290762
- drainTargetImageVersion: state.drainTargetImageVersion,
290763
- drainForceRequired: state.drainForceRequired
290764
- };
290765
- }
290766
- function turnKey(input) {
290767
- return `${input.threadId}:${input.turnId ?? "unknown"}`;
290768
- }
290769
- function normalizeCount(count) {
290770
- return Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
290771
- }
290772
- const makeRuntimeDrainState = require_Schema$1.gen(function* () {
290773
- const stateRef = yield* require_HttpServer.make$3(initialState);
290774
- const update = (f) => require_HttpServer.updateAndGet(stateRef, f).pipe(require_Schema$1.map(snapshotFromState));
290775
- return {
290776
- beginTurn: (input) => update((state) => {
290777
- const activeTurns = new Set(state.activeTurns);
290778
- activeTurns.add(turnKey(input));
290779
- return {
290780
- ...state,
290781
- activeTurns,
290782
- activeTurnCount: activeTurns.size
290783
- };
290784
- }),
290785
- completeTurn: (input) => update((state) => {
290786
- const activeTurns = new Set(state.activeTurns);
290787
- activeTurns.delete(turnKey(input));
290788
- return {
290789
- ...state,
290790
- activeTurns,
290791
- activeTurnCount: activeTurns.size
290792
- };
290793
- }),
290794
- clearActiveTurns: update((state) => ({
290795
- ...state,
290796
- activeTurns: /* @__PURE__ */ new Set(),
290797
- activeTurnCount: 0
290798
- })),
290799
- setActiveSessionCount: (count) => update((state) => ({
290800
- ...state,
290801
- activeSessionCount: normalizeCount(count)
290802
- })),
290803
- reconcileProviderSessions: (sessions) => update((state) => {
290804
- const activeSessions = sessions.filter((session) => session.status !== "closed");
290805
- const activeTurns = new Set(activeSessions.flatMap((session) => session.activeTurnId === void 0 ? [] : [turnKey({
290806
- threadId: session.threadId,
290807
- turnId: session.activeTurnId
290808
- })]));
290809
- return {
290810
- ...state,
290811
- activeTurns,
290812
- activeTurnCount: activeTurns.size,
290813
- activeSessionCount: activeSessions.length
290814
- };
290815
- }),
290816
- applyControlPlaneState: (input) => update((state) => ({
290817
- ...state,
290818
- draining: input.draining ?? state.draining,
290819
- drainReason: input.drainReason ?? state.drainReason,
290820
- drainTargetImageRef: input.drainTargetImageRef ?? state.drainTargetImageRef,
290821
- drainTargetImageVersion: input.drainTargetImageVersion ?? state.drainTargetImageVersion,
290822
- drainForceRequired: input.drainForceRequired ?? state.drainForceRequired
290823
- })),
290824
- snapshot: require_HttpServer.get$1(stateRef).pipe(require_Schema$1.map(snapshotFromState)),
290825
- isDraining: require_HttpServer.get$1(stateRef).pipe(require_Schema$1.map((state) => state.draining))
290826
- };
290827
- });
290828
- var RuntimeDrainState = class extends require_Schema$1.Service()("forge/runtimeDrain/RuntimeDrainState") {};
290829
- const RuntimeDrainStateLive = require_Schema$1.effect(RuntimeDrainState, makeRuntimeDrainState);
290830
- //#endregion
290831
291134
  //#region src/runtimeDrain/RuntimeDrainReporter.ts
290832
291135
  const HEARTBEAT_INTERVAL = require_Schema$1.seconds(30);
290833
291136
  const HEARTBEAT_TICK_TIMEOUT = require_Schema$1.seconds(60);
@@ -290885,6 +291188,7 @@ const postRuntimeHeartbeat = (config, snapshot) => require_Schema$1.promise(asyn
290885
291188
  ...config,
290886
291189
  activeTurnCount: snapshot.activeTurnCount,
290887
291190
  activeSessionCount: snapshot.activeSessionCount,
291191
+ acceptingTurns: snapshot.acceptingTurns,
290888
291192
  ...forgeGitCommit !== void 0 ? { forgeGitCommit } : {},
290889
291193
  forgeServerVersion: installedForgeServerVersion
290890
291194
  });
@@ -290993,6 +291297,7 @@ const makeRuntimeDrainReporter = require_Schema$1.gen(function* () {
290993
291297
  work: reportOnce,
290994
291298
  interval: HEARTBEAT_INTERVAL
290995
291299
  }));
291300
+ yield* require_Schema$1.addFinalizer(() => drainState.requestShutdown);
290996
291301
  })
290997
291302
  };
290998
291303
  });
@@ -291203,6 +291508,7 @@ function decodeCodexRateLimitWindows(raw) {
291203
291508
  function publicProviderRuntimeError(message) {
291204
291509
  return sanitizePublicProviderText(message, { fallback: DEFAULT_PUBLIC_PROVIDER_ERROR });
291205
291510
  }
291511
+ const processDomainEvent = (_event) => require_Schema$1.void_;
291206
291512
  function isAssistantTextDeltaEvent(event) {
291207
291513
  return event.type === "content.delta" && event.payload.streamKind === "assistant_text";
291208
291514
  }
@@ -291998,7 +292304,6 @@ const make$9 = require_Schema$1.fn("make")(function* () {
291998
292304
  yield* runtimeDrainReporter.reportOnce;
291999
292305
  }
292000
292306
  });
292001
- const processDomainEvent = (_event) => require_Schema$1.void_;
292002
292307
  const processInput = (input) => input.source === "runtime" ? processRuntimeEvent(input.event) : processDomainEvent(input.event);
292003
292308
  const processInputSafely = (input) => processInput(input).pipe(require_Schema$1.catchCause((cause) => {
292004
292309
  if (require_Schema$1.hasInterruptsOnly(cause)) return require_Schema$1.failCause(cause);
@@ -292049,6 +292354,7 @@ const make$9 = require_Schema$1.fn("make")(function* () {
292049
292354
  return {
292050
292355
  start: require_Schema$1.fn("start")(function* () {
292051
292356
  yield* reconcileOrphanedRunningSessions();
292357
+ yield* runtimeDrainState.markExecutionPrecondition("providerLayer");
292052
292358
  yield* runtimeDrainReporter.start;
292053
292359
  yield* require_Schema$1.forkScoped(require_Path.runForEach(providerService.streamEvents, (event) => enqueueInput({
292054
292360
  source: "runtime",
@@ -292288,6 +292594,8 @@ const HANDLED_TURN_START_KEY_MAX = 1e4;
292288
292594
  const HANDLED_TURN_START_KEY_TTL = require_Schema$1.minutes(30);
292289
292595
  const DEFAULT_RUNTIME_MODE = "full-access";
292290
292596
  const DEFAULT_THREAD_TITLE = "New thread";
292597
+ const isProviderValidationError = require_Schema$1.is(ProviderValidationError);
292598
+ const isProviderKind = require_Schema$1.is(ProviderKind);
292291
292599
  function isPathInsideRoot(candidatePath, rootPath) {
292292
292600
  let resolvedCandidate;
292293
292601
  let resolvedRoot;
@@ -292324,6 +292632,38 @@ function resolveExistingWorkspaceCwd(input) {
292324
292632
  function publicProviderFailureDetail(detail) {
292325
292633
  return sanitizePublicProviderText(detail, { fallback: "CMMD is temporarily unavailable. Check the workspace AI connection and retry." }) ?? "CMMD is temporarily unavailable. Check the workspace AI connection and retry.";
292326
292634
  }
292635
+ /**
292636
+ * The failure detail a transcript shows, judged on the text and not the envelope.
292637
+ *
292638
+ * Forge writes its own validation copy, so `ProviderValidationError.issue` is
292639
+ * not raw provider output. Every envelope around it hides it, each in its own
292640
+ * way: `Cause.pretty` prints the error tag, and the sanitizer counts
292641
+ * "ProviderValidationError" as a provider leak, so a validation failure
292642
+ * rendered through `Cause.pretty` tripped the guard on its own wrapper. The
292643
+ * `message` getter spells the same tag out in prose. On 2026-09-16 the first of
292644
+ * those replaced "Reconnect the account and try again" with "CMMD is
292645
+ * temporarily unavailable" (#3337).
292646
+ *
292647
+ * The issue still goes through the sanitizer. `ProviderService` builds some
292648
+ * issues from a schema formatter, which echoes the decoded payload, so this is a
292649
+ * narrower input to the same guard rather than a way around it.
292650
+ */
292651
+ function publicProviderFailureDetailFromError(error) {
292652
+ if (isProviderValidationError(error)) return publicProviderFailureDetail(error.issue);
292653
+ return publicProviderFailureDetail(error instanceof Error ? error.message : String(error));
292654
+ }
292655
+ /**
292656
+ * The same judgement for a failure that arrives as a cause.
292657
+ *
292658
+ * Only the validation branch delegates. A cause carries what a bare error does
292659
+ * not: defects, interrupts, and more than one failure, all of which
292660
+ * `Cause.pretty` renders and `.message` would drop. Squashing every failure into
292661
+ * its message to reach one shared line would lose that.
292662
+ */
292663
+ function publicProviderFailureDetailFromCause(cause) {
292664
+ const error = require_Schema$1.squash(cause);
292665
+ return isProviderValidationError(error) ? publicProviderFailureDetailFromError(error) : publicProviderFailureDetail(require_Schema$1.pretty(cause));
292666
+ }
292327
292667
  function canReplaceThreadTitle(currentTitle, titleSeed) {
292328
292668
  const trimmedCurrentTitle = currentTitle.trim();
292329
292669
  if (trimmedCurrentTitle === DEFAULT_THREAD_TITLE) return true;
@@ -292479,7 +292819,7 @@ const make$8 = require_Schema$1.gen(function* () {
292479
292819
  const thread = readModel.threads.find((entry) => entry.id === threadId);
292480
292820
  if (!thread) return yield* require_Schema$1.die(/* @__PURE__ */ new Error(`Thread '${threadId}' was not found in read model.`));
292481
292821
  const desiredRuntimeMode = thread.runtimeMode;
292482
- const currentProvider = thread.session?.status !== "stopped" && require_Schema$1.is(ProviderKind)(thread.session?.providerName) ? thread.session.providerName : void 0;
292822
+ const currentProvider = thread.session?.status !== "stopped" && isProviderKind(thread.session?.providerName) ? thread.session.providerName : void 0;
292483
292823
  const rawRequestedModelSelection = options?.modelSelection;
292484
292824
  const threadProvider = currentProvider ?? thread.modelSelection.provider;
292485
292825
  const isStaleRequestedProvider = rawRequestedModelSelection !== void 0 && rawRequestedModelSelection.provider !== threadProvider;
@@ -292807,7 +293147,7 @@ const make$8 = require_Schema$1.gen(function* () {
292807
293147
  summary: "Provider turn start failed",
292808
293148
  detail: `User message '${event.payload.messageId}' was not found for turn start request.`,
292809
293149
  turnId: null,
292810
- createdAt: event.payload.createdAt
293150
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
292811
293151
  });
292812
293152
  return;
292813
293153
  }
@@ -292858,9 +293198,9 @@ const make$8 = require_Schema$1.gen(function* () {
292858
293198
  threadId: event.payload.threadId,
292859
293199
  kind: "provider.turn.start.failed",
292860
293200
  summary: "Provider turn start failed",
292861
- detail: publicProviderFailureDetail(require_Schema$1.pretty(cause)),
293201
+ detail: publicProviderFailureDetailFromCause(cause),
292862
293202
  turnId: null,
292863
- createdAt: event.payload.createdAt
293203
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
292864
293204
  })));
292865
293205
  });
292866
293206
  const processTurnInterruptRequested = require_Schema$1.fn("processTurnInterruptRequested")(function* (event) {
@@ -292874,7 +293214,14 @@ const make$8 = require_Schema$1.gen(function* () {
292874
293214
  turnId: event.payload.turnId ?? null,
292875
293215
  createdAt: event.payload.createdAt
292876
293216
  });
292877
- yield* providerService.interruptTurn({ threadId: event.payload.threadId });
293217
+ yield* providerService.interruptTurn({ threadId: event.payload.threadId }).pipe(require_Schema$1.catchCause((cause) => appendProviderFailureActivity({
293218
+ threadId: event.payload.threadId,
293219
+ kind: "provider.turn.interrupt.failed",
293220
+ summary: "Provider turn interrupt failed",
293221
+ detail: publicProviderFailureDetail(require_Schema$1.pretty(cause)),
293222
+ turnId: event.payload.turnId ?? null,
293223
+ createdAt: event.payload.createdAt
293224
+ })));
292878
293225
  });
292879
293226
  const processApprovalResponseRequested = require_Schema$1.fn("processApprovalResponseRequested")(function* (event) {
292880
293227
  const staged = peekThreadContinuationSession(event.commandId);
@@ -292911,7 +293258,7 @@ const make$8 = require_Schema$1.gen(function* () {
292911
293258
  threadId: event.payload.threadId,
292912
293259
  kind: "provider.approval.respond.failed",
292913
293260
  summary: "Provider approval response failed",
292914
- detail: isUnknownPendingApprovalRequestError(cause) ? stalePendingRequestDetail("approval", event.payload.requestId) : publicProviderFailureDetail(require_Schema$1.pretty(cause)),
293261
+ detail: isUnknownPendingApprovalRequestError(cause) ? stalePendingRequestDetail("approval", event.payload.requestId) : publicProviderFailureDetailFromCause(cause),
292915
293262
  turnId: null,
292916
293263
  createdAt: event.payload.createdAt,
292917
293264
  requestId: event.payload.requestId
@@ -292954,7 +293301,7 @@ const make$8 = require_Schema$1.gen(function* () {
292954
293301
  threadId: event.payload.threadId,
292955
293302
  kind: "provider.user-input.respond.failed",
292956
293303
  summary: "Provider user input response failed",
292957
- detail: isUnknownPendingUserInputRequestError(cause) ? stalePendingRequestDetail("user-input", event.payload.requestId) : publicProviderFailureDetail(require_Schema$1.pretty(cause)),
293304
+ detail: isUnknownPendingUserInputRequestError(cause) ? stalePendingRequestDetail("user-input", event.payload.requestId) : publicProviderFailureDetailFromCause(cause),
292958
293305
  turnId: null,
292959
293306
  createdAt: event.payload.createdAt,
292960
293307
  requestId: event.payload.requestId
@@ -292967,8 +293314,7 @@ const make$8 = require_Schema$1.gen(function* () {
292967
293314
  yield* providerService.stopSession({
292968
293315
  threadId: thread.id,
292969
293316
  ...event.payload.preserveResumeState === true ? { preserveResumeState: true } : {}
292970
- });
292971
- yield* setThreadSession({
293317
+ }).pipe(require_Schema$1.tap(() => setThreadSession({
292972
293318
  threadId: thread.id,
292973
293319
  session: {
292974
293320
  threadId: thread.id,
@@ -292980,7 +293326,14 @@ const make$8 = require_Schema$1.gen(function* () {
292980
293326
  updatedAt: now
292981
293327
  },
292982
293328
  createdAt: now
292983
- });
293329
+ })), require_Schema$1.catchCause((cause) => appendProviderFailureActivity({
293330
+ threadId: event.payload.threadId,
293331
+ kind: "provider.session.stop.failed",
293332
+ summary: "Provider session stop failed",
293333
+ detail: publicProviderFailureDetail(require_Schema$1.pretty(cause)),
293334
+ turnId: null,
293335
+ createdAt: now
293336
+ })));
292984
293337
  });
292985
293338
  const processDomainEvent = require_Schema$1.fn("processDomainEvent")(function* (event) {
292986
293339
  yield* require_Schema$1.annotateCurrentSpan({
@@ -293075,7 +293428,7 @@ const make$8 = require_Schema$1.gen(function* () {
293075
293428
  threadId: event.payload.threadId,
293076
293429
  kind: "provider.turn.start.failed",
293077
293430
  summary: "Provider turn start cleanup failed",
293078
- detail: publicProviderFailureDetail(cause instanceof Error ? cause.message : String(cause)),
293431
+ detail: publicProviderFailureDetailFromError(cause),
293079
293432
  turnId: null,
293080
293433
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
293081
293434
  })
@@ -298543,7 +298896,7 @@ function resolveBuildCommitFromEnv(env) {
298543
298896
  * environment descriptor down instead of reporting an honest "unknown".
298544
298897
  */
298545
298898
  function readBakedBuildCommit() {
298546
- return "dea91e954d3d2f23154802ae304412fabe90dbbd";
298899
+ return "b06001f4e16f7a336e9108920f9250b366862f3d";
298547
298900
  }
298548
298901
  async function resolveServerBuildCommit(input) {
298549
298902
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -309967,6 +310320,33 @@ registerProxyRoute({
309967
310320
  cmmdPath: "/api/projects/campaigns/:id",
309968
310321
  orgIdSource: "none"
309969
310322
  });
310323
+ /**
310324
+ * Apple App Store guideline 5.1.1(v): an app that supports account creation
310325
+ * or sign-in must let the user start account deletion from inside the app.
310326
+ * CMMD owns the account, so this proxies to its existing deactivation route.
310327
+ *
310328
+ * `cmmdPath` is `/api/settings/delete-account`, not a `/api/forge/context/*`
310329
+ * route: CMMD never built a forge-context-scoped deletion endpoint. It is
310330
+ * still reachable through the same owner JWT this gateway already resolves
310331
+ * per session: CMMD's `optionalJwtAuth` runs ahead of every `/api/*` route
310332
+ * (not only forge-context ones), so the same Bearer that reaches
310333
+ * `/api/cmmd/bootstrap` also authenticates here.
310334
+ *
310335
+ * CMMD's route soft-deletes: it flips the user to `active: false` and
310336
+ * `lifecycleState: 'deactivated'`, revokes CMMD's own refresh tokens, and
310337
+ * destroys its session. It does not purge data on this call. The Forge
310338
+ * client is responsible for the rest: revoking the Forge session
310339
+ * (`/api/auth/logout`) and clearing local state, the same as a manual
310340
+ * sign-out.
310341
+ */
310342
+ registerProxyRoute({
310343
+ forgePath: "/api/cmmd/settings/delete-account",
310344
+ method: "DELETE",
310345
+ cmmdPath: "/api/settings/delete-account",
310346
+ orgIdSource: "none",
310347
+ responseShape: "unwrapData",
310348
+ requestBody: "none"
310349
+ });
309970
310350
  registerProxyRoute({
309971
310351
  forgePath: "/api/cmmd/bootstrap",
309972
310352
  method: "GET",
@@ -322006,7 +322386,7 @@ const PlatformServicesLive = require_Schema$1.unwrap(require_Schema$1.gen(functi
322006
322386
  }));
322007
322387
  const RuntimeReplicationLayerLive = RuntimeReplicationLive.pipe(require_Schema$1.provideMerge(RuntimeReplicationClientLive), require_Schema$1.provideMerge(RuntimeReplicationOutboxLive), require_Schema$1.provideMerge(ActiveHourMeterLive.pipe(require_Schema$1.provide$1(RuntimeReplicationOutboxLive))));
322008
322388
  const RuntimeDrainLayerLive = RuntimeDrainReporterLive.pipe(require_Schema$1.provideMerge(RuntimeDrainStateLive));
322009
- const RuntimeAwareProviderReactorsLive = require_Schema$1.mergeAll(ProviderRuntimeIngestionLive, ProviderCommandReactorLive).pipe(require_Schema$1.provide$1(RuntimeDrainLayerLive), require_Schema$1.provide$1(ClaudeUsageTelemetryLive));
322389
+ const RuntimeAwareProviderReactorsLive = require_Schema$1.mergeAll(ProviderRuntimeIngestionLive, ProviderCommandReactorLive).pipe(require_Schema$1.provide$1(RuntimeDrainLayerLive), require_Schema$1.provideMerge(RuntimeDrainStateLive), require_Schema$1.provide$1(ClaudeUsageTelemetryLive));
322010
322390
  const PipelineWorkerCoordinatorLayerLive = PipelineWorkerCoordinatorLive.pipe(require_Schema$1.provideMerge(PipelineRunClientLive));
322011
322391
  const AgentProtocolReactorLayerLive = AgentProtocolReactorLive.pipe(require_Schema$1.provideMerge(PipelineRunStoreLive));
322012
322392
  const PipelineRunCoordinatorLayerLive = PipelineRunCoordinatorLive.pipe(require_Schema$1.provideMerge(PipelineProviderAuthorityInstallerLive), require_Schema$1.provideMerge(PipelineChangeInspectorLive), require_Schema$1.provideMerge(PipelineContextArtifactStoreLive), require_Schema$1.provideMerge(PipelineModelPolicyLive), require_Schema$1.provideMerge(PipelineRunClientLive), require_Schema$1.provideMerge(PipelineRunStoreLive), require_Schema$1.provideMerge(PipelineRunWorkspaceAllocatorLive), require_Schema$1.provideMerge(PipelineStagePolicyLive), require_Schema$1.provideMerge(PipelineWorkerCoordinatorLayerLive));