@cmmd-center/forge 0.12.0 → 0.13.1

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.
Files changed (25) hide show
  1. package/dist/bin.cjs +546 -192
  2. package/dist/bin.mjs +543 -191
  3. package/dist/client/assets/{CanvasPanel-CL4T3ptl.js → CanvasPanel-BrVen-8G.js} +2 -2
  4. package/dist/client/assets/{CompositeItem-NATmsDHX.js → CompositeItem-BNZfMUvm.js} +2 -2
  5. package/dist/client/assets/{DiffPanel-29UmSyE3.js → DiffPanel-tnITsSoL.js} +3 -3
  6. package/dist/client/assets/{DiffPanel.logic-D5Bn5tgT.js → DiffPanel.logic-Ce-amvoZ.js} +2 -2
  7. package/dist/client/assets/DiffPanelShell-C14W3dyR.js +2531 -0
  8. package/dist/client/assets/{DiffWorkerPoolProvider-BMZL3CsO.js → DiffWorkerPoolProvider-BmseZYiJ.js} +8 -8
  9. package/dist/client/assets/PullRequestCodePanel-A_x1n0YN.js +5 -0
  10. package/dist/client/assets/{Virtualizer-uq0d5TwY.js → Virtualizer-CU3LnU6A.js} +2 -2
  11. package/dist/client/assets/{canvasStateStore-DtidOKbV.js → canvasStateStore-DN5mPB5B.js} +2 -2
  12. package/dist/client/assets/{cmmdAuthRedirect-CzACd5XJ.js → cmmdAuthRedirect-sqR8W6vY.js} +2 -2
  13. package/dist/client/assets/index-DUeDfPMN.js +151 -0
  14. package/dist/client/assets/index-Dk7T0zVg.css +1 -0
  15. package/dist/client/assets/src-CFqnisEM.js +3 -0
  16. package/dist/client/assets/{toggle-pwklbW9N.js → toggle-CLUqZXdO.js} +2 -2
  17. package/dist/client/assets/{useTriggerFocusGuards-CPLQ8_CW.js → useTriggerFocusGuards-CaUimC-O.js} +2 -2
  18. package/dist/client/index.html +11 -11
  19. package/dist/forge-build.json +1 -1
  20. package/package.json +1 -1
  21. package/dist/client/assets/DiffPanelShell-ClKPX_Pu.js +0 -2531
  22. package/dist/client/assets/PullRequestCodePanel-Cp75delh.js +0 -5
  23. package/dist/client/assets/index-BMcOaHS_.css +0 -1
  24. package/dist/client/assets/index-BOI5P7rG.js +0 -151
  25. package/dist/client/assets/src-BlFkWEsP.js +0 -3
package/dist/bin.mjs CHANGED
@@ -16520,7 +16520,7 @@ function makeNetServiceShape(createServer = Net.createServer) {
16520
16520
  }
16521
16521
  //#endregion
16522
16522
  //#region ../../packages/shared/src/releaseInfo.generated.ts
16523
- const FORGE_RELEASE_VERSION = "0.12.0";
16523
+ const FORGE_RELEASE_VERSION = "0.13.1";
16524
16524
  //#endregion
16525
16525
  //#region ../../packages/shared/src/hostProcess.ts
