@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.cjs CHANGED
@@ -16544,7 +16544,7 @@ function makeNetServiceShape(createServer = node_net.createServer) {
16544
16544
  }
16545
16545
  //#endregion
16546
16546
  //#region ../../packages/shared/src/releaseInfo.generated.ts
16547
- const FORGE_RELEASE_VERSION = "0.12.0";
16547
+ const FORGE_RELEASE_VERSION = "0.13.1";
16548
16548
  //#endregion
16549
16549
  //#region ../../packages/shared/src/hostProcess.ts
16550
16550
  /**
@@ -18654,6 +18654,43 @@ const TerminalError = require_Schema$1.Union([
18654
18654
  TerminalSignalError
18655
18655
  ]);
18656
18656
  //#endregion
18657
+ //#region ../../packages/contracts/src/projectScriptCommands.ts
18658
+ const ProjectScriptIcon = require_Schema$1.Literals([
18659
+ "play",
18660
+ "test",
18661
+ "lint",
18662
+ "configure",
18663
+ "build",
18664
+ "debug"
18665
+ ]);
18666
+ const ProjectScript = require_Schema$1.Struct({
18667
+ id: TrimmedNonEmptyString,
18668
+ name: TrimmedNonEmptyString,
18669
+ command: TrimmedNonEmptyString,
18670
+ icon: ProjectScriptIcon,
18671
+ runOnWorktreeCreate: require_Schema$1.Boolean
18672
+ });
18673
+ const ProjectScriptUpdateCommand = require_Schema$1.Struct({
18674
+ type: require_Schema$1.Literal("project.script.update"),
18675
+ commandId: CommandId,
18676
+ projectId: ProjectId,
18677
+ mutation: require_Schema$1.Union([
18678
+ require_Schema$1.Struct({
18679
+ kind: require_Schema$1.Literal("add"),
18680
+ script: ProjectScript
18681
+ }),
18682
+ require_Schema$1.Struct({
18683
+ kind: require_Schema$1.Literal("update"),
18684
+ script: ProjectScript,
18685
+ expected: ProjectScript
18686
+ }),
18687
+ require_Schema$1.Struct({
18688
+ kind: require_Schema$1.Literal("remove"),
18689
+ expected: ProjectScript
18690
+ })
18691
+ ])
18692
+ });
18693
+ //#endregion
18657
18694
  //#region ../../packages/contracts/src/model.ts
18658
18695
  const CODEX_REASONING_EFFORT_OPTIONS = [
18659
18696
  "ultra",
@@ -19313,21 +19350,6 @@ const UploadChatImageAttachment = require_Schema$1.Struct({
19313
19350
  });
19314
19351
  const ChatAttachment = require_Schema$1.Union([ChatImageAttachment]);
19315
19352
  const UploadChatAttachment = require_Schema$1.Union([UploadChatImageAttachment]);
19316
- const ProjectScriptIcon = require_Schema$1.Literals([
19317
- "play",
19318
- "test",
19319
- "lint",
19320
- "configure",
19321
- "build",
19322
- "debug"
19323
- ]);
19324
- const ProjectScript = require_Schema$1.Struct({
19325
- id: TrimmedNonEmptyString,
19326
- name: TrimmedNonEmptyString,
19327
- command: TrimmedNonEmptyString,
19328
- icon: ProjectScriptIcon,
19329
- runOnWorktreeCreate: require_Schema$1.Boolean
19330
- });
19331
19353
  /**
19332
19354
  * Availability of the project's `workspaceRoot` on the server's filesystem.
19333
19355
  *
@@ -19958,6 +19980,7 @@ const ThreadSessionStopCommand = require_Schema$1.Struct({
19958
19980
  const DispatchableClientOrchestrationCommand = require_Schema$1.Union([
19959
19981
  ProjectCreateCommand,
19960
19982
  ProjectMetaUpdateCommand,
19983
+ ProjectScriptUpdateCommand,
19961
19984
  ProjectDeleteCommand,
19962
19985
  ThreadCreateCommand,
19963
19986
  ThreadDeleteCommand,
@@ -19979,6 +20002,7 @@ const DispatchableClientOrchestrationCommand = require_Schema$1.Union([
19979
20002
  const ClientOrchestrationCommand = require_Schema$1.Union([
19980
20003
  ProjectCreateCommand,
19981
20004
  ProjectMetaUpdateCommand,
20005
+ ProjectScriptUpdateCommand,
19982
20006
  ProjectDeleteCommand,
19983
20007
  ThreadCreateCommand,
19984
20008
  ThreadDeleteCommand,
@@ -22623,6 +22647,27 @@ const ProviderUnavailableDetail = require_Schema$1.Literals([
22623
22647
  "delivery-pending",
22624
22648
  "reconnect-required"
22625
22649
  ]);
22650
+ /**
22651
+ * Whether the provider CLI honours the credential-directory variable, as this
22652
+ * Environment's probe found. `inconclusive` is a probe that ran and could not
22653
+ * tell. It is not `unsupported`, and it is never reported as `supported`. The
22654
+ * members are CMMD's `forgeRuntimeInventoryCredentialDirectoryAnswers`.
22655
+ */
22656
+ const ProviderCredentialDirectoryAnswer = require_Schema$1.Literals([
22657
+ "supported",
22658
+ "unsupported",
22659
+ "inconclusive"
22660
+ ]);
22661
+ /**
22662
+ * Which Credential Store the provider CLI uses on this Environment: its file,
22663
+ * the operating system's store, or `unknown` when it cannot be read the way
22664
+ * the CLI reads it. The members are CMMD's `forgeRuntimeInventoryCredentialStores`.
22665
+ */
22666
+ const ProviderCredentialStore = require_Schema$1.Literals([
22667
+ "file",
22668
+ "os",
22669
+ "unknown"
22670
+ ]);
22626
22671
  const ServerProviderModel = require_Schema$1.Struct({
22627
22672
  slug: TrimmedNonEmptyString,
22628
22673
  name: TrimmedNonEmptyString,
@@ -22659,6 +22704,8 @@ const ServerProvider = require_Schema$1.Struct({
22659
22704
  availability: require_Schema$1.optional(ProviderAvailability),
22660
22705
  unavailableReason: require_Schema$1.optional(ProviderUnavailableReason),
22661
22706
  unavailableDetail: require_Schema$1.optional(ProviderUnavailableDetail),
22707
+ credentialDirectory: require_Schema$1.optional(ProviderCredentialDirectoryAnswer),
22708
+ credentialStore: require_Schema$1.optional(ProviderCredentialStore),
22662
22709
  slashCommands: require_Schema$1.ArraySchema(ServerProviderSlashCommand).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed([]))),
22663
22710
  skills: require_Schema$1.ArraySchema(ServerProviderSkill).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed([])))
22664
22711
  });
@@ -26697,12 +26744,12 @@ function encodeCmmdRuntimeThreadWorkspaceMetadata(workspace) {
26697
26744
  }
26698
26745
  //#endregion
26699
26746
  //#region ../../packages/contracts/src/cmmdRuntimeToolActivity.ts