16526
16526
  /**
@@ -18580,6 +18580,40 @@ const TerminalError = Union([
18580
18580
  TerminalControlSequenceError,
18581
18581
  TerminalSignalError
18582
18582
  ]);
18583
+ const ProjectScript = Struct({
18584
+ id: TrimmedNonEmptyString,
18585
+ name: TrimmedNonEmptyString,
18586
+ command: TrimmedNonEmptyString,
18587
+ icon: Literals([
18588
+ "play",
18589
+ "test",
18590
+ "lint",
18591
+ "configure",
18592
+ "build",
18593
+ "debug"
18594
+ ]),
18595
+ runOnWorktreeCreate: Boolean$1
18596
+ });
18597
+ const ProjectScriptUpdateCommand = Struct({
18598
+ type: Literal("project.script.update"),
18599
+ commandId: CommandId,
18600
+ projectId: ProjectId,
18601
+ mutation: Union([
18602
+ Struct({
18603
+ kind: Literal("add"),
18604
+ script: ProjectScript
18605
+ }),
18606
+ Struct({
18607
+ kind: Literal("update"),
18608
+ script: ProjectScript,
18609
+ expected: ProjectScript
18610
+ }),
18611
+ Struct({
18612
+ kind: Literal("remove"),
18613
+ expected: ProjectScript
18614
+ })
18615
+ ])
18616
+ });
18583
18617
  //#endregion
18584
18618
  //#region ../../packages/contracts/src/model.ts
18585
18619
  const CODEX_REASONING_EFFORT_OPTIONS = [
@@ -19222,20 +19256,6 @@ const UploadChatImageAttachment = Struct({
19222
19256
  });
19223
19257
  const ChatAttachment = Union([ChatImageAttachment]);
19224
19258
  const UploadChatAttachment = Union([UploadChatImageAttachment]);
19225
- const ProjectScript = Struct({
19226
- id: TrimmedNonEmptyString,
19227
- name: TrimmedNonEmptyString,
19228
- command: TrimmedNonEmptyString,
19229
- icon: Literals([
19230
- "play",
19231
- "test",
19232
- "lint",
19233
- "configure",
19234
- "build",
19235
- "debug"
19236
- ]),
19237
- runOnWorktreeCreate: Boolean$1
19238
- });
19239
19259
  /**
19240
19260
  * Availability of the project's `workspaceRoot` on the server's filesystem.
19241
19261
  *
@@ -19865,6 +19885,7 @@ const ThreadSessionStopCommand = Struct({
19865
19885
  const DispatchableClientOrchestrationCommand = Union([
19866
19886
  ProjectCreateCommand,
19867
19887
  ProjectMetaUpdateCommand,
19888
+ ProjectScriptUpdateCommand,
19868
19889
  ProjectDeleteCommand,
19869
19890
  ThreadCreateCommand,
19870
19891
  ThreadDeleteCommand,
@@ -19886,6 +19907,7 @@ const DispatchableClientOrchestrationCommand = Union([
19886
19907
  const ClientOrchestrationCommand = Union([
19887
19908
  ProjectCreateCommand,
19888
19909
  ProjectMetaUpdateCommand,
19910
+ ProjectScriptUpdateCommand,
19889
19911
  ProjectDeleteCommand,
19890
19912
  ThreadCreateCommand,
19891
19913
  ThreadDeleteCommand,
@@ -22442,6 +22464,27 @@ const ProviderUnavailableDetail = Literals([
22442
22464
  "delivery-pending",
22443
22465
  "reconnect-required"
22444
22466
  ]);
22467
+ /**
22468
+ * Whether the provider CLI honours the credential-directory variable, as this
22469
+ * Environment's probe found. `inconclusive` is a probe that ran and could not
22470
+ * tell. It is not `unsupported`, and it is never reported as `supported`. The
22471
+ * members are CMMD's `forgeRuntimeInventoryCredentialDirectoryAnswers`.
22472
+ */
22473
+ const ProviderCredentialDirectoryAnswer = Literals([
22474
+ "supported",
22475
+ "unsupported",
22476
+ "inconclusive"
22477
+ ]);
22478
+ /**
22479
+ * Which Credential Store the provider CLI uses on this Environment: its file,
22480
+ * the operating system's store, or `unknown` when it cannot be read the way
22481
+ * the CLI reads it. The members are CMMD's `forgeRuntimeInventoryCredentialStores`.
22482
+ */
22483
+ const ProviderCredentialStore = Literals([
22484
+ "file",
22485
+ "os",
22486
+ "unknown"
22487
+ ]);
22445
22488
  const ServerProviderModel = Struct({
22446
22489
  slug: TrimmedNonEmptyString,
22447
22490
  name: TrimmedNonEmptyString,
@@ -22478,6 +22521,8 @@ const ServerProvider = Struct({
22478
22521
  availability: optional$4(ProviderAvailability),
22479
22522
  unavailableReason: optional$4(ProviderUnavailableReason),
22480
22523
  unavailableDetail: optional$4(ProviderUnavailableDetail),
22524
+ credentialDirectory: optional$4(ProviderCredentialDirectoryAnswer),
22525
+ credentialStore: optional$4(ProviderCredentialStore),
22481
22526
  slashCommands: ArraySchema(ServerProviderSlashCommand).pipe(withDecodingDefault(succeed([]))),
22482
22527
  skills: ArraySchema(ServerProviderSkill).pipe(withDecodingDefault(succeed([])))
22483
22528
  });
@@ -26425,12 +26470,12 @@ function encodeCmmdRuntimeThreadWorkspaceMetadata(workspace) {
26425
26470
  }
26426
26471
  //#endregion
26427
26472
  //#region ../../packages/contracts/src/cmmdRuntimeToolActivity.ts
26428
- function isRecord$22(value) {
26473
+ function isRecord$23(value) {
26429
26474
  return value !== null && typeof value === "object" && !Array.isArray(value);
26430
26475
  }
26431
26476
  function encodeCmmdRuntimeToolActivityPayload(kind, payload) {
26432
26477
  return {
26433
- ...isRecord$22(payload) ? payload : { sourcePayload: payload },
26478
+ ...isRecord$23(payload) ? payload : { sourcePayload: payload },
26434
26479
  forgeToolActivity: {
26435
26480
  schemaVersion: 1,
26436
26481
  source: "native",
@@ -30422,13 +30467,13 @@ async function completePendingDesktopRelayIdentityRotation(input) {
30422
30467
  }
30423
30468
  //#endregion
30424
30469
  //#region ../../packages/relay-host/src/internal/desktopRelayJws.ts
30425
- function isRecord$21(value) {
30470
+ function isRecord$22(value) {
30426
30471
  return typeof value === "object" && value !== null && !Array.isArray(value);
30427
30472
  }
30428
30473
  function decodeBase64UrlJson$1(value) {
30429
30474
  try {
30430
30475
  const decoded = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
30431
- return isRecord$21(decoded) ? decoded : null;
30476
+ return isRecord$22(decoded) ? decoded : null;
30432
30477
  } catch {
30433
30478
  return null;
30434
30479
  }
@@ -30582,11 +30627,11 @@ var DesktopRelayControlPlaneError = class extends Error {
30582
30627
  this.name = "DesktopRelayControlPlaneError";
30583
30628
  }
30584
30629
  };
30585
- function isRecord$20(value) {
30630
+ function isRecord$21(value) {
30586
30631
  return typeof value === "object" && value !== null && !Array.isArray(value);
30587
30632
  }
30588
30633
  function parseErrorPayload(value) {
30589
- return isRecord$20(value) ? value : {};
30634
+ return isRecord$21(value) ? value : {};
30590
30635
  }
30591
30636
  function messageFromErrorPayload(payload, fallback) {
30592
30637
  return typeof payload.error === "string" && payload.error.trim().length > 0 ? payload.error.trim() : fallback;
@@ -30684,7 +30729,7 @@ async function postControlPlane(input) {
30684
30729
  const errorPayload = parseErrorPayload(payload);
30685
30730
  throw new DesktopRelayControlPlaneError(typeof errorPayload.code === "string" && errorPayload.code.trim().length > 0 ? errorPayload.code.trim() : `CMMD_HTTP_${response.status}`, messageFromErrorPayload(errorPayload, `CMMD relay request failed (${response.status}).`), response.status === 408 || response.status === 429 || response.status >= 500);
30686
30731
  }
30687
- if (!isRecord$20(payload) || !("data" in payload)) throw new DesktopRelayControlPlaneError("CONTROL_PLANE_INVALID_RESPONSE", "CMMD returned an invalid Forge relay response.", false);
30732
+ if (!isRecord$21(payload) || !("data" in payload)) throw new DesktopRelayControlPlaneError("CONTROL_PLANE_INVALID_RESPONSE", "CMMD returned an invalid Forge relay response.", false);
30688
30733
  return payload.data;
30689
30734
  }
30690
30735
  function resolveRegisteredDevice(input) {
@@ -31554,6 +31599,10 @@ const relayCommandPolicyByType = {
31554
31599
  requiredScope: "project.write",
31555
31600
  deliveryClass: "idempotent-command"
31556
31601
  },
31602
+ "project.script.update": {
31603
+ requiredScope: "project.write",
31604
+ deliveryClass: "idempotent-command"
31605
+ },
31557
31606
  "project.delete": {
31558
31607
  requiredScope: "project.write",
31559
31608
  deliveryClass: "idempotent-command"
@@ -65533,7 +65582,7 @@ function normalizeNumberish(value) {
65533
65582
  }
65534
65583
  //#endregion
65535
65584
  //#region package.json
65536
- var version$1 = "0.12.0";
65585
+ var version$1 = "0.13.1";
65537
65586
  //#endregion
65538
65587
  //#region src/sentry.ts
65539
65588
  const SERVER_APP_NAME = "forge-server";
@@ -71075,12 +71124,12 @@ function resolveDesktopRelayControlPlaneTarget(path, configuredRuntimeId) {
71075
71124
  code: "RELAY_OPERATION_DENIED"
71076
71125
  };
71077
71126
  }
71078
- function isRecord$19(value) {
71127
+ function isRecord$20(value) {
71079
71128
  return typeof value === "object" && value !== null && !Array.isArray(value);
71080
71129
  }
71081
71130
  function relayControlPlaneBodyMatchesRuntime(path, body, configuredRuntimeId) {
71082
71131
  if (path !== "/api/forge/context/devices/register") return true;
71083
- if (!isRecord$19(body) || !isRecord$19(body.runtime)) return false;
71132
+ if (!isRecord$20(body) || !isRecord$20(body.runtime)) return false;
71084
71133
  return body.runtime.runtimeId === configuredRuntimeId;
71085
71134
  }
71086
71135
  /** Matches the client id `connect login` used, so CMMD accepts the refresh. */
@@ -71095,7 +71144,7 @@ function forbiddenResponse() {
71095
71144
  return errorResponse(403, "RELAY_BRIDGE_FORBIDDEN", "Desktop relay bridge access denied.");
71096
71145
  }
71097
71146
  function relayProxyErrorResponse(error) {
71098
- if (error instanceof CmmdProxyRequestError && isRecord$19(error.body)) {
71147
+ if (error instanceof CmmdProxyRequestError && isRecord$20(error.body)) {
71099
71148
  const code = typeof error.body.code === "string" ? error.body.code : error.code;
71100
71149
  const message = typeof error.body.error === "string" ? error.body.error : error.detail;
71101
71150
  return errorResponse(error.status >= 500 ? 502 : error.status, code, message ?? "CMMD relay coordination failed.");
@@ -93401,7 +93450,7 @@ const REPLICA_THREAD_STATUSES = [
93401
93450
  ];
93402
93451
  //#endregion
93403
93452
  //#region src/runtimeReplication/replicaDecodeModel.ts
93404
- function isRecord$18(value) {
93453
+ function isRecord$19(value) {
93405
93454
  return typeof value === "object" && value !== null && !Array.isArray(value);
93406
93455
  }
93407
93456
  function isNonBlankString(value) {
@@ -93421,7 +93470,7 @@ function nullableInteger(value) {
93421
93470
  return value === null || typeof value === "number" && Number.isSafeInteger(value);
93422
93471
  }
93423
93472
  function unwrapEnvelope(payload, expectedDomain) {
93424
- if (!isRecord$18(payload) || !isRecord$18(payload.meta) || !isRecord$18(payload.data)) return null;
93473
+ if (!isRecord$19(payload) || !isRecord$19(payload.meta) || !isRecord$19(payload.data)) return null;
93425
93474
  const { domain, timestamp, version } = payload.meta;
93426
93475
  if (!isNonBlankString(domain) || expectedDomain !== void 0 && domain !== expectedDomain || version !== "1.0" || !isIsoDateTime(timestamp)) return null;
93427
93476
  return payload.data;
@@ -93434,12 +93483,12 @@ function hasValidRuntimeIdentity(value) {
93434
93483
  ].includes(String(value.runtimeKind)) && typeof value.displayName === "string" && typeof value.status === "string";
93435
93484
  }
93436
93485
  function decodeRuntime$1(value) {
93437
- if (!isRecord$18(value) || !hasValidRuntimeIdentity(value)) return null;
93438
- if (!nullableString(value.endpointUrl) || value.endpointUrl !== null && !URL.canParse(value.endpointUrl) || !nullableInteger(value.deviceId) || !nullableString(value.workspaceRoot) || !nullableString(value.githubRepoFullName) || !(value.lastSeenAt === null || isIsoDateTime(value.lastSeenAt)) || !(value.metadata === null || isRecord$18(value.metadata))) return null;
93486
+ if (!isRecord$19(value) || !hasValidRuntimeIdentity(value)) return null;
93487
+ if (!nullableString(value.endpointUrl) || value.endpointUrl !== null && !URL.canParse(value.endpointUrl) || !nullableInteger(value.deviceId) || !nullableString(value.workspaceRoot) || !nullableString(value.githubRepoFullName) || !(value.lastSeenAt === null || isIsoDateTime(value.lastSeenAt)) || !(value.metadata === null || isRecord$19(value.metadata))) return null;
93439
93488
  return value;
93440
93489
  }
93441
93490
  function decodeProject(value) {
93442
- if (!isRecord$18(value) || !isNonBlankString(value.projectId) || typeof value.title !== "string" || !nullableString(value.githubRepoFullName) || !nullableString(value.branch)) return null;
93491
+ if (!isRecord$19(value) || !isNonBlankString(value.projectId) || typeof value.title !== "string" || !nullableString(value.githubRepoFullName) || !nullableString(value.branch)) return null;
93443
93492
  return {
93444
93493
  projectId: value.projectId,
93445
93494
  title: value.title,
@@ -93448,11 +93497,11 @@ function decodeProject(value) {
93448
93497
  };
93449
93498
  }
93450
93499
  function decodeThread$1(value) {
93451
- if (!isRecord$18(value) || !isNonBlankString(value.threadId) || typeof value.title !== "string" || !isReplicaThreadStatus(value.status) || !nullableString(value.model) || !nullableString(value.branch) || !(value.lastActivityAt === null || isIsoDateTime(value.lastActivityAt))) return null;
93500
+ if (!isRecord$19(value) || !isNonBlankString(value.threadId) || typeof value.title !== "string" || !isReplicaThreadStatus(value.status) || !nullableString(value.model) || !nullableString(value.branch) || !(value.lastActivityAt === null || isIsoDateTime(value.lastActivityAt))) return null;
93452
93501
  return value;
93453
93502
  }
93454
93503
  function decodeSummary(value) {
93455
- if (!isRecord$18(value) || !nullableString(value.conversationId)) return null;
93504
+ if (!isRecord$19(value) || !nullableString(value.conversationId)) return null;
93456
93505
  const project = decodeProject(value.project);
93457
93506
  const thread = decodeThread$1(value.thread);
93458
93507
  return project === null || thread === null ? null : {
@@ -93563,7 +93612,7 @@ function isNonNegativeInt(value) {
93563
93612
  /** `usage` is null for an Unreported turn, otherwise exactly the five buckets. */
93564
93613
  function isValidTurnUsageTokens(value) {
93565
93614
  if (value === null) return true;
93566
- if (!isRecord$18(value)) return false;
93615
+ if (!isRecord$19(value)) return false;
93567
93616
  return turnUsageTokenKeys.every((key) => isNonNegativeInt(value[key])) && Object.keys(value).every((key) => turnUsageTokenKeys.includes(key));
93568
93617
  }
93569
93618
  /** The row itself: no text, no status, and the turn it belongs to. */
@@ -93572,7 +93621,7 @@ function hasValidTurnUsageEnvelope(value) {
93572
93621
  }
93573
93622
  /** What the row records: who ran it, what it spent, and what it says of itself. */
93574
93623
  function hasValidTurnUsageRecord(payload) {
93575
- return isRecord$18(payload) && isNonBlankString(payload.provider) && isNonBlankString(payload.model) && "usage" in payload && isValidTurnUsageTokens(payload.usage) && hasValidTurnUsageAttribution(payload);
93624
+ return isRecord$19(payload) && isNonBlankString(payload.provider) && isNonBlankString(payload.model) && "usage" in payload && isValidTurnUsageTokens(payload.usage) && hasValidTurnUsageAttribution(payload);
93576
93625
  }
93577
93626
  /** A turn_usage row: no text, no status, and its record in `payload`. */
93578
93627
  function hasValidTurnUsagePayload(value) {
@@ -93609,7 +93658,7 @@ function hasValidEventPayload(value) {
93609
93658
  return isNonBlankString(value.text) && value.status === null;
93610
93659
  }
93611
93660
  function decodeEvent$1(value) {
93612
- if (!isRecord$18(value) || !hasValidEventFields(value) || !hasValidEventPayload(value)) return null;
93661
+ if (!isRecord$19(value) || !hasValidEventFields(value) || !hasValidEventPayload(value)) return null;
93613
93662
  return value;
93614
93663
  }
93615
93664
  function decodeEvents(data) {
@@ -93630,11 +93679,11 @@ function decodeReplicaThreadDetail(payload) {
93630
93679
  const thread = decodeThread$1(data.thread);
93631
93680
  const reachability = decodeReachability(data.reachability);
93632
93681
  const events = decodeEvents(data);
93633
- if (runtime === null || project === null || thread === null || reachability === null || events === null || !isRecord$18(data.execution) || typeof data.execution.available !== "boolean" || ![
93682
+ if (runtime === null || project === null || thread === null || reachability === null || events === null || !isRecord$19(data.execution) || typeof data.execution.available !== "boolean" || ![
93634
93683
  null,
93635
93684
  "runtime_offline",
93636
93685
  "runtime_unavailable"
93637
- ].includes(data.execution.reason) || (data.execution.available ? data.execution.reason !== null : data.execution.reason === null) || !isRecord$18(data.replication) || data.replication.protocolVersion !== 2 || !isNonBlankString(data.replication.generationId) || !Number.isSafeInteger(data.replication.acknowledgedSequence) || Number(data.replication.acknowledgedSequence) < 0 || !isNonBlankString(data.replication.headHash)) return null;
93686
+ ].includes(data.execution.reason) || (data.execution.available ? data.execution.reason !== null : data.execution.reason === null) || !isRecord$19(data.replication) || data.replication.protocolVersion !== 2 || !isNonBlankString(data.replication.generationId) || !Number.isSafeInteger(data.replication.acknowledgedSequence) || Number(data.replication.acknowledgedSequence) < 0 || !isNonBlankString(data.replication.headHash)) return null;
93638
93687
  return {
93639
93688
  conversationId: data.conversationId,
93640
93689
  runtime,
@@ -94554,7 +94603,7 @@ function resolveRuntimeReplicationRuntimeId(config) {
94554
94603
  }
94555
94604
  //#endregion
94556
94605
  //#region src/usage/providerTokenBuckets.ts
94557
- function isRecord$17(value) {
94606
+ function isRecord$18(value) {
94558
94607
  return typeof value === "object" && value !== null && !Array.isArray(value);
94559
94608
  }
94560
94609
  function count(record, ...keys) {
@@ -94575,7 +94624,7 @@ function reportedOrNull(tokens) {
94575
94624
  * the app server sends camelCase.
94576
94625
  */
94577
94626
  function codexTokenBuckets(value) {
94578
- if (!isRecord$17(value)) return null;
94627
+ if (!isRecord$18(value)) return null;
94579
94628
  const inputTokens = count(value, "input_tokens", "inputTokens");
94580
94629
  const cachedInputTokens = count(value, "cached_input_tokens", "cachedInputTokens");
94581
94630
  const cacheCreationTokens = count(value, "cache_write_input_tokens", "cacheWriteInputTokens");
@@ -94594,7 +94643,7 @@ function codexTokenBuckets(value) {
94594
94643
  * tokens into output and does not break them out, so reasoning stays zero.
94595
94644
  */
94596
94645
  function claudeTokenBuckets(value) {
94597
- if (!isRecord$17(value)) return null;
94646
+ if (!isRecord$18(value)) return null;
94598
94647
  return reportedOrNull({
94599
94648
  uncachedInputTokens: count(value, "input_tokens", "inputTokens"),
94600
94649
  cachedInputTokens: count(value, "cache_read_input_tokens", "cacheReadInputTokens"),
@@ -95488,6 +95537,20 @@ function publishedAvailability(provider) {
95488
95537
  ...detail === void 0 ? {} : { unavailableDetail: detail }
95489
95538
  };
95490
95539
  }
95540
+ /**
95541
+ * What the Claude CLI said about its credentials, beside the version it said
95542
+ * it for. CMMD keeps each Claude version's answers, so these go on the Claude
95543
+ * entry only. Each is copied only when the snapshot has an answer: absent means
95544
+ * the runtime has not answered yet, and inventing a value would hide that.
95545
+ */
95546
+ function claudeCredentialAnswersToPublish(provider) {
95547
+ if (provider.provider !== "claudeAgent") return {};
95548
+ const { credentialDirectory, credentialStore } = provider;
95549
+ return {
95550
+ ...credentialDirectory === void 0 ? {} : { credentialDirectory },
95551
+ ...credentialStore === void 0 ? {} : { credentialStore }
95552
+ };
95553
+ }
95491
95554
  function serializeProvider(provider) {
95492
95555
  const models = [];
95493
95556
  for (const model of provider.models) {
@@ -95500,7 +95563,8 @@ function serializeProvider(provider) {
95500
95563
  models,
95501
95564
  ...publishedAvailability(provider),
95502
95565
  version: cappedText(provider.version, VERSION_MAX_LENGTH),
95503
- binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH)
95566
+ binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH),
95567
+ ...claudeCredentialAnswersToPublish(provider)
95504
95568
  };
95505
95569
  }
95506
95570
  function serializeProviderCatalog(providers) {
@@ -95521,7 +95585,7 @@ const replicaTurnStatuses = new Set([
95521
95585
  "cancelled",
95522
95586
  "interrupted"
95523
95587
  ]);
95524
- function isRecord$16(value) {
95588
+ function isRecord$17(value) {
95525
95589
  return typeof value === "object" && value !== null && !Array.isArray(value);
95526
95590
  }
95527
95591
  function replicaSourceSequence(value) {
@@ -95534,7 +95598,7 @@ function replicaSourceTurn(sourceTurnId) {
95534
95598
  return typeof sourceTurnId === "string" && sourceTurnId.length > 0 ? { turnId: sourceTurnId } : {};
95535
95599
  }
95536
95600
  function replicaActivitySource(activity) {
95537
- if (!activity.kind.startsWith("replica.") || !isRecord$16(activity.payload)) return null;
95601
+ if (!activity.kind.startsWith("replica.") || !isRecord$17(activity.payload)) return null;
95538
95602
  const sourceSequence = replicaSourceSequence(activity.sequence);
95539
95603
  const eventId = replicaSourceEventId(activity.payload.sourceEventId);
95540
95604
  if (sourceSequence === null || eventId === null) return null;
@@ -95547,7 +95611,7 @@ function replicaActivitySource(activity) {
95547
95611
  }
95548
95612
  function replicaSummaryEvent(activity, source) {
95549
95613
  if (activity.kind !== "replica.activity_summary" && activity.kind !== "replica.error_summary") return null;
95550
- const sourceText = isRecord$16(activity.payload) ? activity.payload.text : null;
95614
+ const sourceText = isRecord$17(activity.payload) ? activity.payload.text : null;
95551
95615
  return {
95552
95616
  sourceSequence: source.sourceSequence,
95553
95617
  event: {
@@ -95560,7 +95624,7 @@ function replicaSummaryEvent(activity, source) {
95560
95624
  };
95561
95625
  }
95562
95626
  function replicaTurnEvent(activity, source) {
95563
- const status = isRecord$16(activity.payload) ? activity.payload.status : null;
95627
+ const status = isRecord$17(activity.payload) ? activity.payload.status : null;
95564
95628
  if (activity.kind !== "replica.turn_status" || !replicaTurnStatuses.has(status)) return null;
95565
95629
  return {
95566
95630
  sourceSequence: source.sourceSequence,
@@ -95574,7 +95638,7 @@ function replicaTurnEvent(activity, source) {
95574
95638
  };
95575
95639
  }
95576
95640
  function replicaToolCallEvent(activity, source) {
95577
- if (activity.kind !== "replica.tool_call" || !isRecord$16(activity.payload)) return null;
95641
+ if (activity.kind !== "replica.tool_call" || !isRecord$17(activity.payload)) return null;
95578
95642
  const sourceText = activity.payload.text;
95579
95643
  const status = activity.payload.status;
95580
95644
  if (typeof sourceText !== "string" || typeof status !== "string") return null;
@@ -95592,9 +95656,9 @@ function replicaToolCallEvent(activity, source) {
95592
95656
  };
95593
95657
  }
95594
95658
  function replicaTurnUsageEvent(activity, source) {
95595
- if (activity.kind !== "replica.turn_usage" || !isRecord$16(activity.payload)) return null;
95659
+ if (activity.kind !== "replica.turn_usage" || !isRecord$17(activity.payload)) return null;
95596
95660
  const usage = activity.payload.payload;
95597
- if (!("turnId" in source.turn) || !isRecord$16(usage)) return null;
95661
+ if (!("turnId" in source.turn) || !isRecord$17(usage)) return null;
95598
95662
  const provider = usage.provider;
95599
95663
  const model = usage.model;
95600
95664
  if (typeof provider !== "string" || typeof model !== "string" || !("usage" in usage)) return null;
@@ -95646,6 +95710,10 @@ function threadStatus(thread) {
95646
95710
  if (thread.latestTurn) return "running";
95647
95711
  return "active";
95648
95712
  }
95713
+ function threadProviderAccountId(thread) {
95714
+ const selection = thread.modelSelection;
95715
+ return selection && "providerAccountId" in selection ? selection.providerAccountId ?? null : null;
95716
+ }
95649
95717
  function serializeRuntimeInventory(snapshot, providerInventory) {
95650
95718
  const activeProjects = snapshot.projects.filter((project) => project.availability !== "missing");
95651
95719
  const projectIds = new Set(activeProjects.map((project) => project.id));
@@ -95676,6 +95744,7 @@ function serializeRuntimeInventory(snapshot, providerInventory) {
95676
95744
  status: threadStatus(thread),
95677
95745
  model: thread.modelSelection?.model ?? null,
95678
95746
  provider: thread.modelSelection?.provider ?? null,
95747
+ providerAccountId: threadProviderAccountId(thread),
95679
95748
  branch: thread.branch,
95680
95749
  lastActivityAt: thread.latestUserMessageAt ?? thread.updatedAt,
95681
95750
  kind: thread.kind,
@@ -96401,9 +96470,9 @@ function isUsageSessionLookupRuntimeNotFound(error) {
96401
96470
  async function isRuntimeNotFoundResponse(response) {
96402
96471
  if (response.status !== 404) return false;
96403
96472
  const body = await response.json().catch(() => null);
96404
- return isRecord$15(body) && body["code"] === "RUNTIME_NOT_FOUND";
96473
+ return isRecord$16(body) && body["code"] === "RUNTIME_NOT_FOUND";
96405
96474
  }
96406
- function isRecord$15(value) {
96475
+ function isRecord$16(value) {
96407
96476
  return typeof value === "object" && value !== null;
96408
96477
  }
96409
96478
  /**
@@ -96415,11 +96484,11 @@ function isRecord$15(value) {
96415
96484
  * no count on the page saying so.
96416
96485
  */
96417
96486
  function decodeHeldSessions(body) {
96418
- const held = isRecord$15(body) && isRecord$15(body["data"]) ? body["data"]["held"] : void 0;
96487
+ const held = isRecord$16(body) && isRecord$16(body["data"]) ? body["data"]["held"] : void 0;
96419
96488
  if (!Array.isArray(held)) throw new Error("The usage session lookup returned an invalid body.");
96420
96489
  return held.map((row) => {
96421
- const providerSessionId = isRecord$15(row) ? row["providerSessionId"] : void 0;
96422
- const runtimeId = isRecord$15(row) ? row["runtimeId"] : void 0;
96490
+ const providerSessionId = isRecord$16(row) ? row["providerSessionId"] : void 0;
96491
+ const runtimeId = isRecord$16(row) ? row["runtimeId"] : void 0;
96423
96492
  if (typeof providerSessionId !== "string" || typeof runtimeId !== "string") throw new Error("The usage session lookup returned an invalid held row.");
96424
96493
  return {
96425
96494
  providerSessionId,
@@ -98907,7 +98976,7 @@ var BrowserManager = class extends Service$1()("forge/browser/Services/Manager/B
98907
98976
  const DEFAULT_PORT_MIN = 5200;
98908
98977
  const DEFAULT_PORT_MAX = 5999;
98909
98978
  var ThreadDevRuntime = class extends Service$1()("forge/threadDevRuntime/ThreadDevRuntime") {};
98910
- function nowIso$4() {
98979
+ function nowIso$5() {
98911
98980
  return (/* @__PURE__ */ new Date()).toISOString();
98912
98981
  }
98913
98982
  function previewUrlForPort(port) {
@@ -98934,7 +99003,7 @@ const ThreadDevRuntimeLive = effect(ThreadDevRuntime, gen(function* () {
98934
99003
  allocatedPort: port,
98935
99004
  status: "allocated",
98936
99005
  previewUrl: previewUrlForPort(port),
98937
- updatedAt: nowIso$4()
99006
+ updatedAt: nowIso$5()
98938
99007
  };
98939
99008
  yield* update$4(byThreadId, (current) => {
98940
99009
  const next = new Map(current);
@@ -98956,7 +99025,7 @@ const ThreadDevRuntimeLive = effect(ThreadDevRuntime, gen(function* () {
98956
99025
  next.set(input.threadId, {
98957
99026
  ...existing,
98958
99027
  status: "released",
98959
- updatedAt: nowIso$4()
99028
+ updatedAt: nowIso$5()
98960
99029
  });
98961
99030
  return next;
98962
99031
  });
@@ -98982,7 +99051,7 @@ const ThreadDevRuntimeLive = effect(ThreadDevRuntime, gen(function* () {
98982
99051
  ...patch.terminalId !== void 0 ? { terminalId: patch.terminalId } : {},
98983
99052
  ...patch.previewUrl !== void 0 ? { previewUrl: patch.previewUrl } : {},
98984
99053
  ...patch.tunnelId !== void 0 ? { tunnelId: patch.tunnelId } : {},
98985
- updatedAt: nowIso$4()
99054
+ updatedAt: nowIso$5()
98986
99055
  };
98987
99056
  yield* update$4(byThreadId, (current) => {
98988
99057
  const next = new Map(current);
@@ -99210,7 +99279,7 @@ function emptyConfig(source, path) {
99210
99279
  issues: []
99211
99280
  };
99212
99281
  }
99213
- function isRecord$14(value) {
99282
+ function isRecord$15(value) {
99214
99283
  return typeof value === "object" && value !== null && !Array.isArray(value);
99215
99284
  }
99216
99285
  function stringArray(value) {
@@ -99238,7 +99307,7 @@ function terminalsFromValue(value) {
99238
99307
  name: `Terminal ${index + 1}`,
99239
99308
  command: terminal.trim()
99240
99309
  }];
99241
- if (!isRecord$14(terminal)) return [];
99310
+ if (!isRecord$15(terminal)) return [];
99242
99311
  const command = typeof terminal.command === "string" ? terminal.command.trim() : "";
99243
99312
  if (!command) return [];
99244
99313
  return [{
@@ -99277,7 +99346,7 @@ function readConfigFile(source, path) {
99277
99346
  }]
99278
99347
  };
99279
99348
  }
99280
- if (!isRecord$14(parsed)) return {
99349
+ if (!isRecord$15(parsed)) return {
99281
99350
  ...emptyConfig(source, path),
99282
99351
  issues: [{
99283
99352
  severity: "blocked",
@@ -100420,7 +100489,7 @@ const CLAUDE_IDENTITY_KEYS = new Set([
100420
100489
  "credentialGenerationId"
100421
100490
  ]);
100422
100491
  const CLAUDE_IDENTITY_OPTIONAL_KEYS = new Set(["providerAccountIdentity"]);
100423
- function isRecord$13(value) {
100492
+ function isRecord$14(value) {
100424
100493
  return typeof value === "object" && value !== null && !Array.isArray(value);
100425
100494
  }
100426
100495
  function isStrictRfc3339DateTime(value) {
@@ -100449,7 +100518,7 @@ function hasAllowedKeys(value, required, optional) {
100449
100518
  return keys.every((key) => required.has(key) || optional.has(key)) && [...required].every((key) => keys.includes(key));
100450
100519
  }
100451
100520
  function parseDeliveryIdentity(value, provider) {
100452
- if (!isRecord$13(value)) return null;
100521
+ if (!isRecord$14(value)) return null;
100453
100522
  if (provider === "openai") return parseCodexDeliveryIdentity(value);
100454
100523
  if (provider === "anthropic") return parseClaudeDeliveryIdentity(value);
100455
100524
  return null;
@@ -100484,7 +100553,7 @@ function parseClaudeDeliveryIdentity(value) {
100484
100553
  };
100485
100554
  }
100486
100555
  function parseProviderRuntimeDeliveryReceipt(value, expected) {
100487
- if (!isRecord$13(value) || !hasExactKeys$2(value, RECEIPT_KEYS)) return null;
100556
+ if (!isRecord$14(value) || !hasExactKeys$2(value, RECEIPT_KEYS)) return null;
100488
100557
  const identity = parseDeliveryIdentity(value.identity, expected.provider);
100489
100558
  if (!identity || typeof value.receiptId !== "string" || value.receiptId.length === 0 || value.receiptId.length > 256 || value.runtimeId !== expected.runtimeId || value.machineId !== expected.machineId || value.credentialEpoch !== expected.credentialEpoch || !Number.isInteger(value.credentialEpoch) || value.credentialEpoch < 0 || !isStrictRfc3339DateTime(value.issuedAt) || value.expiresAt !== expected.expiresAt || value.expiresAt !== null && !isStrictRfc3339DateTime(value.expiresAt)) return null;
100490
100559
  return {
@@ -100506,8 +100575,8 @@ function providerDeliveryBindingFromReceipt(receipt) {
100506
100575
  };
100507
100576
  }
100508
100577
  function readProviderDeliveryBinding(value) {
100509
- if (!isRecord$13(value) || !hasExactKeys$2(value, BINDING_KEYS)) return null;
100510
- const identityProvider = isRecord$13(value.identity) ? value.identity.provider : void 0;
100578
+ if (!isRecord$14(value) || !hasExactKeys$2(value, BINDING_KEYS)) return null;
100579
+ const identityProvider = isRecord$14(value.identity) ? value.identity.provider : void 0;
100511
100580
  const tokenProvider = identityProvider === "codex" ? "openai" : identityProvider === "claude" ? "anthropic" : null;
100512
100581
  if (!tokenProvider) return null;
100513
100582
  const identity = parseDeliveryIdentity(value.identity, tokenProvider);
@@ -147773,12 +147842,12 @@ function decodeProviderKind(providerName, operation) {
147773
147842
  detail: `Unknown persisted provider '${providerName}'.`
147774
147843
  }));
147775
147844
  }
147776
- function isRecord$12(value) {
147845
+ function isRecord$13(value) {
147777
147846
  return value !== null && typeof value === "object" && !Array.isArray(value);
147778
147847
  }
147779
147848
  function mergeRuntimePayload(existing, next) {
147780
147849
  if (next === void 0) return existing ?? null;
147781
- if (isRecord$12(existing) && isRecord$12(next)) return {
147850
+ if (isRecord$13(existing) && isRecord$13(next)) return {
147782
147851
  ...existing,
147783
147852
  ...next
147784
147853
  };
@@ -149742,7 +149811,7 @@ const PAGINATION_KEYS = new Set([
149742
149811
  "total",
149743
149812
  "hasMore"
149744
149813
  ]);
149745
- function isRecord$11(value) {
149814
+ function isRecord$12(value) {
149746
149815
  return typeof value === "object" && value !== null && !Array.isArray(value);
149747
149816
  }
149748
149817
  function hasOnlyKeys(value, allowed) {
@@ -149755,17 +149824,17 @@ function isRfc3339(value) {
149755
149824
  return typeof value === "string" && RFC3339.test(value) && Number.isFinite(Date.parse(value));
149756
149825
  }
149757
149826
  function isEnvelopeMeta(value) {
149758
- if (!isRecord$11(value) || !hasExactKeys$1(value, META_KEYS)) return false;
149827
+ if (!isRecord$12(value) || !hasExactKeys$1(value, META_KEYS)) return false;
149759
149828
  return typeof value.domain === "string" && value.domain.length > 0 && isRfc3339(value.timestamp) && typeof value.version === "string" && value.version.length > 0;
149760
149829
  }
149761
149830
  function isPagination(value) {
149762
- if (!isRecord$11(value) || !hasExactKeys$1(value, PAGINATION_KEYS)) return false;
149831
+ if (!isRecord$12(value) || !hasExactKeys$1(value, PAGINATION_KEYS)) return false;
149763
149832
  return Number.isInteger(value.page) && value.page >= 0 && Number.isInteger(value.limit) && value.limit >= 0 && Number.isInteger(value.total) && value.total >= 0 && typeof value.hasMore === "boolean";
149764
149833
  }
149765
149834
  function readLocalProviderAccountId(authJson) {
149766
149835
  try {
149767
149836
  const value = JSON.parse(authJson);
149768
- if (!isRecord$11(value) || value.auth_mode !== "chatgpt" || !isRecord$11(value.tokens)) return null;
149837
+ if (!isRecord$12(value) || value.auth_mode !== "chatgpt" || !isRecord$12(value.tokens)) return null;
149769
149838
  const providerAccountId = value.tokens.account_id;
149770
149839
  if (typeof providerAccountId !== "string") return null;
149771
149840
  const normalized = providerAccountId.trim();
@@ -149778,13 +149847,13 @@ function resolveLocalAuthJsonPath(homePath) {
149778
149847
  return join(homePath?.trim() || process.env.CODEX_HOME?.trim() || join(homedir(), ".codex"), "auth.json");
149779
149848
  }
149780
149849
  function parseCodexAccountIdentityEnvelope(value) {
149781
- if (!isRecord$11(value) || !hasOnlyKeys(value, ENVELOPE_KEYS)) return null;
149850
+ if (!isRecord$12(value) || !hasOnlyKeys(value, ENVELOPE_KEYS)) return null;
149782
149851
  if (!("data" in value) || !("meta" in value) || !isEnvelopeMeta(value.meta)) return null;
149783
149852
  if (value.pagination !== void 0 && !isPagination(value.pagination)) return null;
149784
149853
  return readCodexAccountIdentity(value.data);
149785
149854
  }
149786
149855
  function readCodexAccountIdentity(value) {
149787
- if (!isRecord$11(value) || !hasExactKeys$1(value, IDENTITY_KEYS)) return null;
149856
+ if (!isRecord$12(value) || !hasExactKeys$1(value, IDENTITY_KEYS)) return null;
149788
149857
  if (value.kind !== "provider_account" || value.provider !== "codex" || typeof value.providerAccountIdentity !== "string" || !PROVIDER_ACCOUNT_IDENTITY$1.test(value.providerAccountIdentity)) return null;
149789
149858
  return {
149790
149859
  kind: "provider_account",
@@ -210566,6 +210635,11 @@ const PUBLIC_PROVIDER_LEAK_PATTERNS = [
210566
210635
  /https?:\/\/[^\s'"<>)]*(?:apex|deepseek|moonshot|kimi)[^\s'"<>)]*/i
210567
210636
  ];
210568
210637
  const DEFAULT_PUBLIC_PROVIDER_ERROR = "CMMD is temporarily unavailable. Check the workspace AI connection and retry.";
210638
+ function isProviderRateOrQuotaError(message) {
210639
+ if (!message) return false;
210640
+ const normalized = message.toLowerCase();
210641
+ return /\b(rate[\s-]?limit|too\s+many\s+requests|usage[ _-]limit|usage_limit_reached|quota|insufficient_quota)\b/.test(normalized) || /\b429\b/.test(normalized);
210642
+ }
210569
210643
  /**
210570
210644
  * Infer a SAFE, clearer failure category from a (possibly leaky) provider error.
210571
210645
  *
@@ -210581,7 +210655,7 @@ function categorizePublicProviderError(message) {
210581
210655
  if (/\bforge_runtime_stopped\b/.test(m) || /\bthis turn was interrupted because the environment was stopped\b/.test(m)) return "This turn was interrupted because the environment was stopped. Start the environment and retry.";
210582
210656
  if (/\bforge_provider_model_not_allowed\b/.test(m) || /\bnot\s+scoped\s+to\s+the\s+requested\s+model\b/.test(m)) return "The workspace AI credential does not cover the model this turn asked for. Retry, or pick that model for the thread.";
210583
210657
  if (/\bmodel\b[\s\S]*\b(not\s+(found|available|supported)|does\s+not\s+exist|unsupported|invalid|unknown)\b/.test(m) || /\b(unsupported|unknown|invalid)\b[\s\S]*\bmodel\b/.test(m) || /\b(model_not_found|invalid_model|model_not_supported)\b/.test(m)) return "The selected model isn't available right now. Pick a different model and retry.";
210584
- if (/\b(rate[\s-]?limit|too\s+many\s+requests|quota|insufficient_quota)\b/.test(m) || /\b429\b/.test(m)) return "The AI service is rate-limited or out of quota. Wait a moment and retry.";
210658
+ if (isProviderRateOrQuotaError(m)) return "The AI service is rate-limited or out of quota. Wait a moment and retry.";
210585
210659
  if (/\b(unauthorized|forbidden|authentication\s+(failed|error|required)|invalid\s+api\s+key|expired\s+(token|credential))\b/.test(m) || /\b40[13]\b/.test(m)) return "Workspace AI authentication failed. Reconnect the workspace and retry.";
210586
210660
  if (/\b(timed?\s*out|timeout|overloaded|service\s+unavailable)\b/.test(m) || /\b50[234]\b/.test(m)) return "The AI service is temporarily overloaded. Retry in a moment.";
210587
210661
  return null;
@@ -210962,10 +211036,16 @@ function claudeSubscriptionChildEnv(processEnv, credentialDirectory) {
210962
211036
  * environment with `claudeSubscriptionChildEnv`, the same builder as the turn's
210963
211037
  * personal-subscription branch.
210964
211038
  *
210965
- * `claude auth status` is a pure read. It sends no prompt and leaves
210966
- * `.credentials.json` byte-identical, so probing spends no subscription usage.
211039
+ * `claude auth status` sends no prompt and leaves `.credentials.json`
211040
+ * byte-identical, so probing spends no subscription usage. For Team and
211041
+ * Enterprise accounts the CLI's `auth` preAction schedules a remote-settings
211042
+ * fetch it never awaits, and the command exits before that fetch runs (see the
211043
+ * 2026-09-10 amendment to `docs/adr/cmmd-is-the-only-claude-credential-renewer.md`).
210967
211044
  * It prints JSON on stdout and exits 1 when logged out, so the exit code is not
210968
211045
  * a failure signal and the `loggedIn` field is what gets read.
211046
+ *
211047
+ * Each answer a probe reaches is also kept in `claudeCredentialDirectoryAnswers`
211048
+ * so the provider check can publish it without probing.
210969
211049
  */
210970
211050
  const execFileAsync$3 = promisify$1(execFile);
210971
211051
  const AUTH_STATUS_ARGS = ["auth", "status"];
@@ -210982,8 +211062,7 @@ const PROBE_DIRECTORY_PREFIX = "forge-claude-capability-";
210982
211062
  const VERSION_PATTERN = /\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/;
210983
211063
  function makeClaudeCredentialDirectoryProbe(deps) {
210984
211064
  const answers = /* @__PURE__ */ new Map();
210985
- const answer = async () => {
210986
- const version = await readVersionSafely(deps);
211065
+ const answerFor = (version) => {
210987
211066
  if (version === null) return runProbe(deps);
210988
211067
  const cached = answers.get(version);
210989
211068
  if (cached) return cached;
@@ -210994,10 +211073,47 @@ function makeClaudeCredentialDirectoryProbe(deps) {
210994
211073
  };
210995
211074
  pending.then((result) => {
210996
211075
  if (result === "inconclusive") forget();
210997
- }, forget);
211076
+ deps.onAnswer?.(version, result);
211077
+ }, () => {
211078
+ forget();
211079
+ deps.onAnswer?.(version, "inconclusive");
211080
+ });
210998
211081
  return pending;
210999
211082
  };
211000
- return { support: tryPromise(answer).pipe(orElseSucceed(() => "inconclusive")) };
211083
+ return { support: inconclusiveOnFailure(async () => answerFor(await readVersionSafely(deps))) };
211084
+ }
211085
+ /**
211086
+ * The one place a probe failure is handled. A missing binary, a timeout or a
211087
+ * directory that cannot be made all mean the probe could not tell.
211088
+ */
211089
+ function inconclusiveOnFailure(run) {
211090
+ return tryPromise(run).pipe(orElseSucceed(() => "inconclusive"));
211091
+ }
211092
+ const keyOf = (binaryPath, version) => JSON.stringify([binaryPath, version]);
211093
+ function makeClaudeCredentialDirectoryAnswers() {
211094
+ const answers = /* @__PURE__ */ new Map();
211095
+ const listeners = /* @__PURE__ */ new Set();
211096
+ return {
211097
+ record: (binaryPath, version, answer) => {
211098
+ const key = keyOf(binaryPath, version);
211099
+ if (answers.get(key) === answer) return;
211100
+ answers.set(key, answer);
211101
+ for (const listener of listeners) listener();
211102
+ },
211103
+ answerFor: (binaryPath, version) => answers.get(keyOf(binaryPath, version)),
211104
+ subscribe: (listener) => {
211105
+ listeners.add(listener);
211106
+ return () => {
211107
+ listeners.delete(listener);
211108
+ };
211109
+ }
211110
+ };
211111
+ }
211112
+ /** Every answer a turn-time probe in this process has reached. */
211113
+ const claudeCredentialDirectoryAnswers = makeClaudeCredentialDirectoryAnswers();
211114
+ /** The version in `claude --version` output, the key a probe answer is kept under. */
211115
+ function claudeVersionFromOutput(stdout) {
211116
+ return VERSION_PATTERN.exec(stdout)?.[0] ?? null;
211001
211117
  }
211002
211118
  /**
211003
211119
  * One probe per binary path for the life of the process. A conclusive answer is
@@ -211022,7 +211138,12 @@ function claudeCredentialDirectoryProbeFor(binaryPath) {
211022
211138
  if (!binaryPath) return INCONCLUSIVE_PROBE;
211023
211139
  const existing = probesByBinaryPath.get(binaryPath);
211024
211140
  if (existing) return existing;
211025
- const probe = makeClaudeCredentialDirectoryProbe(claudeCredentialDirectoryProbeDeps(binaryPath));
211141
+ const probe = makeClaudeCredentialDirectoryProbe({
211142
+ ...claudeCredentialDirectoryProbeDeps(binaryPath),
211143
+ onAnswer: (version, answer) => {
211144
+ claudeCredentialDirectoryAnswers.record(binaryPath, version, answer);
211145
+ }
211146
+ });
211026
211147
  probesByBinaryPath.set(binaryPath, probe);
211027
211148
  return probe;
211028
211149
  }
@@ -211033,7 +211154,7 @@ function claudeCredentialDirectoryProbeDeps(binaryPath, options = {}) {
211033
211154
  runAuthStatus: ({ credentialDirectory }) => run(AUTH_STATUS_ARGS, credentialDirectory),
211034
211155
  readBinaryVersion: async () => {
211035
211156
  const { stdout } = await run(VERSION_ARGS, null);
211036
- return VERSION_PATTERN.exec(stdout)?.[0] ?? null;
211157
+ return claudeVersionFromOutput(stdout);
211037
211158
  },
211038
211159
  makeProbeDirectory: () => mkdtemp(join(tmpdir(), PROBE_DIRECTORY_PREFIX)),
211039
211160
  removeProbeDirectory: (directory) => rm(directory, {
@@ -211199,6 +211320,171 @@ const makeProviderAuthProvenance = (input) => {
211199
211320
  };
211200
211321
  };
211201
211322
  //#endregion
211323
+ //#region src/provider/claudeCredentialStore.ts
211324
+ /**
211325
+ * Which Credential Store the Claude CLI uses on this machine, read the way the
211326
+ * CLI reads it.
211327
+ *
211328
+ * The CLI has two stores, `.credentials.json` in the credential directory and
211329
+ * the operating system's own, and no platform check picks between them. It
211330
+ * takes the OS store when `CLAUDE_CODE_FORCE_WINDOWS_CREDMAN` is `1`, and
211331
+ * otherwise when the remote flag it caches in its config file,
211332
+ * `cachedGrowthBookFeatures.tengu_windows_credman`, is `true`. That file is a
211333
+ * legacy `.config.json` in the config directory when one exists, and
211334
+ * `.claude.json` otherwise. Read from Claude Code 2.1.265 in #3086.
211335
+ *
211336
+ * The CLI takes the file when it cannot find, read or parse the config, and so
211337
+ * does this. A config that parses to JSON `null` reads `file` as well: the CLI
211338
+ * reads a property off the parsed value, which throws on `null`, and it falls
211339
+ * back to the file store on that throw. Any other config that parses but
211340
+ * carries no boolean flag also means the file to the CLI, but it is the shape a
211341
+ * moved key leaves, so this reads `unknown` rather than guess.
211342
+ *
211343
+ * Linux with no session bus has no secret service, so the OS store fails its
211344
+ * own check and the CLI falls back to the file whatever the flag says. Every
211345
+ * Sprite is that case, and it reads `file` without opening the config. Linux
211346
+ * with a session bus is `unknown` when the OS store is selected, because only
211347
+ * the secret service knows whether it would answer.
211348
+ * See `docs/adr/delivery-clears-the-os-credential-store-first.md`.
211349
+ *
211350
+ * Two things in 2.1.265 this does not follow. The CLI puts its OAuth name
211351
+ * suffix, `F$()` in that bundle, into the config name. The suffix is empty in a
211352
+ * production build and `-custom-oauth` with `CLAUDE_CODE_CUSTOM_OAUTH_URL` set,
211353
+ * so the config is then `.claude-custom-oauth.json` instead of `.claude.json`.
211354
+ * And `primeWindowsCredManBackendEnabled` can set the choice before the file is
211355
+ * read, but only with a v5 storage backend, and `tryCreateV5Backend` returns
211356
+ * nothing in that build, so it never does.
211357
+ */
211358
+ const CLAUDE_FORCE_OS_CREDENTIAL_STORE_ENV = "CLAUDE_CODE_FORCE_WINDOWS_CREDMAN";
211359
+ const CACHED_FEATURES_KEY = "cachedGrowthBookFeatures";
211360
+ const OS_STORE_FLAG_KEY = "tengu_windows_credman";
211361
+ function readClaudeCredentialStore(input) {
211362
+ if (input.platform === "linux" && !hasSessionBus(input)) return "file";
211363
+ const selectsOsStore = cliSelectsOsStore(input);
211364
+ if (selectsOsStore === null) return "unknown";
211365
+ if (!selectsOsStore) return "file";
211366
+ return input.platform === "darwin" || input.platform === "win32" ? "os" : "unknown";
211367
+ }
211368
+ /** The same read against this machine's home directory and disk. */
211369
+ const readClaudeCredentialStoreFromDisk = (context) => {
211370
+ let homeDirectory;
211371
+ try {
211372
+ homeDirectory = homedir();
211373
+ } catch {
211374
+ homeDirectory = null;
211375
+ }
211376
+ return readClaudeCredentialStore({
211377
+ ...context,
211378
+ homeDirectory: homeDirectory ?? "",
211379
+ fileExists: homeDirectory === null ? () => false : existsSync,
211380
+ readFile: (path) => {
211381
+ if (homeDirectory === null) throw new Error("no home directory");
211382
+ return readFileSync(path, "utf8");
211383
+ }
211384
+ });
211385
+ };
211386
+ /** True, false, or null when a parsed config does not say. */
211387
+ function cliSelectsOsStore(input) {
211388
+ if (input.env[CLAUDE_FORCE_OS_CREDENTIAL_STORE_ENV] === "1") return true;
211389
+ const config = readConfig(input);
211390
+ if (config === null) return false;
211391
+ const features = isRecord$11(config.parsed) ? config.parsed[CACHED_FEATURES_KEY] : void 0;
211392
+ const flag = isRecord$11(features) ? features[OS_STORE_FLAG_KEY] : void 0;
211393
+ return typeof flag === "boolean" ? flag : null;
211394
+ }
211395
+ /**
211396
+ * The parsed config, or null wherever the CLI's own read throws and it falls
211397
+ * back to the file store: a file that is missing, unreadable or not JSON, and a
211398
+ * config that parses to JSON `null`, because the CLI reads a property off it.
211399
+ */
211400
+ function readConfig(input) {
211401
+ try {
211402
+ const parsed = JSON.parse(input.readFile(cachedFlagFile(input)));
211403
+ return parsed === null ? null : { parsed };
211404
+ } catch {
211405
+ return null;
211406
+ }
211407
+ }
211408
+ function cachedFlagFile(input) {
211409
+ const configured = input.configDirectory?.trim();
211410
+ const legacy = join(configured || join(input.homeDirectory, ".claude"), ".config.json");
211411
+ if (input.fileExists(legacy)) return legacy;
211412
+ return join(configured || input.homeDirectory, ".claude.json");
211413
+ }
211414
+ /** libsecret reaches the secret service over the session bus, so no bus means no OS store. */
211415
+ function hasSessionBus(input) {
211416
+ if (input.env.DBUS_SESSION_BUS_ADDRESS?.trim()) return true;
211417
+ const runtimeDirectory = input.env.XDG_RUNTIME_DIR?.trim();
211418
+ return runtimeDirectory ? input.fileExists(join(runtimeDirectory, "bus")) : false;
211419
+ }
211420
+ function isRecord$11(value) {
211421
+ return typeof value === "object" && value !== null && !Array.isArray(value);
211422
+ }
211423
+ //#endregion
211424
+ //#region src/provider/claudeCredentialAnswers.ts
211425
+ /**
211426
+ * The live resolver. The credential-directory answer is whatever the turn-time
211427
+ * probe last reached for this binary path and version, so the published answer
211428
+ * and the one that gated a turn are the same. Until a turn has probed, or with
211429
+ * no version to look it up by, the field stays absent.
211430
+ */
211431
+ function makeClaudeCredentialAnswersResolver(options) {
211432
+ const readCredentialStore = options.readCredentialStore ?? readClaudeCredentialStoreFromDisk;
211433
+ return (input) => gen(function* () {
211434
+ const platform = yield* HostProcessPlatform;
211435
+ const credentialStore = yield* sync$1(() => readCredentialStore({
211436
+ env: options.env ?? process.env,
211437
+ platform,
211438
+ configDirectory: input.configDirectory
211439
+ }));
211440
+ const credentialDirectory = input.version === null ? void 0 : options.credentialDirectoryAnswers.answerFor(input.binaryPath, input.version);
211441
+ return credentialDirectory === void 0 ? { credentialStore } : {
211442
+ credentialStore,
211443
+ credentialDirectory
211444
+ };
211445
+ });
211446
+ }
211447
+ //#endregion
211448
+ //#region src/runtimeDrain/supervisedRepeat.ts
211449
+ /**
211450
+ * Runs `work` on a fixed schedule, forever, and RESTARTS the schedule itself
211451
+ * if it ever dies for any reason other than this scope closing.
211452
+ *
211453
+ * `Effect.repeat(work, Schedule.spaced(interval)).pipe(Effect.forkScoped)`
211454
+ * alone has no restart path. `work` catching its own errors only protects
211455
+ * the TICK: an interrupt that lands while `Effect.repeat` is asleep between
211456
+ * ticks — which, at any non-trivial interval, is nearly this loop's entire
211457
+ * life — happens outside anything `work` wraps, and kills that one fiber
211458
+ * outright with nothing logged, because nothing was running to log from.
211459
+ * The process this runs in can otherwise be perfectly healthy while the
211460
+ * schedule is silently, permanently dead.
211461
+ *
211462
+ * This runs the schedule on its own child fiber, raced against an explicit
211463
+ * shutdown signal fired by a finalizer registered in this scope. Only that
211464
+ * signal stops the loop for good; any other death of the schedule fiber —
211465
+ * whatever causes it, defect or interrupt — logs and restarts it. `work`
211466
+ * itself must still handle its own EXPECTED failures (this only supervises
211467
+ * the schedule, and treats every death of it as unexpected).
211468
+ */
211469
+ function runSupervisedRepeat(input) {
211470
+ return gen(function* () {
211471
+ const shutdownSignal = yield* make$39();
211472
+ yield* addFinalizer$2(() => succeed$3(shutdownSignal, void 0));
211473
+ let firstAttempt = true;
211474
+ while (true) {
211475
+ if (!firstAttempt) yield* sleep$4(input.interval);
211476
+ firstAttempt = false;
211477
+ const scheduleFiber = yield* forkChild(repeat(input.work, spaced(input.interval)));
211478
+ const outcome = yield* race(await_(scheduleFiber), _await(shutdownSignal).pipe(as$3("shutdown")));
211479
+ if (outcome === "shutdown") {
211480
+ yield* interrupt$4(scheduleFiber);
211481
+ return;
211482
+ }
211483
+ yield* logError(`${input.label} died unexpectedly — restarting`, { cause: isFailure(outcome) ? pretty(outcome.cause) : "unknown exit" });
211484
+ }
211485
+ });
211486
+ }
211487
+ //#endregion
211202
211488
  //#region src/provider/Layers/ClaudeProvider.ts
211203
211489
  function endpointProfileDisplayNames(settings) {
211204
211490
  return Object.fromEntries(settings.endpointProfiles.filter((p) => p.displayName).map((p) => [p.slug, p.displayName]));
@@ -211700,7 +211986,21 @@ const nativeClaudeProviderStatus = fn$1("nativeClaudeProviderStatus")(function*
211700
211986
  }
211701
211987
  });
211702
211988
  });
211703
- const checkClaudeProviderStatus = fn$1("checkClaudeProviderStatus")(function* (resolveSubscriptionType, resolveSlashCommands, resolveModels) {
211989
+ /**
211990
+ * Adds what the CLI said about its credentials. Every check where
211991
+ * `claude --version` ran and exited 0 reaches here, including one whose
211992
+ * version did not parse; that one has no key to look a probe answer up by, so
211993
+ * only the store is added.
211994
+ */
211995
+ function withClaudeCredentialAnswers(provider, resolve, input) {
211996
+ if (!resolve) return succeed(provider);
211997
+ return resolve(input).pipe(map$3((answers) => ({
211998
+ ...provider,
211999
+ ...answers
212000
+ })));
212001
+ }
212002
+ const checkClaudeProviderStatus = fn$1("checkClaudeProviderStatus")(function* (resolvers = {}) {
212003
+ const { resolveSubscriptionType, resolveSlashCommands, resolveModels, resolveCredentialAnswers } = resolvers;
211704
212004
  const config = getOrElse(yield* serviceOption(ServerConfig), () => ({
211705
212005
  ...DEFAULT_PROVIDER_AUTH_CONFIG,
211706
212006
  forgeClaudeConfigPath: void 0
@@ -211772,19 +212072,24 @@ const checkClaudeProviderStatus = fn$1("checkClaudeProviderStatus")(function* (r
211772
212072
  });
211773
212073
  }
211774
212074
  const executableFallbackModels = modelsForClaudeCliVersion(fallbackModels, parsedVersion);
211775
- if (personalClaudeInVmEnabled()) return yield* nativeClaudeProviderStatus({
212075
+ const credentialAnswersInput = {
212076
+ binaryPath: claudeSettings.binaryPath,
212077
+ version: claudeVersionFromOutput(version.stdout),
212078
+ configDirectory: effectiveConfigPath
212079
+ };
212080
+ if (personalClaudeInVmEnabled()) return yield* withClaudeCredentialAnswers(yield* nativeClaudeProviderStatus({
211776
212081
  enabled: claudeSettings.enabled,
211777
212082
  checkedAt,
211778
212083
  version: parsedVersion,
211779
212084
  configPath: effectiveConfigPath,
211780
212085
  models: executableFallbackModels,
211781
212086
  provenance
211782
- });
212087
+ }), resolveCredentialAnswers, credentialAnswersInput);
211783
212088
  const capabilitiesProbeInput = claudeCapabilitiesProbeInput(claudeSettings.binaryPath, effectiveConfigPath);
211784
212089
  const discovery = yield* resolveClaudeDiscovery(capabilitiesProbeInput, resolveSlashCommands, resolveModels);
211785
212090
  const models = modelsForClaudeCliVersion(providerModelsFromSettings(resolveClaudeProviderModels(discovery.models), PROVIDER$12, claudeSettings.customModels, DEFAULT_CLAUDE_MODEL_CAPABILITIES, endpointProfileDisplayNames(claudeSettings)), parsedVersion);
211786
212091
  const authProbe = yield* runClaudeCommand(["auth", "status"]).pipe(timeoutOption(DEFAULT_TIMEOUT_MS$3), result);
211787
- return buildClaudeProviderFromAuthProbe({
212092
+ return yield* withClaudeCredentialAnswers(buildClaudeProviderFromAuthProbe({
211788
212093
  authProbe,
211789
212094
  authMetadata: yield* resolveClaudeAuthMetadata(authProbe, capabilitiesProbeInput, resolveSubscriptionType),
211790
212095
  checkedAt,
@@ -211793,7 +212098,7 @@ const checkClaudeProviderStatus = fn$1("checkClaudeProviderStatus")(function* (r
211793
212098
  slashCommands: discovery.slashCommands,
211794
212099
  parsedVersion,
211795
212100
  provenance
211796
- });
212101
+ }), resolveCredentialAnswers, credentialAnswersInput);
211797
212102
  });
211798
212103
  const makePendingClaudeProvider = (claudeSettings) => {
211799
212104
  const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -211825,30 +212130,76 @@ const makePendingClaudeProvider = (claudeSettings) => {
211825
212130
  }
211826
212131
  });
211827
212132
  };
211828
- const ClaudeProviderLive = effect(ClaudeProvider, gen(function* () {
211829
- const serverSettings = yield* ServerSettingsService;
211830
- const spawner = yield* ChildProcessSpawner;
211831
- const subscriptionProbeCache = yield* make$51({
211832
- capacity: 4,
211833
- timeToLive: minutes(5),
211834
- lookup: (key) => {
211835
- const [binaryPath, configPath] = JSON.parse(key);
211836
- return probeClaudeCapabilities({
211837
- binaryPath,
211838
- ...configPath ? { configPath } : {}
211839
- });
211840
- }
211841
- });
211842
- const getDiscovery = (input) => get$8(subscriptionProbeCache, JSON.stringify([input.binaryPath, input.configPath]));
211843
- const checkProvider = checkClaudeProviderStatus((input) => getDiscovery(input).pipe(map$3((probe) => probe?.subscriptionType)), (input) => getDiscovery(input).pipe(map$3((probe) => probe?.slashCommands)), (input) => getDiscovery(input).pipe(map$3((probe) => probe?.models))).pipe(provideService(ServerSettingsService, serverSettings), provideService(ChildProcessSpawner, spawner));
211844
- return yield* makeManagedServerProvider({
211845
- getSettings: serverSettings.getSettings.pipe(map$3((settings) => settings.providers.claudeAgent), orDie),
211846
- streamSettings: serverSettings.streamChanges.pipe(map$7((settings) => settings.providers.claudeAgent)),
211847
- haveSettingsChanged: (previous, next) => !equals(previous, next),
211848
- initialSnapshot: makePendingClaudeProvider,
211849
- checkProvider
211850
- });
211851
- }));
212133
+ /**
212134
+ * A refresh can wait behind a check that already holds the provider's refresh
212135
+ * permit before running its own, so it gets two check budgets.
212136
+ */
212137
+ const CREDENTIAL_ANSWER_REFRESH_TIMEOUT_MS = 2 * (CLAUDE_VERSION_PROBE_TIMEOUT_MS + CAPABILITIES_PROBE_TIMEOUT_MS$1 + DEFAULT_TIMEOUT_MS$3);
212138
+ /** The gap after each answer refresh, and the pause before a restart. */
212139
+ const CREDENTIAL_ANSWER_REFRESH_SPACING = seconds(1);
212140
+ /**
212141
+ * Reruns the check when a probe reaches a new or changed answer, so the
212142
+ * inventory carries it without waiting for the next scheduled check. Answers
212143
+ * that arrive during a refresh collapse into one more refresh.
212144
+ *
212145
+ * A refresh that fails, dies or outlives its timeout is logged, and the loop
212146
+ * goes back to waiting for the next answer. The timeout interrupts the check,
212147
+ * which hands back the refresh permit. `runSupervisedRepeat` restarts the loop
212148
+ * after any interrupt that does not come from the scope closing; closing the
212149
+ * scope stops it and drops the subscription.
212150
+ */
212151
+ const refreshOnNewCredentialDirectoryAnswer = fn$1("refreshOnNewCredentialDirectoryAnswer")(function* (credentialDirectoryAnswers, refresh) {
212152
+ const changed = yield* sliding(1);
212153
+ yield* acquireRelease(sync$1(() => credentialDirectoryAnswers.subscribe(() => {
212154
+ offerUnsafe(changed, void 0);
212155
+ })), (unsubscribe) => sync$1(unsubscribe));
212156
+ const refreshOnce = refresh.pipe(timeout$1(CREDENTIAL_ANSWER_REFRESH_TIMEOUT_MS), asVoid, catchCause((cause) => hasInterruptsOnly(cause) ? interrupt$1 : logWarning$1("Claude refresh for a new credential-directory answer failed", { cause: pretty(cause) })));
212157
+ yield* forkScoped(runSupervisedRepeat({
212158
+ label: "Claude credential-directory answer refresh",
212159
+ work: take$1(changed).pipe(andThen(refreshOnce)),
212160
+ interval: CREDENTIAL_ANSWER_REFRESH_SPACING
212161
+ }));
212162
+ });
212163
+ /** The Claude provider. Tests hand in their own answers and store read. */
212164
+ function makeClaudeProviderLive(options = {}) {
212165
+ const credentialDirectoryAnswers = options.credentialDirectoryAnswers ?? claudeCredentialDirectoryAnswers;
212166
+ const resolveCredentialAnswers = makeClaudeCredentialAnswersResolver({
212167
+ ...options,
212168
+ credentialDirectoryAnswers
212169
+ });
212170
+ return effect(ClaudeProvider, gen(function* () {
212171
+ const serverSettings = yield* ServerSettingsService;
212172
+ const spawner = yield* ChildProcessSpawner;
212173
+ const subscriptionProbeCache = yield* make$51({
212174
+ capacity: 4,
212175
+ timeToLive: minutes(5),
212176
+ lookup: (key) => {
212177
+ const [binaryPath, configPath] = JSON.parse(key);
212178
+ return probeClaudeCapabilities({
212179
+ binaryPath,
212180
+ ...configPath ? { configPath } : {}
212181
+ });
212182
+ }
212183
+ });
212184
+ const getDiscovery = (input) => get$8(subscriptionProbeCache, JSON.stringify([input.binaryPath, input.configPath]));
212185
+ const checkProvider = checkClaudeProviderStatus({
212186
+ resolveSubscriptionType: (input) => getDiscovery(input).pipe(map$3((probe) => probe?.subscriptionType)),
212187
+ resolveSlashCommands: (input) => getDiscovery(input).pipe(map$3((probe) => probe?.slashCommands)),
212188
+ resolveModels: (input) => getDiscovery(input).pipe(map$3((probe) => probe?.models)),
212189
+ resolveCredentialAnswers
212190
+ }).pipe(provideService(ServerSettingsService, serverSettings), provideService(ChildProcessSpawner, spawner));
212191
+ const provider = yield* makeManagedServerProvider({
212192
+ getSettings: serverSettings.getSettings.pipe(map$3((settings) => settings.providers.claudeAgent), orDie),
212193
+ streamSettings: serverSettings.streamChanges.pipe(map$7((settings) => settings.providers.claudeAgent)),
212194
+ haveSettingsChanged: (previous, next) => !equals(previous, next),
212195
+ initialSnapshot: makePendingClaudeProvider,
212196
+ checkProvider
212197
+ });
212198
+ yield* refreshOnNewCredentialDirectoryAnswer(credentialDirectoryAnswers, provider.refresh);
212199
+ return provider;
212200
+ }));
212201
+ }
212202
+ const ClaudeProviderLive = makeClaudeProviderLive();
211852
212203
  //#endregion
211853
212204
  //#region src/provider/Services/ClaudeAdapter.ts
211854
212205
  /**
@@ -228212,7 +228563,7 @@ const OpenCodeRuntimeLive = effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(pr
228212
228563
  //#endregion
228213
228564
  //#region src/provider/Layers/OpenCodeAdapter.ts
228214
228565
  const PROVIDER$5 = "opencode";
228215
- const nowIso$3 = map$3(now, formatIso);
228566
+ const nowIso$4 = map$3(now, formatIso);
228216
228567
  /**
228217
228568
  * Map a tagged OpenCodeRuntimeError produced by {@link runOpenCodeSdk} into
228218
228569
  * the adapter-boundary `ProviderAdapterRequestError`. SDK-method-level call
@@ -228239,7 +228590,7 @@ const toProcessError = (threadId, cause) => new ProviderAdapterProcessError({
228239
228590
  });
228240
228591
  const buildEventBase = (input) => gen(function* () {
228241
228592
  const uuid = yield* nextUUIDv4;
228242
- const createdAt = input.createdAt ?? (yield* nowIso$3);
228593
+ const createdAt = input.createdAt ?? (yield* nowIso$4);
228243
228594
  return {
228244
228595
  eventId: EventId.make(uuid),
228245
228596
  provider: PROVIDER$5,
@@ -228398,7 +228749,7 @@ function sessionErrorMessage(error) {
228398
228749
  }
228399
228750
  function updateProviderSession(context, patch, options) {
228400
228751
  return gen(function* () {
228401
- const updatedAt = yield* nowIso$3;
228752
+ const updatedAt = yield* nowIso$4;
228402
228753
  const nextSession = {
228403
228754
  ...context.session,
228404
228755
  ...patch,
@@ -228520,7 +228871,7 @@ function makeOpenCodeAdapter(openCodeSettings, options) {
228520
228871
  if (("properties" in event ? event.properties.sessionID : void 0) !== context.openCodeSessionId) return;
228521
228872
  const turnId = context.activeTurnId;
228522
228873
  yield* writeNativeEventBestEffort(context.session.threadId, {
228523
- observedAt: yield* nowIso$3,
228874
+ observedAt: yield* nowIso$4,
228524
228875
  event: {
228525
228876
  provider: PROVIDER$5,
228526
228877
  threadId: context.session.threadId,
@@ -228836,7 +229187,7 @@ function makeOpenCodeAdapter(openCodeSettings, options) {
228836
229187
  yield* close(started.sessionScope, void_$3).pipe(ignore);
228837
229188
  return raceWinner.session;
228838
229189
  }
228839
- const createdAt = yield* nowIso$3;
229190
+ const createdAt = yield* nowIso$4;
228840
229191
  const session = {
228841
229192
  provider: PROVIDER$5,
228842
229193
  status: "ready",
@@ -235600,7 +235951,7 @@ function filterEvents(events, options) {
235600
235951
  });
235601
235952
  return options.limit === void 0 ? filtered : filtered.slice(-options.limit);
235602
235953
  }
235603
- function nowIso$2() {
235954
+ function nowIso$3() {
235604
235955
  return (/* @__PURE__ */ new Date()).toISOString();
235605
235956
  }
235606
235957
  //#endregion
@@ -235615,7 +235966,7 @@ var BrowserActionEventRecorder = class {
235615
235966
  type: "browser.action",
235616
235967
  action,
235617
235968
  ...this.sessionFields(),
235618
- timestamp: nowIso$2()
235969
+ timestamp: nowIso$3()
235619
235970
  });
235620
235971
  }
235621
235972
  async result(action) {
@@ -235624,7 +235975,7 @@ var BrowserActionEventRecorder = class {
235624
235975
  action,
235625
235976
  ok: true,
235626
235977
  ...this.sessionFields(),
235627
- timestamp: nowIso$2()
235978
+ timestamp: nowIso$3()
235628
235979
  });
235629
235980
  }
235630
235981
  async error(action, error) {
@@ -235633,7 +235984,7 @@ var BrowserActionEventRecorder = class {
235633
235984
  action,
235634
235985
  error: error instanceof Error ? error.message : String(error),
235635
235986
  ...this.sessionFields(),
235636
- timestamp: nowIso$2()
235987
+ timestamp: nowIso$3()
235637
235988
  });
235638
235989
  }
235639
235990
  sessionFields() {
@@ -236154,7 +236505,7 @@ var AgentBrowser = class {
236154
236505
  await this.options.events?.record({
236155
236506
  type: "browser.safety.confirmation.requested",
236156
236507
  ...request,
236157
- createdAt: nowIso$2()
236508
+ createdAt: nowIso$3()
236158
236509
  });
236159
236510
  const result = await this.resolveConfirmation(request);
236160
236511
  const approved = typeof result === "boolean" ? result : result.approved;
@@ -236162,7 +236513,7 @@ var AgentBrowser = class {
236162
236513
  type: "browser.safety.confirmation.resolved",
236163
236514
  approved,
236164
236515
  requestId: request.requestId,
236165
- resolvedAt: nowIso$2(),
236516
+ resolvedAt: nowIso$3(),
236166
236517
  ...this.events.sessionFields()
236167
236518
  });
236168
236519
  if (!approved) throw new BrowserSafetyError(decision.reason ?? "Browser action was not confirmed.", action, {
@@ -281514,7 +281865,7 @@ const DEFAULT_AGENT_BROWSER_BRIDGE_CLOSE_TIMEOUT_MS = 3e4;
281514
281865
  const DEFAULT_AGENT_BROWSER_PLAYWRIGHT_BACKEND_CLOSE_TIMEOUT_MS = 1e4;
281515
281866
  const REMOTE_WEBRTC_UNAVAILABLE_DETAIL = "Remote WebRTC browser display is not available: the current Agent Browser runtime exposes tab control but does not provide a media transport.";
281516
281867
  const REMOTE_WEBRTC_RELAY_REQUIRED_DETAIL = "Remote WebRTC browser display requires a TURN relay in cloud mode. Configure FORGE_BROWSER_WEBRTC_ICE_SERVERS with authenticated turn: or turns: URLs.";
281517
- function nowIso$1() {
281868
+ function nowIso$2() {
281518
281869
  return (/* @__PURE__ */ new Date()).toISOString();
281519
281870
  }
281520
281871
  function positiveTimeoutMs(value) {
@@ -281614,12 +281965,12 @@ function snapshotAfterTimedOutControl(snapshot, command) {
281614
281965
  case "setViewport": return {
281615
281966
  ...snapshot,
281616
281967
  viewport: command.viewport,
281617
- updatedAt: nowIso$1()
281968
+ updatedAt: nowIso$2()
281618
281969
  };
281619
281970
  case "resetViewport": return {
281620
281971
  ...snapshot,
281621
281972
  viewport: DEFAULT_AGENT_BROWSER_VIEWPORT,
281622
- updatedAt: nowIso$1()
281973
+ updatedAt: nowIso$2()
281623
281974
  };
281624
281975
  default: return snapshot;
281625
281976
  }
@@ -282065,7 +282416,7 @@ function toStreamEvent(input) {
282065
282416
  userId: BrowserUserId.make(input.userId),
282066
282417
  agentId: input.agentId,
282067
282418
  ...input.controlState ? { sessionEpoch: input.controlState.sessionEpoch } : {},
282068
- createdAt: nowIso$1()
282419
+ createdAt: nowIso$2()
282069
282420
  };
282070
282421
  switch (input.event.type) {
282071
282422
  case "session.started": return {
@@ -282366,7 +282717,7 @@ function createAgentBrowserHostBridgeManager(bridge, options) {
282366
282717
  environmentId: event.input.environmentId ?? DEFAULT_BROWSER_ENVIRONMENT_ID,
282367
282718
  userId: event.input.userId ?? "local-user",
282368
282719
  agentId: null,
282369
- createdAt: nowIso$1(),
282720
+ createdAt: nowIso$2(),
282370
282721
  type: "display.iceCandidate",
282371
282722
  payload: {
282372
282723
  sessionId: event.input.sessionId,
@@ -287003,46 +287354,6 @@ const makeRuntimeDrainState = gen(function* () {
287003
287354
  var RuntimeDrainState = class extends Service$1()("forge/runtimeDrain/RuntimeDrainState") {};
287004
287355
  const RuntimeDrainStateLive = effect(RuntimeDrainState, makeRuntimeDrainState);
287005
287356
  //#endregion
287006
- //#region src/runtimeDrain/supervisedRepeat.ts
287007
- /**
287008
- * Runs `work` on a fixed schedule, forever, and RESTARTS the schedule itself
287009
- * if it ever dies for any reason other than this scope closing.
287010
- *
287011
- * `Effect.repeat(work, Schedule.spaced(interval)).pipe(Effect.forkScoped)`
287012
- * alone has no restart path. `work` catching its own errors only protects
287013
- * the TICK: an interrupt that lands while `Effect.repeat` is asleep between
287014
- * ticks — which, at any non-trivial interval, is nearly this loop's entire
287015
- * life — happens outside anything `work` wraps, and kills that one fiber
287016
- * outright with nothing logged, because nothing was running to log from.
287017
- * The process this runs in can otherwise be perfectly healthy while the
287018
- * schedule is silently, permanently dead.
287019
- *
287020
- * This runs the schedule on its own child fiber, raced against an explicit
287021
- * shutdown signal fired by a finalizer registered in this scope. Only that
287022
- * signal stops the loop for good; any other death of the schedule fiber —
287023
- * whatever causes it, defect or interrupt — logs and restarts it. `work`
287024
- * itself must still handle its own EXPECTED failures (this only supervises
287025
- * the schedule, and treats every death of it as unexpected).
287026
- */
287027
- function runSupervisedRepeat(input) {
287028
- return gen(function* () {
287029
- const shutdownSignal = yield* make$39();
287030
- yield* addFinalizer$2(() => succeed$3(shutdownSignal, void 0));
287031
- let firstAttempt = true;
287032
- while (true) {
287033
- if (!firstAttempt) yield* sleep$4(input.interval);
287034
- firstAttempt = false;
287035
- const scheduleFiber = yield* forkChild(repeat(input.work, spaced(input.interval)));
287036
- const outcome = yield* race(await_(scheduleFiber), _await(shutdownSignal).pipe(as$3("shutdown")));
287037
- if (outcome === "shutdown") {
287038
- yield* interrupt$4(scheduleFiber);
287039
- return;
287040
- }
287041
- yield* logError(`${input.label} died unexpectedly — restarting`, { cause: isFailure(outcome) ? pretty(outcome.cause) : "unknown exit" });
287042
- }
287043
- });
287044
- }
287045
- //#endregion
287046
287357
  //#region src/runtimeDrain/RuntimeDrainReporter.ts
287047
287358
  const HEARTBEAT_INTERVAL = seconds(30);
287048
287359
  const HEARTBEAT_TICK_TIMEOUT = seconds(60);
@@ -294413,7 +294724,7 @@ function resolveBuildCommitFromEnv(env) {
294413
294724
  * environment descriptor down instead of reporting an honest "unknown".
294414
294725
  */
294415
294726
  function readBakedBuildCommit() {
294416
- return "7695a8221bc664caedd33aa411ac7bcdbb0a3594";
294727
+ return "9243995fc67ca68aa610fabccbf48bba35a5ffd1";
294417
294728
  }
294418
294729
  async function resolveServerBuildCommit(input) {
294419
294730
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -297474,6 +297785,52 @@ function toProjectorDecodeError(eventType) {
297474
297785
  });
297475
297786
  }
297476
297787
  //#endregion
297788
+ //#region src/orchestration/deciderEventBase.ts
297789
+ const nowIso$1 = () => (/* @__PURE__ */ new Date()).toISOString();
297790
+ const defaultMetadata = {
297791
+ eventId: crypto.randomUUID(),
297792
+ aggregateKind: "thread",
297793
+ aggregateId: "",
297794
+ occurredAt: nowIso$1(),
297795
+ commandId: null,
297796
+ causationEventId: null,
297797
+ correlationId: null,
297798
+ metadata: {}
297799
+ };
297800
+ function withEventBase(input) {
297801
+ return {
297802
+ ...defaultMetadata,
297803
+ eventId: crypto.randomUUID(),
297804
+ aggregateKind: input.aggregateKind,
297805
+ aggregateId: input.aggregateId,
297806
+ occurredAt: input.occurredAt,
297807
+ commandId: input.commandId,
297808
+ correlationId: input.commandId,
297809
+ metadata: input.metadata ?? {}
297810
+ };
297811
+ }
297812
+ //#endregion
297813
+ //#region src/orchestration/projectScriptMutation.ts
297814
+ function unchanged(a, b) {
297815
+ return a !== void 0 && a.id === b.id && a.name === b.name && a.command === b.command && a.icon === b.icon && a.runOnWorktreeCreate === b.runOnWorktreeCreate;
297816
+ }
297817
+ const applyProjectScriptMutation = fn$1("applyProjectScriptMutation")(function* (scripts, command) {
297818
+ const mutation = command.mutation;
297819
+ const id = mutation.kind === "add" ? mutation.script.id : mutation.expected.id;
297820
+ const existing = scripts.find((script) => script.id === id);
297821
+ if (mutation.kind === "add" ? existing !== void 0 : !unchanged(existing, mutation.expected)) return yield* new OrchestrationCommandInvariantError({
297822
+ commandType: command.type,
297823
+ detail: "This action changed in another window. Reopen it and try again."
297824
+ });
297825
+ if (mutation.kind === "add") return [...scripts, mutation.script];
297826
+ if (mutation.kind === "remove") return scripts.filter((script) => script.id !== id);
297827
+ if (mutation.script.id !== id) return yield* new OrchestrationCommandInvariantError({
297828
+ commandType: command.type,
297829
+ detail: "An action's ID cannot change during an update."
297830
+ });
297831
+ return scripts.map((script) => script.id === id ? mutation.script : script);
297832
+ });
297833
+ //#endregion
297477
297834
  //#region src/orchestration/turnAccountSelection.ts
297478
297835
  /** A per-turn account choice remains local to the thread and cannot change its provider. */
297479
297836
  function turnAccountSelection(current, requested) {
@@ -297530,28 +297887,6 @@ function requireThreadAbsent(input) {
297530
297887
  //#endregion
297531
297888
  //#region src/orchestration/decider.ts
297532
297889
  const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
297533
- const defaultMetadata = {
297534
- eventId: crypto.randomUUID(),
297535
- aggregateKind: "thread",
297536
- aggregateId: "",
297537
- occurredAt: nowIso(),
297538
- commandId: null,
297539
- causationEventId: null,
297540
- correlationId: null,
297541
- metadata: {}
297542
- };
297543
- function withEventBase(input) {
297544
- return {
297545
- ...defaultMetadata,
297546
- eventId: crypto.randomUUID(),
297547
- aggregateKind: input.aggregateKind,
297548
- aggregateId: input.aggregateId,
297549
- occurredAt: input.occurredAt,
297550
- commandId: input.commandId,
297551
- correlationId: input.commandId,
297552
- metadata: input.metadata ?? {}
297553
- };
297554
- }
297555
297890
  function requireOrdinaryMessageId(commandType, messageId) {
297556
297891
  return isReservedReplicaSourceMessageId(messageId) ? fail(new OrchestrationCommandInvariantError({
297557
297892
  commandType,
@@ -297604,6 +297939,22 @@ const decideOrchestrationCommand = fn$1("decideOrchestrationCommand")(function*
297604
297939
  updatedAt: command.createdAt
297605
297940
  }
297606
297941
  };
297942
+ case "project.script.update": {
297943
+ const scripts = yield* applyProjectScriptMutation((yield* requireProject({
297944
+ readModel,
297945
+ command,
297946
+ projectId: command.projectId
297947
+ })).scripts, command);
297948
+ return yield* decideOrchestrationCommand({
297949
+ readModel,
297950
+ command: {
297951
+ type: "project.meta.update",
297952
+ commandId: command.commandId,
297953
+ projectId: command.projectId,
297954
+ scripts
297955
+ }
297956
+ });
297957
+ }
297607
297958
  case "project.meta.update": {
297608
297959
  yield* requireProject({
297609
297960
  readModel,
@@ -298890,6 +299241,7 @@ function commandToAggregateRef(command) {
298890
299241
  switch (command.type) {
298891
299242
  case "project.create":
298892
299243
  case "project.meta.update":
299244
+ case "project.script.update":
298893
299245
  case "project.delete": return {
298894
299246
  aggregateKind: "project",
298895
299247
  aggregateId: command.projectId