26700
- function isRecord$22(value) {
26747
+ function isRecord$23(value) {
26701
26748
  return value !== null && typeof value === "object" && !Array.isArray(value);
26702
26749
  }
26703
26750
  function encodeCmmdRuntimeToolActivityPayload(kind, payload) {
26704
26751
  return {
26705
- ...isRecord$22(payload) ? payload : { sourcePayload: payload },
26752
+ ...isRecord$23(payload) ? payload : { sourcePayload: payload },
26706
26753
  forgeToolActivity: {
26707
26754
  schemaVersion: 1,
26708
26755
  source: "native",
@@ -30702,13 +30749,13 @@ async function completePendingDesktopRelayIdentityRotation(input) {
30702
30749
  }
30703
30750
  //#endregion
30704
30751
  //#region ../../packages/relay-host/src/internal/desktopRelayJws.ts
30705
- function isRecord$21(value) {
30752
+ function isRecord$22(value) {
30706
30753
  return typeof value === "object" && value !== null && !Array.isArray(value);
30707
30754
  }
30708
30755
  function decodeBase64UrlJson$1(value) {
30709
30756
  try {
30710
30757
  const decoded = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
30711
- return isRecord$21(decoded) ? decoded : null;
30758
+ return isRecord$22(decoded) ? decoded : null;
30712
30759
  } catch {
30713
30760
  return null;
30714
30761
  }
@@ -30862,11 +30909,11 @@ var DesktopRelayControlPlaneError = class extends Error {
30862
30909
  this.name = "DesktopRelayControlPlaneError";
30863
30910
  }
30864
30911
  };
30865
- function isRecord$20(value) {
30912
+ function isRecord$21(value) {
30866
30913
  return typeof value === "object" && value !== null && !Array.isArray(value);
30867
30914
  }
30868
30915
  function parseErrorPayload(value) {
30869
- return isRecord$20(value) ? value : {};
30916
+ return isRecord$21(value) ? value : {};
30870
30917
  }
30871
30918
  function messageFromErrorPayload(payload, fallback) {
30872
30919
  return typeof payload.error === "string" && payload.error.trim().length > 0 ? payload.error.trim() : fallback;
@@ -30964,7 +31011,7 @@ async function postControlPlane(input) {
30964
31011
  const errorPayload = parseErrorPayload(payload);
30965
31012
  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);
30966
31013
  }
30967
- if (!isRecord$20(payload) || !("data" in payload)) throw new DesktopRelayControlPlaneError("CONTROL_PLANE_INVALID_RESPONSE", "CMMD returned an invalid Forge relay response.", false);
31014
+ if (!isRecord$21(payload) || !("data" in payload)) throw new DesktopRelayControlPlaneError("CONTROL_PLANE_INVALID_RESPONSE", "CMMD returned an invalid Forge relay response.", false);
30968
31015
  return payload.data;
30969
31016
  }
30970
31017
  function resolveRegisteredDevice(input) {
@@ -31834,6 +31881,10 @@ const relayCommandPolicyByType = {
31834
31881
  requiredScope: "project.write",
31835
31882
  deliveryClass: "idempotent-command"
31836
31883
  },
31884
+ "project.script.update": {
31885
+ requiredScope: "project.write",
31886
+ deliveryClass: "idempotent-command"
31887
+ },
31837
31888
  "project.delete": {
31838
31889
  requiredScope: "project.write",
31839
31890
  deliveryClass: "idempotent-command"
@@ -65813,7 +65864,7 @@ function normalizeNumberish(value) {
65813
65864
  }
65814
65865
  //#endregion
65815
65866
  //#region package.json
65816
- var version$1 = "0.12.0";
65867
+ var version$1 = "0.13.1";
65817
65868
  //#endregion
65818
65869
  //#region src/sentry.ts
65819
65870
  const SERVER_APP_NAME = "forge-server";
@@ -71377,12 +71428,12 @@ function resolveDesktopRelayControlPlaneTarget(path, configuredRuntimeId) {
71377
71428
  code: "RELAY_OPERATION_DENIED"
71378
71429
  };
71379
71430
  }
71380
- function isRecord$19(value) {
71431
+ function isRecord$20(value) {
71381
71432
  return typeof value === "object" && value !== null && !Array.isArray(value);
71382
71433
  }
71383
71434
  function relayControlPlaneBodyMatchesRuntime(path, body, configuredRuntimeId) {
71384
71435
  if (path !== "/api/forge/context/devices/register") return true;
71385
- if (!isRecord$19(body) || !isRecord$19(body.runtime)) return false;
71436
+ if (!isRecord$20(body) || !isRecord$20(body.runtime)) return false;
71386
71437
  return body.runtime.runtimeId === configuredRuntimeId;
71387
71438
  }
71388
71439
  /** Matches the client id `connect login` used, so CMMD accepts the refresh. */
@@ -71397,7 +71448,7 @@ function forbiddenResponse() {
71397
71448
  return errorResponse(403, "RELAY_BRIDGE_FORBIDDEN", "Desktop relay bridge access denied.");
71398
71449
  }
71399
71450
  function relayProxyErrorResponse(error) {
71400
- if (error instanceof CmmdProxyRequestError && isRecord$19(error.body)) {
71451
+ if (error instanceof CmmdProxyRequestError && isRecord$20(error.body)) {
71401
71452
  const code = typeof error.body.code === "string" ? error.body.code : error.code;
71402
71453
  const message = typeof error.body.error === "string" ? error.body.error : error.detail;
71403
71454
  return errorResponse(error.status >= 500 ? 502 : error.status, code, message ?? "CMMD relay coordination failed.");
@@ -93773,7 +93824,7 @@ const REPLICA_THREAD_STATUSES = [
93773
93824
  ];
93774
93825
  //#endregion
93775
93826
  //#region src/runtimeReplication/replicaDecodeModel.ts
93776
- function isRecord$18(value) {
93827
+ function isRecord$19(value) {
93777
93828
  return typeof value === "object" && value !== null && !Array.isArray(value);
93778
93829
  }
93779
93830
  function isNonBlankString(value) {
@@ -93793,7 +93844,7 @@ function nullableInteger(value) {
93793
93844
  return value === null || typeof value === "number" && Number.isSafeInteger(value);
93794
93845
  }
93795
93846
  function unwrapEnvelope(payload, expectedDomain) {
93796
- if (!isRecord$18(payload) || !isRecord$18(payload.meta) || !isRecord$18(payload.data)) return null;
93847
+ if (!isRecord$19(payload) || !isRecord$19(payload.meta) || !isRecord$19(payload.data)) return null;
93797
93848
  const { domain, timestamp, version } = payload.meta;
93798
93849
  if (!isNonBlankString(domain) || expectedDomain !== void 0 && domain !== expectedDomain || version !== "1.0" || !isIsoDateTime(timestamp)) return null;
93799
93850
  return payload.data;
@@ -93806,12 +93857,12 @@ function hasValidRuntimeIdentity(value) {
93806
93857
  ].includes(String(value.runtimeKind)) && typeof value.displayName === "string" && typeof value.status === "string";
93807
93858
  }
93808
93859
  function decodeRuntime$1(value) {
93809
- if (!isRecord$18(value) || !hasValidRuntimeIdentity(value)) return null;
93810
- 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;
93860
+ if (!isRecord$19(value) || !hasValidRuntimeIdentity(value)) return null;
93861
+ 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;
93811
93862
  return value;
93812
93863
  }
93813
93864
  function decodeProject(value) {
93814
- if (!isRecord$18(value) || !isNonBlankString(value.projectId) || typeof value.title !== "string" || !nullableString(value.githubRepoFullName) || !nullableString(value.branch)) return null;
93865
+ if (!isRecord$19(value) || !isNonBlankString(value.projectId) || typeof value.title !== "string" || !nullableString(value.githubRepoFullName) || !nullableString(value.branch)) return null;
93815
93866
  return {
93816
93867
  projectId: value.projectId,
93817
93868
  title: value.title,
@@ -93820,11 +93871,11 @@ function decodeProject(value) {
93820
93871
  };
93821
93872
  }
93822
93873
  function decodeThread$1(value) {
93823
- 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;
93874
+ 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;
93824
93875
  return value;
93825
93876
  }
93826
93877
  function decodeSummary(value) {
93827
- if (!isRecord$18(value) || !nullableString(value.conversationId)) return null;
93878
+ if (!isRecord$19(value) || !nullableString(value.conversationId)) return null;
93828
93879
  const project = decodeProject(value.project);
93829
93880
  const thread = decodeThread$1(value.thread);
93830
93881
  return project === null || thread === null ? null : {
@@ -93935,7 +93986,7 @@ function isNonNegativeInt(value) {
93935
93986
  /** `usage` is null for an Unreported turn, otherwise exactly the five buckets. */
93936
93987
  function isValidTurnUsageTokens(value) {
93937
93988
  if (value === null) return true;
93938
- if (!isRecord$18(value)) return false;
93989
+ if (!isRecord$19(value)) return false;
93939
93990
  return turnUsageTokenKeys.every((key) => isNonNegativeInt(value[key])) && Object.keys(value).every((key) => turnUsageTokenKeys.includes(key));
93940
93991
  }
93941
93992
  /** The row itself: no text, no status, and the turn it belongs to. */
@@ -93944,7 +93995,7 @@ function hasValidTurnUsageEnvelope(value) {
93944
93995
  }
93945
93996
  /** What the row records: who ran it, what it spent, and what it says of itself. */
93946
93997
  function hasValidTurnUsageRecord(payload) {
93947
- return isRecord$18(payload) && isNonBlankString(payload.provider) && isNonBlankString(payload.model) && "usage" in payload && isValidTurnUsageTokens(payload.usage) && hasValidTurnUsageAttribution(payload);
93998
+ return isRecord$19(payload) && isNonBlankString(payload.provider) && isNonBlankString(payload.model) && "usage" in payload && isValidTurnUsageTokens(payload.usage) && hasValidTurnUsageAttribution(payload);
93948
93999
  }
93949
94000
  /** A turn_usage row: no text, no status, and its record in `payload`. */
93950
94001
  function hasValidTurnUsagePayload(value) {
@@ -93981,7 +94032,7 @@ function hasValidEventPayload(value) {
93981
94032
  return isNonBlankString(value.text) && value.status === null;
93982
94033
  }
93983
94034
  function decodeEvent$1(value) {
93984
- if (!isRecord$18(value) || !hasValidEventFields(value) || !hasValidEventPayload(value)) return null;
94035
+ if (!isRecord$19(value) || !hasValidEventFields(value) || !hasValidEventPayload(value)) return null;
93985
94036
  return value;
93986
94037
  }
93987
94038
  function decodeEvents(data) {
@@ -94002,11 +94053,11 @@ function decodeReplicaThreadDetail(payload) {
94002
94053
  const thread = decodeThread$1(data.thread);
94003
94054
  const reachability = decodeReachability(data.reachability);
94004
94055
  const events = decodeEvents(data);
94005
- if (runtime === null || project === null || thread === null || reachability === null || events === null || !isRecord$18(data.execution) || typeof data.execution.available !== "boolean" || ![
94056
+ if (runtime === null || project === null || thread === null || reachability === null || events === null || !isRecord$19(data.execution) || typeof data.execution.available !== "boolean" || ![
94006
94057
  null,
94007
94058
  "runtime_offline",
94008
94059
  "runtime_unavailable"
94009
- ].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;
94060
+ ].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;
94010
94061
  return {
94011
94062
  conversationId: data.conversationId,
94012
94063
  runtime,
@@ -94927,7 +94978,7 @@ function resolveRuntimeReplicationRuntimeId(config) {
94927
94978
  }
94928
94979
  //#endregion
94929
94980
  //#region src/usage/providerTokenBuckets.ts
94930
- function isRecord$17(value) {
94981
+ function isRecord$18(value) {
94931
94982
  return typeof value === "object" && value !== null && !Array.isArray(value);
94932
94983
  }
94933
94984
  function count(record, ...keys) {
@@ -94948,7 +94999,7 @@ function reportedOrNull(tokens) {
94948
94999
  * the app server sends camelCase.
94949
95000
  */
94950
95001
  function codexTokenBuckets(value) {
94951
- if (!isRecord$17(value)) return null;
95002
+ if (!isRecord$18(value)) return null;
94952
95003
  const inputTokens = count(value, "input_tokens", "inputTokens");
94953
95004
  const cachedInputTokens = count(value, "cached_input_tokens", "cachedInputTokens");
94954
95005
  const cacheCreationTokens = count(value, "cache_write_input_tokens", "cacheWriteInputTokens");
@@ -94967,7 +95018,7 @@ function codexTokenBuckets(value) {
94967
95018
  * tokens into output and does not break them out, so reasoning stays zero.
94968
95019
  */
94969
95020
  function claudeTokenBuckets(value) {
94970
- if (!isRecord$17(value)) return null;
95021
+ if (!isRecord$18(value)) return null;
94971
95022
  return reportedOrNull({
94972
95023
  uncachedInputTokens: count(value, "input_tokens", "inputTokens"),
94973
95024
  cachedInputTokens: count(value, "cache_read_input_tokens", "cacheReadInputTokens"),
@@ -95861,6 +95912,20 @@ function publishedAvailability(provider) {
95861
95912
  ...detail === void 0 ? {} : { unavailableDetail: detail }
95862
95913
  };
95863
95914
  }
95915
+ /**
95916
+ * What the Claude CLI said about its credentials, beside the version it said
95917
+ * it for. CMMD keeps each Claude version's answers, so these go on the Claude
95918
+ * entry only. Each is copied only when the snapshot has an answer: absent means
95919
+ * the runtime has not answered yet, and inventing a value would hide that.
95920
+ */
95921
+ function claudeCredentialAnswersToPublish(provider) {
95922
+ if (provider.provider !== "claudeAgent") return {};
95923
+ const { credentialDirectory, credentialStore } = provider;
95924
+ return {
95925
+ ...credentialDirectory === void 0 ? {} : { credentialDirectory },
95926
+ ...credentialStore === void 0 ? {} : { credentialStore }
95927
+ };
95928
+ }
95864
95929
  function serializeProvider(provider) {
95865
95930
  const models = [];
95866
95931
  for (const model of provider.models) {
@@ -95873,7 +95938,8 @@ function serializeProvider(provider) {
95873
95938
  models,
95874
95939
  ...publishedAvailability(provider),
95875
95940
  version: cappedText(provider.version, VERSION_MAX_LENGTH),
95876
- binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH)
95941
+ binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH),
95942
+ ...claudeCredentialAnswersToPublish(provider)
95877
95943
  };
95878
95944
  }
95879
95945
  function serializeProviderCatalog(providers) {
@@ -95894,7 +95960,7 @@ const replicaTurnStatuses = new Set([
95894
95960
  "cancelled",
95895
95961
  "interrupted"
95896
95962
  ]);
95897
- function isRecord$16(value) {
95963
+ function isRecord$17(value) {
95898
95964
  return typeof value === "object" && value !== null && !Array.isArray(value);
95899
95965
  }
95900
95966
  function replicaSourceSequence(value) {
@@ -95907,7 +95973,7 @@ function replicaSourceTurn(sourceTurnId) {
95907
95973
  return typeof sourceTurnId === "string" && sourceTurnId.length > 0 ? { turnId: sourceTurnId } : {};
95908
95974
  }
95909
95975
  function replicaActivitySource(activity) {
95910
- if (!activity.kind.startsWith("replica.") || !isRecord$16(activity.payload)) return null;
95976
+ if (!activity.kind.startsWith("replica.") || !isRecord$17(activity.payload)) return null;
95911
95977
  const sourceSequence = replicaSourceSequence(activity.sequence);
95912
95978
  const eventId = replicaSourceEventId(activity.payload.sourceEventId);
95913
95979
  if (sourceSequence === null || eventId === null) return null;
@@ -95920,7 +95986,7 @@ function replicaActivitySource(activity) {
95920
95986
  }
95921
95987
  function replicaSummaryEvent(activity, source) {
95922
95988
  if (activity.kind !== "replica.activity_summary" && activity.kind !== "replica.error_summary") return null;
95923
- const sourceText = isRecord$16(activity.payload) ? activity.payload.text : null;
95989
+ const sourceText = isRecord$17(activity.payload) ? activity.payload.text : null;
95924
95990
  return {
95925
95991
  sourceSequence: source.sourceSequence,
95926
95992
  event: {
@@ -95933,7 +95999,7 @@ function replicaSummaryEvent(activity, source) {
95933
95999
  };
95934
96000
  }
95935
96001
  function replicaTurnEvent(activity, source) {
95936
- const status = isRecord$16(activity.payload) ? activity.payload.status : null;
96002
+ const status = isRecord$17(activity.payload) ? activity.payload.status : null;
95937
96003
  if (activity.kind !== "replica.turn_status" || !replicaTurnStatuses.has(status)) return null;
95938
96004
  return {
95939
96005
  sourceSequence: source.sourceSequence,
@@ -95947,7 +96013,7 @@ function replicaTurnEvent(activity, source) {
95947
96013
  };
95948
96014
  }
95949
96015
  function replicaToolCallEvent(activity, source) {
95950
- if (activity.kind !== "replica.tool_call" || !isRecord$16(activity.payload)) return null;
96016
+ if (activity.kind !== "replica.tool_call" || !isRecord$17(activity.payload)) return null;
95951
96017
  const sourceText = activity.payload.text;
95952
96018
  const status = activity.payload.status;
95953
96019
  if (typeof sourceText !== "string" || typeof status !== "string") return null;
@@ -95965,9 +96031,9 @@ function replicaToolCallEvent(activity, source) {
95965
96031
  };
95966
96032
  }
95967
96033
  function replicaTurnUsageEvent(activity, source) {
95968
- if (activity.kind !== "replica.turn_usage" || !isRecord$16(activity.payload)) return null;
96034
+ if (activity.kind !== "replica.turn_usage" || !isRecord$17(activity.payload)) return null;
95969
96035
  const usage = activity.payload.payload;
95970
- if (!("turnId" in source.turn) || !isRecord$16(usage)) return null;
96036
+ if (!("turnId" in source.turn) || !isRecord$17(usage)) return null;
95971
96037
  const provider = usage.provider;
95972
96038
  const model = usage.model;
95973
96039
  if (typeof provider !== "string" || typeof model !== "string" || !("usage" in usage)) return null;
@@ -96019,6 +96085,10 @@ function threadStatus(thread) {
96019
96085
  if (thread.latestTurn) return "running";
96020
96086
  return "active";
96021
96087
  }
96088
+ function threadProviderAccountId(thread) {
96089
+ const selection = thread.modelSelection;
96090
+ return selection && "providerAccountId" in selection ? selection.providerAccountId ?? null : null;
96091
+ }
96022
96092
  function serializeRuntimeInventory(snapshot, providerInventory) {
96023
96093
  const activeProjects = snapshot.projects.filter((project) => project.availability !== "missing");
96024
96094
  const projectIds = new Set(activeProjects.map((project) => project.id));
@@ -96049,6 +96119,7 @@ function serializeRuntimeInventory(snapshot, providerInventory) {
96049
96119
  status: threadStatus(thread),
96050
96120
  model: thread.modelSelection?.model ?? null,
96051
96121
  provider: thread.modelSelection?.provider ?? null,
96122
+ providerAccountId: threadProviderAccountId(thread),
96052
96123
  branch: thread.branch,
96053
96124
  lastActivityAt: thread.latestUserMessageAt ?? thread.updatedAt,
96054
96125
  kind: thread.kind,
@@ -96775,9 +96846,9 @@ function isUsageSessionLookupRuntimeNotFound(error) {
96775
96846
  async function isRuntimeNotFoundResponse(response) {
96776
96847
  if (response.status !== 404) return false;
96777
96848
  const body = await response.json().catch(() => null);
96778
- return isRecord$15(body) && body["code"] === "RUNTIME_NOT_FOUND";
96849
+ return isRecord$16(body) && body["code"] === "RUNTIME_NOT_FOUND";
96779
96850
  }
96780
- function isRecord$15(value) {
96851
+ function isRecord$16(value) {
96781
96852
  return typeof value === "object" && value !== null;
96782
96853
  }
96783
96854
  /**
@@ -96789,11 +96860,11 @@ function isRecord$15(value) {
96789
96860
  * no count on the page saying so.
96790
96861
  */
96791
96862
  function decodeHeldSessions(body) {
96792
- const held = isRecord$15(body) && isRecord$15(body["data"]) ? body["data"]["held"] : void 0;
96863
+ const held = isRecord$16(body) && isRecord$16(body["data"]) ? body["data"]["held"] : void 0;
96793
96864
  if (!Array.isArray(held)) throw new Error("The usage session lookup returned an invalid body.");
96794
96865
  return held.map((row) => {
96795
- const providerSessionId = isRecord$15(row) ? row["providerSessionId"] : void 0;
96796
- const runtimeId = isRecord$15(row) ? row["runtimeId"] : void 0;
96866
+ const providerSessionId = isRecord$16(row) ? row["providerSessionId"] : void 0;
96867
+ const runtimeId = isRecord$16(row) ? row["runtimeId"] : void 0;
96797
96868
  if (typeof providerSessionId !== "string" || typeof runtimeId !== "string") throw new Error("The usage session lookup returned an invalid held row.");
96798
96869
  return {
96799
96870
  providerSessionId,
@@ -99286,7 +99357,7 @@ var BrowserManager = class extends require_Schema$1.Service()("forge/browser/Ser
99286
99357
  const DEFAULT_PORT_MIN = 5200;
99287
99358
  const DEFAULT_PORT_MAX = 5999;
99288
99359
  var ThreadDevRuntime = class extends require_Schema$1.Service()("forge/threadDevRuntime/ThreadDevRuntime") {};
99289
- function nowIso$4() {
99360
+ function nowIso$5() {
99290
99361
  return (/* @__PURE__ */ new Date()).toISOString();
99291
99362
  }
99292
99363
  function previewUrlForPort(port) {
@@ -99313,7 +99384,7 @@ const ThreadDevRuntimeLive = require_Schema$1.effect(ThreadDevRuntime, require_S
99313
99384
  allocatedPort: port,
99314
99385
  status: "allocated",
99315
99386
  previewUrl: previewUrlForPort(port),
99316
- updatedAt: nowIso$4()
99387
+ updatedAt: nowIso$5()
99317
99388
  };
99318
99389
  yield* require_HttpServer.update(byThreadId, (current) => {
99319
99390
  const next = new Map(current);
@@ -99335,7 +99406,7 @@ const ThreadDevRuntimeLive = require_Schema$1.effect(ThreadDevRuntime, require_S
99335
99406
  next.set(input.threadId, {
99336
99407
  ...existing,
99337
99408
  status: "released",
99338
- updatedAt: nowIso$4()
99409
+ updatedAt: nowIso$5()
99339
99410
  });
99340
99411
  return next;
99341
99412
  });
@@ -99361,7 +99432,7 @@ const ThreadDevRuntimeLive = require_Schema$1.effect(ThreadDevRuntime, require_S
99361
99432
  ...patch.terminalId !== void 0 ? { terminalId: patch.terminalId } : {},
99362
99433
  ...patch.previewUrl !== void 0 ? { previewUrl: patch.previewUrl } : {},
99363
99434
  ...patch.tunnelId !== void 0 ? { tunnelId: patch.tunnelId } : {},
99364
- updatedAt: nowIso$4()
99435
+ updatedAt: nowIso$5()
99365
99436
  };
99366
99437
  yield* require_HttpServer.update(byThreadId, (current) => {
99367
99438
  const next = new Map(current);
@@ -99589,7 +99660,7 @@ function emptyConfig(source, path) {
99589
99660
  issues: []
99590
99661
  };
99591
99662
  }
99592
- function isRecord$14(value) {
99663
+ function isRecord$15(value) {
99593
99664
  return typeof value === "object" && value !== null && !Array.isArray(value);
99594
99665
  }
99595
99666
  function stringArray(value) {
@@ -99617,7 +99688,7 @@ function terminalsFromValue(value) {
99617
99688
  name: `Terminal ${index + 1}`,
99618
99689
  command: terminal.trim()
99619
99690
  }];
99620
- if (!isRecord$14(terminal)) return [];
99691
+ if (!isRecord$15(terminal)) return [];
99621
99692
  const command = typeof terminal.command === "string" ? terminal.command.trim() : "";
99622
99693
  if (!command) return [];
99623
99694
  return [{
@@ -99656,7 +99727,7 @@ function readConfigFile(source, path) {
99656
99727
  }]
99657
99728
  };
99658
99729
  }
99659
- if (!isRecord$14(parsed)) return {
99730
+ if (!isRecord$15(parsed)) return {
99660
99731
  ...emptyConfig(source, path),
99661
99732
  issues: [{
99662
99733
  severity: "blocked",
@@ -100799,7 +100870,7 @@ const CLAUDE_IDENTITY_KEYS = new Set([
100799
100870
  "credentialGenerationId"
100800
100871
  ]);
100801
100872
  const CLAUDE_IDENTITY_OPTIONAL_KEYS = new Set(["providerAccountIdentity"]);
100802
- function isRecord$13(value) {
100873
+ function isRecord$14(value) {
100803
100874
  return typeof value === "object" && value !== null && !Array.isArray(value);
100804
100875
  }
100805
100876
  function isStrictRfc3339DateTime(value) {
@@ -100828,7 +100899,7 @@ function hasAllowedKeys(value, required, optional) {
100828
100899
  return keys.every((key) => required.has(key) || optional.has(key)) && [...required].every((key) => keys.includes(key));
100829
100900
  }
100830
100901
  function parseDeliveryIdentity(value, provider) {
100831
- if (!isRecord$13(value)) return null;
100902
+ if (!isRecord$14(value)) return null;
100832
100903
  if (provider === "openai") return parseCodexDeliveryIdentity(value);
100833
100904
  if (provider === "anthropic") return parseClaudeDeliveryIdentity(value);
100834
100905
  return null;
@@ -100863,7 +100934,7 @@ function parseClaudeDeliveryIdentity(value) {
100863
100934
  };
100864
100935
  }
100865
100936
  function parseProviderRuntimeDeliveryReceipt(value, expected) {
100866
- if (!isRecord$13(value) || !hasExactKeys$2(value, RECEIPT_KEYS)) return null;
100937
+ if (!isRecord$14(value) || !hasExactKeys$2(value, RECEIPT_KEYS)) return null;
100867
100938
  const identity = parseDeliveryIdentity(value.identity, expected.provider);
100868
100939
  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;
100869
100940
  return {
@@ -100885,8 +100956,8 @@ function providerDeliveryBindingFromReceipt(receipt) {
100885
100956
  };
100886
100957
  }
100887
100958
  function readProviderDeliveryBinding(value) {
100888
- if (!isRecord$13(value) || !hasExactKeys$2(value, BINDING_KEYS)) return null;
100889
- const identityProvider = isRecord$13(value.identity) ? value.identity.provider : void 0;
100959
+ if (!isRecord$14(value) || !hasExactKeys$2(value, BINDING_KEYS)) return null;
100960
+ const identityProvider = isRecord$14(value.identity) ? value.identity.provider : void 0;
100890
100961
  const tokenProvider = identityProvider === "codex" ? "openai" : identityProvider === "claude" ? "anthropic" : null;
100891
100962
  if (!tokenProvider) return null;
100892
100963
  const identity = parseDeliveryIdentity(value.identity, tokenProvider);
@@ -148170,12 +148241,12 @@ function decodeProviderKind(providerName, operation) {
148170
148241
  detail: `Unknown persisted provider '${providerName}'.`
148171
148242
  }));
148172
148243
  }
148173
- function isRecord$12(value) {
148244
+ function isRecord$13(value) {
148174
148245
  return value !== null && typeof value === "object" && !Array.isArray(value);
148175
148246
  }
148176
148247
  function mergeRuntimePayload(existing, next) {
148177
148248
  if (next === void 0) return existing ?? null;
148178
- if (isRecord$12(existing) && isRecord$12(next)) return {
148249
+ if (isRecord$13(existing) && isRecord$13(next)) return {
148179
148250
  ...existing,
148180
148251
  ...next
148181
148252
  };
@@ -150147,7 +150218,7 @@ const PAGINATION_KEYS = new Set([
150147
150218
  "total",
150148
150219
  "hasMore"
150149
150220
  ]);
150150
- function isRecord$11(value) {
150221
+ function isRecord$12(value) {
150151
150222
  return typeof value === "object" && value !== null && !Array.isArray(value);
150152
150223
  }
150153
150224
  function hasOnlyKeys(value, allowed) {
@@ -150160,17 +150231,17 @@ function isRfc3339(value) {
150160
150231
  return typeof value === "string" && RFC3339.test(value) && Number.isFinite(Date.parse(value));
150161
150232
  }
150162
150233
  function isEnvelopeMeta(value) {
150163
- if (!isRecord$11(value) || !hasExactKeys$1(value, META_KEYS)) return false;
150234
+ if (!isRecord$12(value) || !hasExactKeys$1(value, META_KEYS)) return false;
150164
150235
  return typeof value.domain === "string" && value.domain.length > 0 && isRfc3339(value.timestamp) && typeof value.version === "string" && value.version.length > 0;
150165
150236
  }
150166
150237
  function isPagination(value) {
150167
- if (!isRecord$11(value) || !hasExactKeys$1(value, PAGINATION_KEYS)) return false;
150238
+ if (!isRecord$12(value) || !hasExactKeys$1(value, PAGINATION_KEYS)) return false;
150168
150239
  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";
150169
150240
  }
150170
150241
  function readLocalProviderAccountId(authJson) {
150171
150242
  try {
150172
150243
  const value = JSON.parse(authJson);
150173
- if (!isRecord$11(value) || value.auth_mode !== "chatgpt" || !isRecord$11(value.tokens)) return null;
150244
+ if (!isRecord$12(value) || value.auth_mode !== "chatgpt" || !isRecord$12(value.tokens)) return null;
150174
150245
  const providerAccountId = value.tokens.account_id;
150175
150246
  if (typeof providerAccountId !== "string") return null;
150176
150247
  const normalized = providerAccountId.trim();
@@ -150183,13 +150254,13 @@ function resolveLocalAuthJsonPath(homePath) {
150183
150254
  return (0, node_path.join)(homePath?.trim() || process.env.CODEX_HOME?.trim() || (0, node_path.join)((0, node_os.homedir)(), ".codex"), "auth.json");
150184
150255
  }
150185
150256
  function parseCodexAccountIdentityEnvelope(value) {
150186
- if (!isRecord$11(value) || !hasOnlyKeys(value, ENVELOPE_KEYS)) return null;
150257
+ if (!isRecord$12(value) || !hasOnlyKeys(value, ENVELOPE_KEYS)) return null;
150187
150258
  if (!("data" in value) || !("meta" in value) || !isEnvelopeMeta(value.meta)) return null;
150188
150259
  if (value.pagination !== void 0 && !isPagination(value.pagination)) return null;
150189
150260
  return readCodexAccountIdentity(value.data);
150190
150261
  }
150191
150262
  function readCodexAccountIdentity(value) {
150192
- if (!isRecord$11(value) || !hasExactKeys$1(value, IDENTITY_KEYS)) return null;
150263
+ if (!isRecord$12(value) || !hasExactKeys$1(value, IDENTITY_KEYS)) return null;
150193
150264
  if (value.kind !== "provider_account" || value.provider !== "codex" || typeof value.providerAccountIdentity !== "string" || !PROVIDER_ACCOUNT_IDENTITY$1.test(value.providerAccountIdentity)) return null;
150194
150265
  return {
150195
150266
  kind: "provider_account",
@@ -210973,6 +211044,11 @@ const PUBLIC_PROVIDER_LEAK_PATTERNS = [
210973
211044
  /https?:\/\/[^\s'"<>)]*(?:apex|deepseek|moonshot|kimi)[^\s'"<>)]*/i
210974
211045
  ];
210975
211046
  const DEFAULT_PUBLIC_PROVIDER_ERROR = "CMMD is temporarily unavailable. Check the workspace AI connection and retry.";
211047
+ function isProviderRateOrQuotaError(message) {
211048
+ if (!message) return false;
211049
+ const normalized = message.toLowerCase();
211050
+ 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);
211051
+ }
210976
211052
  /**
210977
211053
  * Infer a SAFE, clearer failure category from a (possibly leaky) provider error.
210978
211054
  *
@@ -210988,7 +211064,7 @@ function categorizePublicProviderError(message) {
210988
211064
  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.";
210989
211065
  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.";
210990
211066
  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.";
210991
- 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.";
211067
+ if (isProviderRateOrQuotaError(m)) return "The AI service is rate-limited or out of quota. Wait a moment and retry.";
210992
211068
  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.";
210993
211069
  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.";
210994
211070
  return null;
@@ -211369,10 +211445,16 @@ function claudeSubscriptionChildEnv(processEnv, credentialDirectory) {
211369
211445
  * environment with `claudeSubscriptionChildEnv`, the same builder as the turn's
211370
211446
  * personal-subscription branch.
211371
211447
  *
211372
- * `claude auth status` is a pure read. It sends no prompt and leaves
211373
- * `.credentials.json` byte-identical, so probing spends no subscription usage.
211448
+ * `claude auth status` sends no prompt and leaves `.credentials.json`
211449
+ * byte-identical, so probing spends no subscription usage. For Team and
211450
+ * Enterprise accounts the CLI's `auth` preAction schedules a remote-settings
211451
+ * fetch it never awaits, and the command exits before that fetch runs (see the
211452
+ * 2026-09-10 amendment to `docs/adr/cmmd-is-the-only-claude-credential-renewer.md`).
211374
211453
  * It prints JSON on stdout and exits 1 when logged out, so the exit code is not
211375
211454
  * a failure signal and the `loggedIn` field is what gets read.
211455
+ *
211456
+ * Each answer a probe reaches is also kept in `claudeCredentialDirectoryAnswers`
211457
+ * so the provider check can publish it without probing.
211376
211458
  */
211377
211459
  const execFileAsync$3 = (0, node_util.promisify)(node_child_process.execFile);
211378
211460
  const AUTH_STATUS_ARGS = ["auth", "status"];
@@ -211389,8 +211471,7 @@ const PROBE_DIRECTORY_PREFIX = "forge-claude-capability-";
211389
211471
  const VERSION_PATTERN = /\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/;
211390
211472
  function makeClaudeCredentialDirectoryProbe(deps) {
211391
211473
  const answers = /* @__PURE__ */ new Map();
211392
- const answer = async () => {
211393
- const version = await readVersionSafely(deps);
211474
+ const answerFor = (version) => {
211394
211475
  if (version === null) return runProbe(deps);
211395
211476
  const cached = answers.get(version);
211396
211477
  if (cached) return cached;
@@ -211401,10 +211482,47 @@ function makeClaudeCredentialDirectoryProbe(deps) {
211401
211482
  };
211402
211483
  pending.then((result) => {
211403
211484
  if (result === "inconclusive") forget();
211404
- }, forget);
211485
+ deps.onAnswer?.(version, result);
211486
+ }, () => {
211487
+ forget();
211488
+ deps.onAnswer?.(version, "inconclusive");
211489
+ });
211405
211490
  return pending;
211406
211491
  };
211407
- return { support: require_Schema$1.tryPromise(answer).pipe(require_Schema$1.orElseSucceed(() => "inconclusive")) };
211492
+ return { support: inconclusiveOnFailure(async () => answerFor(await readVersionSafely(deps))) };
211493
+ }
211494
+ /**
211495
+ * The one place a probe failure is handled. A missing binary, a timeout or a
211496
+ * directory that cannot be made all mean the probe could not tell.
211497
+ */
211498
+ function inconclusiveOnFailure(run) {
211499
+ return require_Schema$1.tryPromise(run).pipe(require_Schema$1.orElseSucceed(() => "inconclusive"));
211500
+ }
211501
+ const keyOf = (binaryPath, version) => JSON.stringify([binaryPath, version]);
211502
+ function makeClaudeCredentialDirectoryAnswers() {
211503
+ const answers = /* @__PURE__ */ new Map();
211504
+ const listeners = /* @__PURE__ */ new Set();
211505
+ return {
211506
+ record: (binaryPath, version, answer) => {
211507
+ const key = keyOf(binaryPath, version);
211508
+ if (answers.get(key) === answer) return;
211509
+ answers.set(key, answer);
211510
+ for (const listener of listeners) listener();
211511
+ },
211512
+ answerFor: (binaryPath, version) => answers.get(keyOf(binaryPath, version)),
211513
+ subscribe: (listener) => {
211514
+ listeners.add(listener);
211515
+ return () => {
211516
+ listeners.delete(listener);
211517
+ };
211518
+ }
211519
+ };
211520
+ }
211521
+ /** Every answer a turn-time probe in this process has reached. */
211522
+ const claudeCredentialDirectoryAnswers = makeClaudeCredentialDirectoryAnswers();
211523
+ /** The version in `claude --version` output, the key a probe answer is kept under. */
211524
+ function claudeVersionFromOutput(stdout) {
211525
+ return VERSION_PATTERN.exec(stdout)?.[0] ?? null;
211408
211526
  }
211409
211527
  /**
211410
211528
  * One probe per binary path for the life of the process. A conclusive answer is
@@ -211429,7 +211547,12 @@ function claudeCredentialDirectoryProbeFor(binaryPath) {
211429
211547
  if (!binaryPath) return INCONCLUSIVE_PROBE;
211430
211548
  const existing = probesByBinaryPath.get(binaryPath);
211431
211549
  if (existing) return existing;
211432
- const probe = makeClaudeCredentialDirectoryProbe(claudeCredentialDirectoryProbeDeps(binaryPath));
211550
+ const probe = makeClaudeCredentialDirectoryProbe({
211551
+ ...claudeCredentialDirectoryProbeDeps(binaryPath),
211552
+ onAnswer: (version, answer) => {
211553
+ claudeCredentialDirectoryAnswers.record(binaryPath, version, answer);
211554
+ }
211555
+ });
211433
211556
  probesByBinaryPath.set(binaryPath, probe);
211434
211557
  return probe;
211435
211558
  }
@@ -211440,7 +211563,7 @@ function claudeCredentialDirectoryProbeDeps(binaryPath, options = {}) {
211440
211563
  runAuthStatus: ({ credentialDirectory }) => run(AUTH_STATUS_ARGS, credentialDirectory),
211441
211564
  readBinaryVersion: async () => {
211442
211565
  const { stdout } = await run(VERSION_ARGS, null);
211443
- return VERSION_PATTERN.exec(stdout)?.[0] ?? null;
211566
+ return claudeVersionFromOutput(stdout);
211444
211567
  },
211445
211568
  makeProbeDirectory: () => (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), PROBE_DIRECTORY_PREFIX)),
211446
211569
  removeProbeDirectory: (directory) => (0, node_fs_promises.rm)(directory, {
@@ -211609,6 +211732,171 @@ const makeProviderAuthProvenance = (input) => {
211609
211732
  };
211610
211733
  };
211611
211734
  //#endregion
211735
+ //#region src/provider/claudeCredentialStore.ts
211736
+ /**
211737
+ * Which Credential Store the Claude CLI uses on this machine, read the way the
211738
+ * CLI reads it.
211739
+ *
211740
+ * The CLI has two stores, `.credentials.json` in the credential directory and
211741
+ * the operating system's own, and no platform check picks between them. It
211742
+ * takes the OS store when `CLAUDE_CODE_FORCE_WINDOWS_CREDMAN` is `1`, and
211743
+ * otherwise when the remote flag it caches in its config file,
211744
+ * `cachedGrowthBookFeatures.tengu_windows_credman`, is `true`. That file is a
211745
+ * legacy `.config.json` in the config directory when one exists, and
211746
+ * `.claude.json` otherwise. Read from Claude Code 2.1.265 in #3086.
211747
+ *
211748
+ * The CLI takes the file when it cannot find, read or parse the config, and so
211749
+ * does this. A config that parses to JSON `null` reads `file` as well: the CLI
211750
+ * reads a property off the parsed value, which throws on `null`, and it falls
211751
+ * back to the file store on that throw. Any other config that parses but
211752
+ * carries no boolean flag also means the file to the CLI, but it is the shape a
211753
+ * moved key leaves, so this reads `unknown` rather than guess.
211754
+ *
211755
+ * Linux with no session bus has no secret service, so the OS store fails its
211756
+ * own check and the CLI falls back to the file whatever the flag says. Every
211757
+ * Sprite is that case, and it reads `file` without opening the config. Linux
211758
+ * with a session bus is `unknown` when the OS store is selected, because only
211759
+ * the secret service knows whether it would answer.
211760
+ * See `docs/adr/delivery-clears-the-os-credential-store-first.md`.
211761
+ *
211762
+ * Two things in 2.1.265 this does not follow. The CLI puts its OAuth name
211763
+ * suffix, `F$()` in that bundle, into the config name. The suffix is empty in a
211764
+ * production build and `-custom-oauth` with `CLAUDE_CODE_CUSTOM_OAUTH_URL` set,
211765
+ * so the config is then `.claude-custom-oauth.json` instead of `.claude.json`.
211766
+ * And `primeWindowsCredManBackendEnabled` can set the choice before the file is
211767
+ * read, but only with a v5 storage backend, and `tryCreateV5Backend` returns
211768
+ * nothing in that build, so it never does.
211769
+ */
211770
+ const CLAUDE_FORCE_OS_CREDENTIAL_STORE_ENV = "CLAUDE_CODE_FORCE_WINDOWS_CREDMAN";
211771
+ const CACHED_FEATURES_KEY = "cachedGrowthBookFeatures";
211772
+ const OS_STORE_FLAG_KEY = "tengu_windows_credman";
211773
+ function readClaudeCredentialStore(input) {
211774
+ if (input.platform === "linux" && !hasSessionBus(input)) return "file";
211775
+ const selectsOsStore = cliSelectsOsStore(input);
211776
+ if (selectsOsStore === null) return "unknown";
211777
+ if (!selectsOsStore) return "file";
211778
+ return input.platform === "darwin" || input.platform === "win32" ? "os" : "unknown";
211779
+ }
211780
+ /** The same read against this machine's home directory and disk. */
211781
+ const readClaudeCredentialStoreFromDisk = (context) => {
211782
+ let homeDirectory;
211783
+ try {
211784
+ homeDirectory = (0, node_os.homedir)();
211785
+ } catch {
211786
+ homeDirectory = null;
211787
+ }
211788
+ return readClaudeCredentialStore({
211789
+ ...context,
211790
+ homeDirectory: homeDirectory ?? "",
211791
+ fileExists: homeDirectory === null ? () => false : node_fs.existsSync,
211792
+ readFile: (path) => {
211793
+ if (homeDirectory === null) throw new Error("no home directory");
211794
+ return (0, node_fs.readFileSync)(path, "utf8");
211795
+ }
211796
+ });
211797
+ };
211798
+ /** True, false, or null when a parsed config does not say. */
211799
+ function cliSelectsOsStore(input) {
211800
+ if (input.env[CLAUDE_FORCE_OS_CREDENTIAL_STORE_ENV] === "1") return true;
211801
+ const config = readConfig(input);
211802
+ if (config === null) return false;
211803
+ const features = isRecord$11(config.parsed) ? config.parsed[CACHED_FEATURES_KEY] : void 0;
211804
+ const flag = isRecord$11(features) ? features[OS_STORE_FLAG_KEY] : void 0;
211805
+ return typeof flag === "boolean" ? flag : null;
211806
+ }
211807
+ /**
211808
+ * The parsed config, or null wherever the CLI's own read throws and it falls
211809
+ * back to the file store: a file that is missing, unreadable or not JSON, and a
211810
+ * config that parses to JSON `null`, because the CLI reads a property off it.
211811
+ */
211812
+ function readConfig(input) {
211813
+ try {
211814
+ const parsed = JSON.parse(input.readFile(cachedFlagFile(input)));
211815
+ return parsed === null ? null : { parsed };
211816
+ } catch {
211817
+ return null;
211818
+ }
211819
+ }
211820
+ function cachedFlagFile(input) {
211821
+ const configured = input.configDirectory?.trim();
211822
+ const legacy = (0, node_path.join)(configured || (0, node_path.join)(input.homeDirectory, ".claude"), ".config.json");
211823
+ if (input.fileExists(legacy)) return legacy;
211824
+ return (0, node_path.join)(configured || input.homeDirectory, ".claude.json");
211825
+ }
211826
+ /** libsecret reaches the secret service over the session bus, so no bus means no OS store. */
211827
+ function hasSessionBus(input) {
211828
+ if (input.env.DBUS_SESSION_BUS_ADDRESS?.trim()) return true;
211829
+ const runtimeDirectory = input.env.XDG_RUNTIME_DIR?.trim();
211830
+ return runtimeDirectory ? input.fileExists((0, node_path.join)(runtimeDirectory, "bus")) : false;
211831
+ }
211832
+ function isRecord$11(value) {
211833
+ return typeof value === "object" && value !== null && !Array.isArray(value);
211834
+ }
211835
+ //#endregion
211836
+ //#region src/provider/claudeCredentialAnswers.ts
211837
+ /**
211838
+ * The live resolver. The credential-directory answer is whatever the turn-time
211839
+ * probe last reached for this binary path and version, so the published answer
211840
+ * and the one that gated a turn are the same. Until a turn has probed, or with
211841
+ * no version to look it up by, the field stays absent.
211842
+ */
211843
+ function makeClaudeCredentialAnswersResolver(options) {
211844
+ const readCredentialStore = options.readCredentialStore ?? readClaudeCredentialStoreFromDisk;
211845
+ return (input) => require_Schema$1.gen(function* () {
211846
+ const platform = yield* HostProcessPlatform;
211847
+ const credentialStore = yield* require_Schema$1.sync(() => readCredentialStore({
211848
+ env: options.env ?? process.env,
211849
+ platform,
211850
+ configDirectory: input.configDirectory
211851
+ }));
211852
+ const credentialDirectory = input.version === null ? void 0 : options.credentialDirectoryAnswers.answerFor(input.binaryPath, input.version);
211853
+ return credentialDirectory === void 0 ? { credentialStore } : {
211854
+ credentialStore,
211855
+ credentialDirectory
211856
+ };
211857
+ });
211858
+ }
211859
+ //#endregion
211860
+ //#region src/runtimeDrain/supervisedRepeat.ts
211861
+ /**
211862
+ * Runs `work` on a fixed schedule, forever, and RESTARTS the schedule itself
211863
+ * if it ever dies for any reason other than this scope closing.
211864
+ *
211865
+ * `Effect.repeat(work, Schedule.spaced(interval)).pipe(Effect.forkScoped)`
211866
+ * alone has no restart path. `work` catching its own errors only protects
211867
+ * the TICK: an interrupt that lands while `Effect.repeat` is asleep between
211868
+ * ticks — which, at any non-trivial interval, is nearly this loop's entire
211869
+ * life — happens outside anything `work` wraps, and kills that one fiber
211870
+ * outright with nothing logged, because nothing was running to log from.
211871
+ * The process this runs in can otherwise be perfectly healthy while the
211872
+ * schedule is silently, permanently dead.
211873
+ *
211874
+ * This runs the schedule on its own child fiber, raced against an explicit
211875
+ * shutdown signal fired by a finalizer registered in this scope. Only that
211876
+ * signal stops the loop for good; any other death of the schedule fiber —
211877
+ * whatever causes it, defect or interrupt — logs and restarts it. `work`
211878
+ * itself must still handle its own EXPECTED failures (this only supervises
211879
+ * the schedule, and treats every death of it as unexpected).
211880
+ */
211881
+ function runSupervisedRepeat(input) {
211882
+ return require_Schema$1.gen(function* () {
211883
+ const shutdownSignal = yield* require_Schema$1.make$4();
211884
+ yield* require_Schema$1.addFinalizer(() => require_Schema$1.succeed$2(shutdownSignal, void 0));
211885
+ let firstAttempt = true;
211886
+ while (true) {
211887
+ if (!firstAttempt) yield* require_Schema$1.sleep(input.interval);
211888
+ firstAttempt = false;
211889
+ const scheduleFiber = yield* require_Schema$1.forkChild(require_Schema$1.repeat(input.work, require_Schema$1.spaced(input.interval)));
211890
+ const outcome = yield* require_Schema$1.race(require_Path.await_(scheduleFiber), require_Schema$1._await(shutdownSignal).pipe(require_Schema$1.as("shutdown")));
211891
+ if (outcome === "shutdown") {
211892
+ yield* require_Path.interrupt(scheduleFiber);
211893
+ return;
211894
+ }
211895
+ yield* require_Schema$1.logError(`${input.label} died unexpectedly — restarting`, { cause: require_Schema$1.isFailure(outcome) ? require_Schema$1.pretty(outcome.cause) : "unknown exit" });
211896
+ }
211897
+ });
211898
+ }
211899
+ //#endregion
211612
211900
  //#region src/provider/Layers/ClaudeProvider.ts
211613
211901
  function endpointProfileDisplayNames(settings) {
211614
211902
  return Object.fromEntries(settings.endpointProfiles.filter((p) => p.displayName).map((p) => [p.slug, p.displayName]));
@@ -212110,7 +212398,21 @@ const nativeClaudeProviderStatus = require_Schema$1.fn("nativeClaudeProviderStat
212110
212398
  }
212111
212399
  });
212112
212400
  });
212113
- const checkClaudeProviderStatus = require_Schema$1.fn("checkClaudeProviderStatus")(function* (resolveSubscriptionType, resolveSlashCommands, resolveModels) {
212401
+ /**
212402
+ * Adds what the CLI said about its credentials. Every check where
212403
+ * `claude --version` ran and exited 0 reaches here, including one whose
212404
+ * version did not parse; that one has no key to look a probe answer up by, so
212405
+ * only the store is added.
212406
+ */
212407
+ function withClaudeCredentialAnswers(provider, resolve, input) {
212408
+ if (!resolve) return require_Schema$1.succeed(provider);
212409
+ return resolve(input).pipe(require_Schema$1.map((answers) => ({
212410
+ ...provider,
212411
+ ...answers
212412
+ })));
212413
+ }
212414
+ const checkClaudeProviderStatus = require_Schema$1.fn("checkClaudeProviderStatus")(function* (resolvers = {}) {
212415
+ const { resolveSubscriptionType, resolveSlashCommands, resolveModels, resolveCredentialAnswers } = resolvers;
212114
212416
  const config = require_Schema$1.getOrElse(yield* require_Schema$1.serviceOption(ServerConfig), () => ({
212115
212417
  ...DEFAULT_PROVIDER_AUTH_CONFIG,
212116
212418
  forgeClaudeConfigPath: void 0
@@ -212182,19 +212484,24 @@ const checkClaudeProviderStatus = require_Schema$1.fn("checkClaudeProviderStatus
212182
212484
  });
212183
212485
  }
212184
212486
  const executableFallbackModels = modelsForClaudeCliVersion(fallbackModels, parsedVersion);
212185
- if (personalClaudeInVmEnabled()) return yield* nativeClaudeProviderStatus({
212487
+ const credentialAnswersInput = {
212488
+ binaryPath: claudeSettings.binaryPath,
212489
+ version: claudeVersionFromOutput(version.stdout),
212490
+ configDirectory: effectiveConfigPath
212491
+ };
212492
+ if (personalClaudeInVmEnabled()) return yield* withClaudeCredentialAnswers(yield* nativeClaudeProviderStatus({
212186
212493
  enabled: claudeSettings.enabled,
212187
212494
  checkedAt,
212188
212495
  version: parsedVersion,
212189
212496
  configPath: effectiveConfigPath,
212190
212497
  models: executableFallbackModels,
212191
212498
  provenance
212192
- });
212499
+ }), resolveCredentialAnswers, credentialAnswersInput);
212193
212500
  const capabilitiesProbeInput = claudeCapabilitiesProbeInput(claudeSettings.binaryPath, effectiveConfigPath);
212194
212501
  const discovery = yield* resolveClaudeDiscovery(capabilitiesProbeInput, resolveSlashCommands, resolveModels);
212195
212502
  const models = modelsForClaudeCliVersion(providerModelsFromSettings(resolveClaudeProviderModels(discovery.models), PROVIDER$12, claudeSettings.customModels, DEFAULT_CLAUDE_MODEL_CAPABILITIES, endpointProfileDisplayNames(claudeSettings)), parsedVersion);
212196
212503
  const authProbe = yield* runClaudeCommand(["auth", "status"]).pipe(require_Schema$1.timeoutOption(DEFAULT_TIMEOUT_MS$3), require_Schema$1.result);
212197
- return buildClaudeProviderFromAuthProbe({
212504
+ return yield* withClaudeCredentialAnswers(buildClaudeProviderFromAuthProbe({
212198
212505
  authProbe,
212199
212506
  authMetadata: yield* resolveClaudeAuthMetadata(authProbe, capabilitiesProbeInput, resolveSubscriptionType),
212200
212507
  checkedAt,
@@ -212203,7 +212510,7 @@ const checkClaudeProviderStatus = require_Schema$1.fn("checkClaudeProviderStatus
212203
212510
  slashCommands: discovery.slashCommands,
212204
212511
  parsedVersion,
212205
212512
  provenance
212206
- });
212513
+ }), resolveCredentialAnswers, credentialAnswersInput);
212207
212514
  });
212208
212515
  const makePendingClaudeProvider = (claudeSettings) => {
212209
212516
  const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -212235,30 +212542,76 @@ const makePendingClaudeProvider = (claudeSettings) => {
212235
212542
  }
212236
212543
  });
212237
212544
  };
212238
- const ClaudeProviderLive = require_Schema$1.effect(ClaudeProvider, require_Schema$1.gen(function* () {
212239
- const serverSettings = yield* ServerSettingsService;
212240
- const spawner = yield* require_NodeTerminal.ChildProcessSpawner;
212241
- const subscriptionProbeCache = yield* require_Cache.make({
212242
- capacity: 4,
212243
- timeToLive: require_Schema$1.minutes(5),
212244
- lookup: (key) => {
212245
- const [binaryPath, configPath] = JSON.parse(key);
212246
- return probeClaudeCapabilities({
212247
- binaryPath,
212248
- ...configPath ? { configPath } : {}
212249
- });
212250
- }
212251
- });
212252
- const getDiscovery = (input) => require_Cache.get(subscriptionProbeCache, JSON.stringify([input.binaryPath, input.configPath]));
212253
- const checkProvider = checkClaudeProviderStatus((input) => getDiscovery(input).pipe(require_Schema$1.map((probe) => probe?.subscriptionType)), (input) => getDiscovery(input).pipe(require_Schema$1.map((probe) => probe?.slashCommands)), (input) => getDiscovery(input).pipe(require_Schema$1.map((probe) => probe?.models))).pipe(require_Schema$1.provideService(ServerSettingsService, serverSettings), require_Schema$1.provideService(require_NodeTerminal.ChildProcessSpawner, spawner));
212254
- return yield* makeManagedServerProvider({
212255
- getSettings: serverSettings.getSettings.pipe(require_Schema$1.map((settings) => settings.providers.claudeAgent), require_Schema$1.orDie),
212256
- streamSettings: serverSettings.streamChanges.pipe(require_Path.map((settings) => settings.providers.claudeAgent)),
212257
- haveSettingsChanged: (previous, next) => !require_Schema$1.equals(previous, next),
212258
- initialSnapshot: makePendingClaudeProvider,
212259
- checkProvider
212260
- });
212261
- }));
212545
+ /**
212546
+ * A refresh can wait behind a check that already holds the provider's refresh
212547
+ * permit before running its own, so it gets two check budgets.
212548
+ */
212549
+ const CREDENTIAL_ANSWER_REFRESH_TIMEOUT_MS = 2 * (CLAUDE_VERSION_PROBE_TIMEOUT_MS + CAPABILITIES_PROBE_TIMEOUT_MS$1 + DEFAULT_TIMEOUT_MS$3);
212550
+ /** The gap after each answer refresh, and the pause before a restart. */
212551
+ const CREDENTIAL_ANSWER_REFRESH_SPACING = require_Schema$1.seconds(1);
212552
+ /**
212553
+ * Reruns the check when a probe reaches a new or changed answer, so the
212554
+ * inventory carries it without waiting for the next scheduled check. Answers
212555
+ * that arrive during a refresh collapse into one more refresh.
212556
+ *
212557
+ * A refresh that fails, dies or outlives its timeout is logged, and the loop
212558
+ * goes back to waiting for the next answer. The timeout interrupts the check,
212559
+ * which hands back the refresh permit. `runSupervisedRepeat` restarts the loop
212560
+ * after any interrupt that does not come from the scope closing; closing the
212561
+ * scope stops it and drops the subscription.
212562
+ */
212563
+ const refreshOnNewCredentialDirectoryAnswer = require_Schema$1.fn("refreshOnNewCredentialDirectoryAnswer")(function* (credentialDirectoryAnswers, refresh) {
212564
+ const changed = yield* require_Path.sliding(1);
212565
+ yield* require_Schema$1.acquireRelease(require_Schema$1.sync(() => credentialDirectoryAnswers.subscribe(() => {
212566
+ require_Path.offerUnsafe(changed, void 0);
212567
+ })), (unsubscribe) => require_Schema$1.sync(unsubscribe));
212568
+ const refreshOnce = refresh.pipe(require_Schema$1.timeout(CREDENTIAL_ANSWER_REFRESH_TIMEOUT_MS), require_Schema$1.asVoid, require_Schema$1.catchCause((cause) => require_Schema$1.hasInterruptsOnly(cause) ? require_Schema$1.interrupt : require_Schema$1.logWarning("Claude refresh for a new credential-directory answer failed", { cause: require_Schema$1.pretty(cause) })));
212569
+ yield* require_Schema$1.forkScoped(runSupervisedRepeat({
212570
+ label: "Claude credential-directory answer refresh",
212571
+ work: require_Path.take(changed).pipe(require_Schema$1.andThen(refreshOnce)),
212572
+ interval: CREDENTIAL_ANSWER_REFRESH_SPACING
212573
+ }));
212574
+ });
212575
+ /** The Claude provider. Tests hand in their own answers and store read. */
212576
+ function makeClaudeProviderLive(options = {}) {
212577
+ const credentialDirectoryAnswers = options.credentialDirectoryAnswers ?? claudeCredentialDirectoryAnswers;
212578
+ const resolveCredentialAnswers = makeClaudeCredentialAnswersResolver({
212579
+ ...options,
212580
+ credentialDirectoryAnswers
212581
+ });
212582
+ return require_Schema$1.effect(ClaudeProvider, require_Schema$1.gen(function* () {
212583
+ const serverSettings = yield* ServerSettingsService;
212584
+ const spawner = yield* require_NodeTerminal.ChildProcessSpawner;
212585
+ const subscriptionProbeCache = yield* require_Cache.make({
212586
+ capacity: 4,
212587
+ timeToLive: require_Schema$1.minutes(5),
212588
+ lookup: (key) => {
212589
+ const [binaryPath, configPath] = JSON.parse(key);
212590
+ return probeClaudeCapabilities({
212591
+ binaryPath,
212592
+ ...configPath ? { configPath } : {}
212593
+ });
212594
+ }
212595
+ });
212596
+ const getDiscovery = (input) => require_Cache.get(subscriptionProbeCache, JSON.stringify([input.binaryPath, input.configPath]));
212597
+ const checkProvider = checkClaudeProviderStatus({
212598
+ resolveSubscriptionType: (input) => getDiscovery(input).pipe(require_Schema$1.map((probe) => probe?.subscriptionType)),
212599
+ resolveSlashCommands: (input) => getDiscovery(input).pipe(require_Schema$1.map((probe) => probe?.slashCommands)),
212600
+ resolveModels: (input) => getDiscovery(input).pipe(require_Schema$1.map((probe) => probe?.models)),
212601
+ resolveCredentialAnswers
212602
+ }).pipe(require_Schema$1.provideService(ServerSettingsService, serverSettings), require_Schema$1.provideService(require_NodeTerminal.ChildProcessSpawner, spawner));
212603
+ const provider = yield* makeManagedServerProvider({
212604
+ getSettings: serverSettings.getSettings.pipe(require_Schema$1.map((settings) => settings.providers.claudeAgent), require_Schema$1.orDie),
212605
+ streamSettings: serverSettings.streamChanges.pipe(require_Path.map((settings) => settings.providers.claudeAgent)),
212606
+ haveSettingsChanged: (previous, next) => !require_Schema$1.equals(previous, next),
212607
+ initialSnapshot: makePendingClaudeProvider,
212608
+ checkProvider
212609
+ });
212610
+ yield* refreshOnNewCredentialDirectoryAnswer(credentialDirectoryAnswers, provider.refresh);
212611
+ return provider;
212612
+ }));
212613
+ }
212614
+ const ClaudeProviderLive = makeClaudeProviderLive();
212262
212615
  //#endregion
212263
212616
  //#region src/provider/Services/ClaudeAdapter.ts
212264
212617
  /**
@@ -228638,7 +228991,7 @@ const OpenCodeRuntimeLive = require_Schema$1.effect(OpenCodeRuntime, makeOpenCod
228638
228991
  //#endregion
228639
228992
  //#region src/provider/Layers/OpenCodeAdapter.ts
228640
228993
  const PROVIDER$5 = "opencode";
228641
- const nowIso$3 = require_Schema$1.map(require_Schema$1.now, require_Schema$1.formatIso);
228994
+ const nowIso$4 = require_Schema$1.map(require_Schema$1.now, require_Schema$1.formatIso);
228642
228995
  /**
228643
228996
  * Map a tagged OpenCodeRuntimeError produced by {@link runOpenCodeSdk} into
228644
228997
  * the adapter-boundary `ProviderAdapterRequestError`. SDK-method-level call
@@ -228665,7 +229018,7 @@ const toProcessError = (threadId, cause) => new ProviderAdapterProcessError({
228665
229018
  });
228666
229019
  const buildEventBase = (input) => require_Schema$1.gen(function* () {
228667
229020
  const uuid = yield* nextUUIDv4;
228668
- const createdAt = input.createdAt ?? (yield* nowIso$3);
229021
+ const createdAt = input.createdAt ?? (yield* nowIso$4);
228669
229022
  return {
228670
229023
  eventId: EventId.make(uuid),
228671
229024
  provider: PROVIDER$5,
@@ -228824,7 +229177,7 @@ function sessionErrorMessage(error) {
228824
229177
  }
228825
229178
  function updateProviderSession(context, patch, options) {
228826
229179
  return require_Schema$1.gen(function* () {
228827
- const updatedAt = yield* nowIso$3;
229180
+ const updatedAt = yield* nowIso$4;
228828
229181
  const nextSession = {
228829
229182
  ...context.session,
228830
229183
  ...patch,
@@ -228946,7 +229299,7 @@ function makeOpenCodeAdapter(openCodeSettings, options) {
228946
229299
  if (("properties" in event ? event.properties.sessionID : void 0) !== context.openCodeSessionId) return;
228947
229300
  const turnId = context.activeTurnId;
228948
229301
  yield* writeNativeEventBestEffort(context.session.threadId, {
228949
- observedAt: yield* nowIso$3,
229302
+ observedAt: yield* nowIso$4,
228950
229303
  event: {
228951
229304
  provider: PROVIDER$5,
228952
229305
  threadId: context.session.threadId,
@@ -229262,7 +229615,7 @@ function makeOpenCodeAdapter(openCodeSettings, options) {
229262
229615
  yield* require_Schema$1.close(started.sessionScope, require_Schema$1.void_$1).pipe(require_Schema$1.ignore);
229263
229616
  return raceWinner.session;
229264
229617
  }
229265
- const createdAt = yield* nowIso$3;
229618
+ const createdAt = yield* nowIso$4;
229266
229619
  const session = {
229267
229620
  provider: PROVIDER$5,
229268
229621
  status: "ready",
@@ -236061,7 +236414,7 @@ function filterEvents(events, options) {
236061
236414
  });
236062
236415
  return options.limit === void 0 ? filtered : filtered.slice(-options.limit);
236063
236416
  }
236064
- function nowIso$2() {
236417
+ function nowIso$3() {
236065
236418
  return (/* @__PURE__ */ new Date()).toISOString();
236066
236419
  }
236067
236420
  //#endregion
@@ -236076,7 +236429,7 @@ var BrowserActionEventRecorder = class {
236076
236429
  type: "browser.action",
236077
236430
  action,
236078
236431
  ...this.sessionFields(),
236079
- timestamp: nowIso$2()
236432
+ timestamp: nowIso$3()
236080
236433
  });
236081
236434
  }
236082
236435
  async result(action) {
@@ -236085,7 +236438,7 @@ var BrowserActionEventRecorder = class {
236085
236438
  action,
236086
236439
  ok: true,
236087
236440
  ...this.sessionFields(),
236088
- timestamp: nowIso$2()
236441
+ timestamp: nowIso$3()
236089
236442
  });
236090
236443
  }
236091
236444
  async error(action, error) {
@@ -236094,7 +236447,7 @@ var BrowserActionEventRecorder = class {
236094
236447
  action,
236095
236448
  error: error instanceof Error ? error.message : String(error),
236096
236449
  ...this.sessionFields(),
236097
- timestamp: nowIso$2()
236450
+ timestamp: nowIso$3()
236098
236451
  });
236099
236452
  }
236100
236453
  sessionFields() {
@@ -236615,7 +236968,7 @@ var AgentBrowser = class {
236615
236968
  await this.options.events?.record({
236616
236969
  type: "browser.safety.confirmation.requested",
236617
236970
  ...request,
236618
- createdAt: nowIso$2()
236971
+ createdAt: nowIso$3()
236619
236972
  });
236620
236973
  const result = await this.resolveConfirmation(request);
236621
236974
  const approved = typeof result === "boolean" ? result : result.approved;
@@ -236623,7 +236976,7 @@ var AgentBrowser = class {
236623
236976
  type: "browser.safety.confirmation.resolved",
236624
236977
  approved,
236625
236978
  requestId: request.requestId,
236626
- resolvedAt: nowIso$2(),
236979
+ resolvedAt: nowIso$3(),
236627
236980
  ...this.events.sessionFields()
236628
236981
  });
236629
236982
  if (!approved) throw new BrowserSafetyError(decision.reason ?? "Browser action was not confirmed.", action, {
@@ -281978,7 +282331,7 @@ const DEFAULT_AGENT_BROWSER_BRIDGE_CLOSE_TIMEOUT_MS = 3e4;
281978
282331
  const DEFAULT_AGENT_BROWSER_PLAYWRIGHT_BACKEND_CLOSE_TIMEOUT_MS = 1e4;
281979
282332
  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.";
281980
282333
  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.";
281981
- function nowIso$1() {
282334
+ function nowIso$2() {
281982
282335
  return (/* @__PURE__ */ new Date()).toISOString();
281983
282336
  }
281984
282337
  function positiveTimeoutMs(value) {
@@ -282078,12 +282431,12 @@ function snapshotAfterTimedOutControl(snapshot, command) {
282078
282431
  case "setViewport": return {
282079
282432
  ...snapshot,
282080
282433
  viewport: command.viewport,
282081
- updatedAt: nowIso$1()
282434
+ updatedAt: nowIso$2()
282082
282435
  };
282083
282436
  case "resetViewport": return {
282084
282437
  ...snapshot,
282085
282438
  viewport: DEFAULT_AGENT_BROWSER_VIEWPORT,
282086
- updatedAt: nowIso$1()
282439
+ updatedAt: nowIso$2()
282087
282440
  };
282088
282441
  default: return snapshot;
282089
282442
  }
@@ -282529,7 +282882,7 @@ function toStreamEvent(input) {
282529
282882
  userId: BrowserUserId.make(input.userId),
282530
282883
  agentId: input.agentId,
282531
282884
  ...input.controlState ? { sessionEpoch: input.controlState.sessionEpoch } : {},
282532
- createdAt: nowIso$1()
282885
+ createdAt: nowIso$2()
282533
282886
  };
282534
282887
  switch (input.event.type) {
282535
282888
  case "session.started": return {
@@ -282830,7 +283183,7 @@ function createAgentBrowserHostBridgeManager(bridge, options) {
282830
283183
  environmentId: event.input.environmentId ?? DEFAULT_BROWSER_ENVIRONMENT_ID,
282831
283184
  userId: event.input.userId ?? "local-user",
282832
283185
  agentId: null,
282833
- createdAt: nowIso$1(),
283186
+ createdAt: nowIso$2(),
282834
283187
  type: "display.iceCandidate",
282835
283188
  payload: {
282836
283189
  sessionId: event.input.sessionId,
@@ -287499,46 +287852,6 @@ const makeRuntimeDrainState = require_Schema$1.gen(function* () {
287499
287852
  var RuntimeDrainState = class extends require_Schema$1.Service()("forge/runtimeDrain/RuntimeDrainState") {};
287500
287853
  const RuntimeDrainStateLive = require_Schema$1.effect(RuntimeDrainState, makeRuntimeDrainState);
287501
287854
  //#endregion
287502
- //#region src/runtimeDrain/supervisedRepeat.ts
287503
- /**
287504
- * Runs `work` on a fixed schedule, forever, and RESTARTS the schedule itself
287505
- * if it ever dies for any reason other than this scope closing.
287506
- *
287507
- * `Effect.repeat(work, Schedule.spaced(interval)).pipe(Effect.forkScoped)`
287508
- * alone has no restart path. `work` catching its own errors only protects
287509
- * the TICK: an interrupt that lands while `Effect.repeat` is asleep between
287510
- * ticks — which, at any non-trivial interval, is nearly this loop's entire
287511
- * life — happens outside anything `work` wraps, and kills that one fiber
287512
- * outright with nothing logged, because nothing was running to log from.
287513
- * The process this runs in can otherwise be perfectly healthy while the
287514
- * schedule is silently, permanently dead.
287515
- *
287516
- * This runs the schedule on its own child fiber, raced against an explicit
287517
- * shutdown signal fired by a finalizer registered in this scope. Only that
287518
- * signal stops the loop for good; any other death of the schedule fiber —
287519
- * whatever causes it, defect or interrupt — logs and restarts it. `work`
287520
- * itself must still handle its own EXPECTED failures (this only supervises
287521
- * the schedule, and treats every death of it as unexpected).
287522
- */
287523
- function runSupervisedRepeat(input) {
287524
- return require_Schema$1.gen(function* () {
287525
- const shutdownSignal = yield* require_Schema$1.make$4();
287526
- yield* require_Schema$1.addFinalizer(() => require_Schema$1.succeed$2(shutdownSignal, void 0));
287527
- let firstAttempt = true;
287528
- while (true) {
287529
- if (!firstAttempt) yield* require_Schema$1.sleep(input.interval);
287530
- firstAttempt = false;
287531
- const scheduleFiber = yield* require_Schema$1.forkChild(require_Schema$1.repeat(input.work, require_Schema$1.spaced(input.interval)));
287532
- const outcome = yield* require_Schema$1.race(require_Path.await_(scheduleFiber), require_Schema$1._await(shutdownSignal).pipe(require_Schema$1.as("shutdown")));
287533
- if (outcome === "shutdown") {
287534
- yield* require_Path.interrupt(scheduleFiber);
287535
- return;
287536
- }
287537
- yield* require_Schema$1.logError(`${input.label} died unexpectedly — restarting`, { cause: require_Schema$1.isFailure(outcome) ? require_Schema$1.pretty(outcome.cause) : "unknown exit" });
287538
- }
287539
- });
287540
- }
287541
- //#endregion
287542
287855
  //#region src/runtimeDrain/RuntimeDrainReporter.ts
287543
287856
  const HEARTBEAT_INTERVAL = require_Schema$1.seconds(30);
287544
287857
  const HEARTBEAT_TICK_TIMEOUT = require_Schema$1.seconds(60);
@@ -294961,7 +295274,7 @@ function resolveBuildCommitFromEnv(env) {
294961
295274
  * environment descriptor down instead of reporting an honest "unknown".
294962
295275
  */
294963
295276
  function readBakedBuildCommit() {
294964
- return "7695a8221bc664caedd33aa411ac7bcdbb0a3594";
295277
+ return "9243995fc67ca68aa610fabccbf48bba35a5ffd1";
294965
295278
  }
294966
295279
  async function resolveServerBuildCommit(input) {
294967
295280
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -298042,6 +298355,52 @@ function toProjectorDecodeError(eventType) {
298042
298355
  });
298043
298356
  }
298044
298357
  //#endregion
298358
+ //#region src/orchestration/deciderEventBase.ts
298359
+ const nowIso$1 = () => (/* @__PURE__ */ new Date()).toISOString();
298360
+ const defaultMetadata = {
298361
+ eventId: crypto.randomUUID(),
298362
+ aggregateKind: "thread",
298363
+ aggregateId: "",
298364
+ occurredAt: nowIso$1(),
298365
+ commandId: null,
298366
+ causationEventId: null,
298367
+ correlationId: null,
298368
+ metadata: {}
298369
+ };
298370
+ function withEventBase(input) {
298371
+ return {
298372
+ ...defaultMetadata,
298373
+ eventId: crypto.randomUUID(),
298374
+ aggregateKind: input.aggregateKind,
298375
+ aggregateId: input.aggregateId,
298376
+ occurredAt: input.occurredAt,
298377
+ commandId: input.commandId,
298378
+ correlationId: input.commandId,
298379
+ metadata: input.metadata ?? {}
298380
+ };
298381
+ }
298382
+ //#endregion
298383
+ //#region src/orchestration/projectScriptMutation.ts
298384
+ function unchanged(a, b) {
298385
+ return a !== void 0 && a.id === b.id && a.name === b.name && a.command === b.command && a.icon === b.icon && a.runOnWorktreeCreate === b.runOnWorktreeCreate;
298386
+ }
298387
+ const applyProjectScriptMutation = require_Schema$1.fn("applyProjectScriptMutation")(function* (scripts, command) {
298388
+ const mutation = command.mutation;
298389
+ const id = mutation.kind === "add" ? mutation.script.id : mutation.expected.id;
298390
+ const existing = scripts.find((script) => script.id === id);
298391
+ if (mutation.kind === "add" ? existing !== void 0 : !unchanged(existing, mutation.expected)) return yield* new OrchestrationCommandInvariantError({
298392
+ commandType: command.type,
298393
+ detail: "This action changed in another window. Reopen it and try again."
298394
+ });
298395
+ if (mutation.kind === "add") return [...scripts, mutation.script];
298396
+ if (mutation.kind === "remove") return scripts.filter((script) => script.id !== id);
298397
+ if (mutation.script.id !== id) return yield* new OrchestrationCommandInvariantError({
298398
+ commandType: command.type,
298399
+ detail: "An action's ID cannot change during an update."
298400
+ });
298401
+ return scripts.map((script) => script.id === id ? mutation.script : script);
298402
+ });
298403
+ //#endregion
298045
298404
  //#region src/orchestration/turnAccountSelection.ts
298046
298405
  /** A per-turn account choice remains local to the thread and cannot change its provider. */
298047
298406
  function turnAccountSelection(current, requested) {
@@ -298098,28 +298457,6 @@ function requireThreadAbsent(input) {
298098
298457
  //#endregion
298099
298458
  //#region src/orchestration/decider.ts
298100
298459
  const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
298101
- const defaultMetadata = {
298102
- eventId: crypto.randomUUID(),
298103
- aggregateKind: "thread",
298104
- aggregateId: "",
298105
- occurredAt: nowIso(),
298106
- commandId: null,
298107
- causationEventId: null,
298108
- correlationId: null,
298109
- metadata: {}
298110
- };
298111
- function withEventBase(input) {
298112
- return {
298113
- ...defaultMetadata,
298114
- eventId: crypto.randomUUID(),
298115
- aggregateKind: input.aggregateKind,
298116
- aggregateId: input.aggregateId,
298117
- occurredAt: input.occurredAt,
298118
- commandId: input.commandId,
298119
- correlationId: input.commandId,
298120
- metadata: input.metadata ?? {}
298121
- };
298122
- }
298123
298460
  function requireOrdinaryMessageId(commandType, messageId) {
298124
298461
  return isReservedReplicaSourceMessageId(messageId) ? require_Schema$1.fail(new OrchestrationCommandInvariantError({
298125
298462
  commandType,
@@ -298172,6 +298509,22 @@ const decideOrchestrationCommand = require_Schema$1.fn("decideOrchestrationComma
298172
298509
  updatedAt: command.createdAt
298173
298510
  }
298174
298511
  };
298512
+ case "project.script.update": {
298513
+ const scripts = yield* applyProjectScriptMutation((yield* requireProject({
298514
+ readModel,
298515
+ command,
298516
+ projectId: command.projectId
298517
+ })).scripts, command);
298518
+ return yield* decideOrchestrationCommand({
298519
+ readModel,
298520
+ command: {
298521
+ type: "project.meta.update",
298522
+ commandId: command.commandId,
298523
+ projectId: command.projectId,
298524
+ scripts
298525
+ }
298526
+ });
298527
+ }
298175
298528
  case "project.meta.update": {
298176
298529
  yield* requireProject({
298177
298530
  readModel,
@@ -299458,6 +299811,7 @@ function commandToAggregateRef(command) {
299458
299811
  switch (command.type) {
299459
299812
  case "project.create":
299460
299813
  case "project.meta.update":
299814
+ case "project.script.update":
299461
299815
  case "project.delete": return {
299462
299816
  aggregateKind: "project",
299463
299817
  aggregateId: command.projectId