@cmmd-center/forge 0.13.123 → 0.13.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -66337,7 +66337,7 @@ function normalizeNumberish(value) {
66337
66337
  }
66338
66338
  //#endregion
66339
66339
  //#region package.json
66340
- var version$1 = "0.13.123";
66340
+ var version$1 = "0.13.124";
66341
66341
  //#endregion
66342
66342
  //#region src/sentry.ts
66343
66343
  const SERVER_APP_NAME = "forge-server";
@@ -300407,7 +300407,7 @@ function resolveBuildCommitFromEnv(env) {
300407
300407
  * environment descriptor down instead of reporting an honest "unknown".
300408
300408
  */
300409
300409
  function readBakedBuildCommit() {
300410
- return "36ad9a06120f61eef3931979fa6c199d3c862efb";
300410
+ return "52e8c25f0de536143b44ff1ac86510aa93d35772";
300411
300411
  }
300412
300412
  async function resolveServerBuildCommit(input) {
300413
300413
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -315066,9 +315066,17 @@ const RuntimeReplicationLive = require_Schema$1.effect(RuntimeReplication, requi
315066
315066
  concurrency: 1,
315067
315067
  discard: true
315068
315068
  });
315069
+ const retryStuckThreadStreams = (streamKeys) => runtimeId ? require_Schema$1.forEach(streamKeys, (streamKey) => outbox.resetStuckThreadGeneration({
315070
+ runtimeId,
315071
+ streamKey
315072
+ }).pipe(require_Schema$1.flatMap((reset) => reset ? enqueueThread(threadIdFromStreamKey(streamKey)) : require_Schema$1.void_)), {
315073
+ concurrency: 1,
315074
+ discard: true
315075
+ }) : require_Schema$1.void_;
315069
315076
  return RuntimeReplication.of({
315070
315077
  start,
315071
- retryReprojectionPending
315078
+ retryReprojectionPending,
315079
+ retryStuckThreadStreams
315072
315080
  });
315073
315081
  }));
315074
315082
  //#endregion
@@ -315415,6 +315423,31 @@ const THREAD_STREAM_PAYLOAD_HASH_MISMATCH_CODE = "THREAD_STREAM_PAYLOAD_HASH_MIS
315415
315423
  */
315416
315424
  const THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE = "THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING";
315417
315425
  /**
315426
+ * Terminal park code for a thread-events reset `refusedResetIsStuck` judges
315427
+ * genuinely stuck: the exact canonical reset shape was already sent once
315428
+ * under this generation and CMMD refused it again with zero acknowledged
315429
+ * progress (forge#3635). Before this code existed, that case fell through to
315430
+ * `failGeneric`, which recorded the raw `THREAD_STREAM_PAYLOAD_CONFLICT` /
315431
+ * `THREAD_STREAM_HEAD_MISMATCH` code -- a code neither
315432
+ * `readReprojectionPendingThreadStreamKeys` nor
315433
+ * `MANUALLY_RECOVERABLE_BLOCKED_CODES` selects, so the manual retry endpoint
315434
+ * could never reach the row.
315435
+ *
315436
+ * Deliberately excluded from `CLAIMABLE_BLOCKED_CODES`,
315437
+ * `STARTUP_RECOVERABLE_BLOCKED_CODES`, and `MANUALLY_RECOVERABLE_BLOCKED_CODES`:
315438
+ * a plain status flip back to `pending` would resend the exact batch CMMD
315439
+ * already refused twice. `enqueueThread` also leaves a row parked under this
315440
+ * code alone on new domain events and the heartbeat (see the stuck check
315441
+ * inside `enqueueThread` below) instead of re-projecting it back to `pending`
315442
+ * the way it does for every other generation, so a stuck row only clears once
315443
+ * the operator retry endpoint calls `RuntimeReplicationOutboxShape`'s
315444
+ * `resetStuckThreadGeneration` (which mints a fresh, never-before-sent repair
315445
+ * generation before re-projecting) or
315446
+ * a genuinely new generation id supersedes it (e.g. the thread archives and
315447
+ * gets a `:final:` id).
315448
+ */
315449
+ const THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE = "THREAD_STREAM_REMOTE_CONFLICT_STUCK";
315450
+ /**
315418
315451
  * Whether a refused batch is the exact canonical reset shape (`fromSequence
315419
315452
  * === 1`, empty previous head, deterministic content) under a generation the
315420
315453
  * local cursor already holds with zero acknowledged progress -- i.e. this
@@ -315692,6 +315725,23 @@ const readReprojectionPendingThreadStreamKeys = require_Schema$1.gen(function* (
315692
315725
  AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE}
315693
315726
  `).map((row) => row.streamKey);
315694
315727
  }).pipe(require_Schema$1.orDie);
315728
+ /**
315729
+ * Thread streams parked under THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE, for the
315730
+ * manual `/api/runtime-replication/retry` endpoint (forge#3635). Deliberately absent
315731
+ * from MANUALLY_RECOVERABLE_BLOCKED_CODES for the same reason
315732
+ * `readReprojectionPendingThreadStreamKeys` is: a plain status flip back to `pending`
315733
+ * would resend the exact reset CMMD already refused twice. `http.ts`'s retry handler
315734
+ * passes these to `RuntimeReplication`'s `retryStuckThreadStreams`, which calls
315735
+ * `resetStuckThreadGeneration` for each of these before re-projecting, so the retry
315736
+ * always sends a fresh, never-before-refused generation.
315737
+ */
315738
+ const readStuckThreadStreamKeys = require_Schema$1.gen(function* () {
315739
+ return (yield* (yield* require_SqlError.SqlClient)`
315740
+ SELECT stream_key AS "streamKey" FROM runtime_replication_outbox
315741
+ WHERE operation = 'thread-events' AND status = 'blocked'
315742
+ AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE}
315743
+ `).map((row) => row.streamKey);
315744
+ }).pipe(require_Schema$1.orDie);
315695
315745
  const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationOutbox, require_Schema$1.gen(function* () {
315696
315746
  const sql = yield* require_SqlError.SqlClient;
315697
315747
  const upsertPayload = (input) => {
@@ -315912,6 +315962,12 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
315912
315962
  now
315913
315963
  }).pipe(require_Schema$1.orDie);
315914
315964
  };
315965
+ const isThreadStreamParkedStuck = (input) => sql`
315966
+ SELECT runtime_id AS "runtimeId" FROM runtime_replication_outbox
315967
+ WHERE runtime_id = ${input.runtimeId} AND stream_key = ${input.streamKey}
315968
+ AND operation = 'thread-events' AND status = 'blocked'
315969
+ AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE}
315970
+ `.pipe(require_Schema$1.map((rows) => rows.length === 1));
315915
315971
  const enqueueThread = (input) => {
315916
315972
  const now = (/* @__PURE__ */ new Date()).toISOString();
315917
315973
  const baseGenerationId = runtimeThreadGenerationId(input.runtimeId, input.streamKey);
@@ -315920,6 +315976,7 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
315920
315976
  const previousActiveGeneration = activeRuntimeThreadGenerationId(baseGenerationId, previousCursor?.generationId);
315921
315977
  const generationId = input.historyFinalized ? runtimeThreadFinalGenerationId(input.runtimeId, input.streamKey, input.events) : previousActiveGeneration;
315922
315978
  const replacesGeneration = previousCursor !== void 0 && previousCursor.generationId !== generationId;
315979
+ if (!replacesGeneration && (yield* isThreadStreamParkedStuck(input))) return;
315923
315980
  yield* initializeThreadCursor({
315924
315981
  runtimeId: input.runtimeId,
315925
315982
  streamKey: input.streamKey,
@@ -316221,12 +316278,16 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
316221
316278
  })).pipe(require_Schema$1.orDie);
316222
316279
  /**
316223
316280
  * Recovers from a CMMD-reported thread stream conflict (see
316224
- * THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` when this item is
316225
- * not eligible (its payload does not decode, or `refusedResetIsStuck`
316226
- * says the exact reset shape already failed once -- in which case the
316227
- * caller falls back to blocking) and `false` when the row was superseded
316228
- * before this could apply, mirroring `fail`'s own supersession signal.
316229
- * Otherwise replaces the local cursor with a brand-new repair generation
316281
+ * THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` only when this
316282
+ * item's payload does not decode, in which case the caller falls back to
316283
+ * `failGeneric`. When `refusedResetIsStuck` says the exact reset shape
316284
+ * already failed once with zero progress, parks the row under
316285
+ * `THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE` directly (see that code's
316286
+ * doc comment) and returns `true`/`false` the same way the normal repair
316287
+ * path below does -- never `null` -- so the caller never falls through to
316288
+ * `failGeneric`'s raw conflict code (forge#3635). `false` means the row
316289
+ * was superseded before this could apply, mirroring `fail`'s own
316290
+ * supersession signal. Otherwise replaces the local cursor with a brand-new repair generation
316230
316291
  * from sequence 1, empty previous head -- the shape CMMD's reset rule
316231
316292
  * accepts unconditionally -- and parks the row blocked under
316232
316293
  * `THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE`, deferring the
@@ -316245,13 +316306,30 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
316245
316306
  runtimeId: item.runtimeId,
316246
316307
  streamKey: item.streamKey
316247
316308
  });
316309
+ const now = (/* @__PURE__ */ new Date()).toISOString();
316248
316310
  if (refusedResetIsStuck({
316249
316311
  baseGenerationId,
316250
316312
  refusedGenerationId: parsedBatch.generationId,
316251
316313
  refusedFromSequence: parsedBatch.fromSequence,
316252
316314
  cursor
316253
- })) return null;
316254
- const now = (/* @__PURE__ */ new Date()).toISOString();
316315
+ })) {
316316
+ if ((yield* sql`
316317
+ UPDATE runtime_replication_outbox SET
316318
+ status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
316319
+ last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_STUCK"},
316320
+ last_error = ${`Runtime replication conflict (${failure.code}) refused a thread-events reset that had already been refused once with zero acknowledged progress under this generation. Parked as stuck rather than resent; use the manual retry endpoint to reset it to a fresh generation.`}, updated_at = ${now}
316321
+ WHERE runtime_id = ${item.runtimeId} AND stream_key = ${item.streamKey}
316322
+ AND payload_hash = ${item.payloadHash}
316323
+ RETURNING runtime_id AS "runtimeId"
316324
+ `).length !== 1) return false;
316325
+ yield* require_Schema$1.logWarning("Runtime replication parked a thread-stream repair as stuck after its reset was refused with zero progress.", {
316326
+ runtimeId: item.runtimeId,
316327
+ streamKey: item.streamKey,
316328
+ errorCode: failure.code,
316329
+ refusedGenerationId: parsedBatch.generationId
316330
+ });
316331
+ return true;
316332
+ }
316255
316333
  const repairGenerationId = `${baseGenerationId}:repair:${node_crypto.randomUUID()}`;
316256
316334
  const message = `Runtime replication conflict (${failure.code}) refused a thread-events batch; deferring to a full-history re-projection under a new repair generation.`;
316257
316335
  if (!(yield* sql.withTransaction(require_Schema$1.gen(function* () {
@@ -316319,6 +316397,30 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
316319
316397
  AND status = 'blocked'
316320
316398
  AND last_error_code = 'RUNTIME_REVOKED'
316321
316399
  `.pipe(require_Schema$1.asVoid, require_Schema$1.orDie);
316400
+ const resetStuckThreadGeneration = (input) => {
316401
+ const now = (/* @__PURE__ */ new Date()).toISOString();
316402
+ const repairGenerationId = `${runtimeThreadGenerationId(input.runtimeId, input.streamKey)}:repair:${node_crypto.randomUUID()}`;
316403
+ const message = "Runtime replication operator retry reset a stuck thread-stream repair to a new generation.";
316404
+ return sql.withTransaction(require_Schema$1.gen(function* () {
316405
+ if ((yield* sql`
316406
+ UPDATE runtime_replication_outbox SET
316407
+ status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
316408
+ last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING"},
316409
+ last_error = ${message}, updated_at = ${now}
316410
+ WHERE runtime_id = ${input.runtimeId} AND stream_key = ${input.streamKey}
316411
+ AND operation = 'thread-events'
316412
+ AND last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_STUCK"}
316413
+ RETURNING runtime_id AS "runtimeId"
316414
+ `).length !== 1) return false;
316415
+ yield* replaceThreadCursor({
316416
+ runtimeId: input.runtimeId,
316417
+ streamKey: input.streamKey,
316418
+ generationId: repairGenerationId,
316419
+ now
316420
+ });
316421
+ return true;
316422
+ })).pipe(require_Schema$1.orDie);
316423
+ };
316322
316424
  const getStatus = () => readRuntimeReplicationOutboxStatus.pipe(require_Schema$1.provideService(require_SqlError.SqlClient, sql));
316323
316425
  const getDiagnostics = () => require_Schema$1.gen(function* () {
316324
316426
  const [diagnostics] = decodeDiagnostics(yield* sql`
@@ -316349,6 +316451,7 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
316349
316451
  acknowledge,
316350
316452
  fail,
316351
316453
  retryBlocked,
316454
+ resetStuckThreadGeneration,
316352
316455
  recover,
316353
316456
  recoverLegacyRevokedInventory,
316354
316457
  getStatus,
@@ -316481,10 +316584,14 @@ const retryHandler = require_Schema$1.gen(function* () {
316481
316584
  const replication = yield* require_Schema$1.serviceOption(RuntimeReplication);
316482
316585
  const requeued = yield* retryBlockedRuntimeReplicationOutbox;
316483
316586
  const reprojectionPendingStreamKeys = yield* readReprojectionPendingThreadStreamKeys;
316484
- if (require_Schema$1.isSome(replication)) yield* replication.value.retryReprojectionPending(reprojectionPendingStreamKeys);
316587
+ const stuckStreamKeys = yield* readStuckThreadStreamKeys;
316588
+ if (require_Schema$1.isSome(replication)) {
316589
+ yield* replication.value.retryReprojectionPending(reprojectionPendingStreamKeys);
316590
+ yield* replication.value.retryStuckThreadStreams(stuckStreamKeys);
316591
+ }
316485
316592
  const status = yield* readRuntimeReplicationOutboxStatus;
316486
316593
  return require_HttpServer.jsonUnsafe({
316487
- requeued: requeued + (require_Schema$1.isSome(replication) ? reprojectionPendingStreamKeys.length : 0),
316594
+ requeued: requeued + (require_Schema$1.isSome(replication) ? reprojectionPendingStreamKeys.length + stuckStreamKeys.length : 0),
316488
316595
  ...responseFromStatus(status)
316489
316596
  });
316490
316597
  }).pipe(require_Schema$1.catchTag("AuthError", respondToAuthError));
package/dist/bin.mjs CHANGED
@@ -23,7 +23,7 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicK
23
23
  import * as FS$1 from "node:fs";
24
24
  import fs, { accessSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, mkdtempSync, promises, readFile, readFileSync, readdir, readdirSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
25
25
  import * as NodeOS from "node:os";
26
- import os, { homedir, networkInterfaces, tmpdir } from "node:os";
26
+ import $U, { homedir, networkInterfaces, tmpdir } from "node:os";
27
27
  import * as Path$1 from "node:path";
28
28
  import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
29
29
  import { pathToFileURL } from "node:url";
@@ -50658,7 +50658,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
50658
50658
  const min = forceColor || 0;
50659
50659
  if (env.TERM === "dumb") return min;
50660
50660
  if (process$1.platform === "win32") {
50661
- const osRelease = os.release().split(".");
50661
+ const osRelease = $U.release().split(".");
50662
50662
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
50663
50663
  return 1;
50664
50664
  }
@@ -53101,7 +53101,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53101
53101
  exports.processDetector = void 0;
53102
53102
  const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
53103
53103
  const semconv_1 = require_semconv$1();
53104
- const os$4 = __require("os");
53104
+ const os$3 = __require("os");
53105
53105
  /**
53106
53106
  * ProcessDetector will be used to detect the resources related current process running
53107
53107
  * and being instrumented from the NodeJS Process module.
@@ -53123,7 +53123,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53123
53123
  };
53124
53124
  if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
53125
53125
  try {
53126
- const userInfo = os$4.userInfo();
53126
+ const userInfo = os$3.userInfo();
53127
53127
  attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
53128
53128
  } catch (e) {
53129
53129
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -66046,7 +66046,7 @@ function normalizeNumberish(value) {
66046
66046
  }
66047
66047
  //#endregion
66048
66048
  //#region package.json
66049
- var version$1 = "0.13.123";
66049
+ var version$1 = "0.13.124";
66050
66050
  //#endregion
66051
66051
  //#region src/sentry.ts
66052
66052
  const SERVER_APP_NAME = "forge-server";
@@ -76116,7 +76116,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
76116
76116
  var fs$6 = __require("fs");
76117
76117
  var path$5 = __require("path");
76118
76118
  var url$1 = __require("url");
76119
- var os$3 = __require("os");
76119
+ var os$2 = __require("os");
76120
76120
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
76121
76121
  var vars = process.config && process.config.variables || {};
76122
76122
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
@@ -76124,8 +76124,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
76124
76124
  var abi = versions.modules;
76125
76125
  if (versions.deno || process.isBun) abi = "unsupported";
76126
76126
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
76127
- var arch = process.env.npm_config_arch || os$3.arch();
76128
- var platform = process.env.npm_config_platform || os$3.platform();
76127
+ var arch = process.env.npm_config_arch || os$2.arch();
76128
+ var platform = process.env.npm_config_platform || os$2.platform();
76129
76129
  var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
76130
76130
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
76131
76131
  var uv = (versions.uv || "").split(".")[0];
@@ -93969,7 +93969,7 @@ const launchDetached = (launch) => gen(function* () {
93969
93969
  });
93970
93970
  const OpenLive = effect(Open, gen(function* () {
93971
93971
  const open = yield* tryPromise({
93972
- try: () => import("./open-yHTUkd2Y.mjs"),
93972
+ try: () => import("./open-2-reBrpv.mjs"),
93973
93973
  catch: (cause) => new OpenError$1({
93974
93974
  message: "failed to load browser opener",
93975
93975
  cause
@@ -192255,7 +192255,7 @@ var Vce = {
192255
192255
  function Ve() {
192256
192256
  return Vce;
192257
192257
  }
192258
- var Us = os.homedir(), lk = os.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
192258
+ var Us = $U.homedir(), lk = $U.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
192259
192259
  let t = path.join(Us, "Library");
192260
192260
  return {
192261
192261
  data: path.join(t, "Application Support", e),
@@ -202455,12 +202455,12 @@ function qZ(e, t) {
202455
202455
  }
202456
202456
  return n;
202457
202457
  }
202458
- var os$2 = "https://code.claude.com/docs/en", kKe = [
202458
+ var os$1 = "https://code.claude.com/docs/en", kKe = [
202459
202459
  {
202460
202460
  matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
202461
202461
  tip: {
202462
202462
  suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
202463
- docLink: `${os$2}/iam#permission-modes`
202463
+ docLink: `${os$1}/iam#permission-modes`
202464
202464
  }
202465
202465
  },
202466
202466
  {
@@ -202475,7 +202475,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202475
202475
  matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
202476
202476
  tip: {
202477
202477
  suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
202478
- docLink: `${os$2}/settings#environment-variables`
202478
+ docLink: `${os$1}/settings#environment-variables`
202479
202479
  }
202480
202480
  },
202481
202481
  {
@@ -202486,14 +202486,14 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202486
202486
  matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
202487
202487
  tip: {
202488
202488
  suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
202489
- docLink: `${os$2}/hooks`
202489
+ docLink: `${os$1}/hooks`
202490
202490
  }
202491
202491
  },
202492
202492
  {
202493
202493
  matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
202494
202494
  tip: {
202495
202495
  suggestion: "Command hooks require `command`. For exec form (no shell), set `command` to the executable and `args` to its arguments: {\"type\": \"command\", \"command\": \"echo\", \"args\": [\"hi\"]}. For shell form, set `command` to the full shell string: {\"type\": \"command\", \"command\": \"echo hi\"}.",
202496
- docLink: `${os$2}/hooks#exec-form-and-shell-form`
202496
+ docLink: `${os$1}/hooks#exec-form-and-shell-form`
202497
202497
  }
202498
202498
  },
202499
202499
  {
@@ -202508,7 +202508,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202508
202508
  matches: (e) => e.code === "unrecognized_keys",
202509
202509
  tip: {
202510
202510
  suggestion: "Check for typos or refer to the documentation for valid fields",
202511
- docLink: `${os$2}/settings`
202511
+ docLink: `${os$1}/settings`
202512
202512
  }
202513
202513
  },
202514
202514
  {
@@ -202523,13 +202523,13 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202523
202523
  matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
202524
202524
  tip: {
202525
202525
  suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
202526
- docLink: `${os$2}/iam#working-directories`
202526
+ docLink: `${os$1}/iam#working-directories`
202527
202527
  }
202528
202528
  }
202529
202529
  ], AKe = {
202530
- permissions: `${os$2}/iam#configuring-permissions`,
202531
- env: `${os$2}/settings#environment-variables`,
202532
- hooks: `${os$2}/hooks`
202530
+ permissions: `${os$1}/iam#configuring-permissions`,
202531
+ env: `${os$1}/settings#environment-variables`,
202532
+ hooks: `${os$1}/hooks`
202533
202533
  };
202534
202534
  function GZ(e) {
202535
202535
  let t = kKe.find((r) => r.matches(e));
@@ -271073,7 +271073,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
271073
271073
  var dgram = __require("dgram");
271074
271074
  var thunky = require_thunky();
271075
271075
  var events = __require("events");
271076
- var os$1 = __require("os");
271076
+ var os = __require("os");
271077
271077
  var noop = function() {};
271078
271078
  module.exports = function(opts) {
271079
271079
  if (!opts) opts = {};
@@ -271204,14 +271204,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
271204
271204
  return that;
271205
271205
  };
271206
271206
  function defaultInterface() {
271207
- var networks = os$1.networkInterfaces();
271207
+ var networks = os.networkInterfaces();
271208
271208
  var names = Object.keys(networks);
271209
271209
  for (var i = 0; i < names.length; i++) {
271210
271210
  var net = networks[names[i]];
271211
271211
  for (var j = 0; j < net.length; j++) {
271212
271212
  var iface = net[j];
271213
271213
  if (isIPv4(iface.family) && !iface.internal) {
271214
- if (os$1.platform() === "darwin" && names[i] === "en0") return iface.address;
271214
+ if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
271215
271215
  return "0.0.0.0";
271216
271216
  }
271217
271217
  }
@@ -271219,7 +271219,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
271219
271219
  return "127.0.0.1";
271220
271220
  }
271221
271221
  function allInterfaces() {
271222
- var networks = os$1.networkInterfaces();
271222
+ var networks = os.networkInterfaces();
271223
271223
  var names = Object.keys(networks);
271224
271224
  var res = [];
271225
271225
  for (var i = 0; i < names.length; i++) {
@@ -299854,7 +299854,7 @@ function resolveBuildCommitFromEnv(env) {
299854
299854
  * environment descriptor down instead of reporting an honest "unknown".
299855
299855
  */
299856
299856
  function readBakedBuildCommit() {
299857
- return "36ad9a06120f61eef3931979fa6c199d3c862efb";
299857
+ return "52e8c25f0de536143b44ff1ac86510aa93d35772";
299858
299858
  }
299859
299859
  async function resolveServerBuildCommit(input) {
299860
299860
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -314427,9 +314427,17 @@ const RuntimeReplicationLive = effect(RuntimeReplication, gen(function* () {
314427
314427
  concurrency: 1,
314428
314428
  discard: true
314429
314429
  });
314430
+ const retryStuckThreadStreams = (streamKeys) => runtimeId ? forEach(streamKeys, (streamKey) => outbox.resetStuckThreadGeneration({
314431
+ runtimeId,
314432
+ streamKey
314433
+ }).pipe(flatMap((reset) => reset ? enqueueThread(threadIdFromStreamKey(streamKey)) : void_$1)), {
314434
+ concurrency: 1,
314435
+ discard: true
314436
+ }) : void_$1;
314430
314437
  return RuntimeReplication.of({
314431
314438
  start,
314432
- retryReprojectionPending
314439
+ retryReprojectionPending,
314440
+ retryStuckThreadStreams
314433
314441
  });
314434
314442
  }));
314435
314443
  //#endregion
@@ -314764,6 +314772,31 @@ const THREAD_STREAM_PAYLOAD_HASH_MISMATCH_CODE = "THREAD_STREAM_PAYLOAD_HASH_MIS
314764
314772
  */
314765
314773
  const THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE = "THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING";
314766
314774
  /**
314775
+ * Terminal park code for a thread-events reset `refusedResetIsStuck` judges
314776
+ * genuinely stuck: the exact canonical reset shape was already sent once
314777
+ * under this generation and CMMD refused it again with zero acknowledged
314778
+ * progress (forge#3635). Before this code existed, that case fell through to
314779
+ * `failGeneric`, which recorded the raw `THREAD_STREAM_PAYLOAD_CONFLICT` /
314780
+ * `THREAD_STREAM_HEAD_MISMATCH` code -- a code neither
314781
+ * `readReprojectionPendingThreadStreamKeys` nor
314782
+ * `MANUALLY_RECOVERABLE_BLOCKED_CODES` selects, so the manual retry endpoint
314783
+ * could never reach the row.
314784
+ *
314785
+ * Deliberately excluded from `CLAIMABLE_BLOCKED_CODES`,
314786
+ * `STARTUP_RECOVERABLE_BLOCKED_CODES`, and `MANUALLY_RECOVERABLE_BLOCKED_CODES`:
314787
+ * a plain status flip back to `pending` would resend the exact batch CMMD
314788
+ * already refused twice. `enqueueThread` also leaves a row parked under this
314789
+ * code alone on new domain events and the heartbeat (see the stuck check
314790
+ * inside `enqueueThread` below) instead of re-projecting it back to `pending`
314791
+ * the way it does for every other generation, so a stuck row only clears once
314792
+ * the operator retry endpoint calls `RuntimeReplicationOutboxShape`'s
314793
+ * `resetStuckThreadGeneration` (which mints a fresh, never-before-sent repair
314794
+ * generation before re-projecting) or
314795
+ * a genuinely new generation id supersedes it (e.g. the thread archives and
314796
+ * gets a `:final:` id).
314797
+ */
314798
+ const THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE = "THREAD_STREAM_REMOTE_CONFLICT_STUCK";
314799
+ /**
314767
314800
  * Whether a refused batch is the exact canonical reset shape (`fromSequence
314768
314801
  * === 1`, empty previous head, deterministic content) under a generation the
314769
314802
  * local cursor already holds with zero acknowledged progress -- i.e. this
@@ -315041,6 +315074,23 @@ const readReprojectionPendingThreadStreamKeys = gen(function* () {
315041
315074
  AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE}
315042
315075
  `).map((row) => row.streamKey);
315043
315076
  }).pipe(orDie);
315077
+ /**
315078
+ * Thread streams parked under THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE, for the
315079
+ * manual `/api/runtime-replication/retry` endpoint (forge#3635). Deliberately absent
315080
+ * from MANUALLY_RECOVERABLE_BLOCKED_CODES for the same reason
315081
+ * `readReprojectionPendingThreadStreamKeys` is: a plain status flip back to `pending`
315082
+ * would resend the exact reset CMMD already refused twice. `http.ts`'s retry handler
315083
+ * passes these to `RuntimeReplication`'s `retryStuckThreadStreams`, which calls
315084
+ * `resetStuckThreadGeneration` for each of these before re-projecting, so the retry
315085
+ * always sends a fresh, never-before-refused generation.
315086
+ */
315087
+ const readStuckThreadStreamKeys = gen(function* () {
315088
+ return (yield* (yield* SqlClient)`
315089
+ SELECT stream_key AS "streamKey" FROM runtime_replication_outbox
315090
+ WHERE operation = 'thread-events' AND status = 'blocked'
315091
+ AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE}
315092
+ `).map((row) => row.streamKey);
315093
+ }).pipe(orDie);
315044
315094
  const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(function* () {
315045
315095
  const sql = yield* SqlClient;
315046
315096
  const upsertPayload = (input) => {
@@ -315261,6 +315311,12 @@ const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(functi
315261
315311
  now
315262
315312
  }).pipe(orDie);
315263
315313
  };
315314
+ const isThreadStreamParkedStuck = (input) => sql`
315315
+ SELECT runtime_id AS "runtimeId" FROM runtime_replication_outbox
315316
+ WHERE runtime_id = ${input.runtimeId} AND stream_key = ${input.streamKey}
315317
+ AND operation = 'thread-events' AND status = 'blocked'
315318
+ AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE}
315319
+ `.pipe(map$3((rows) => rows.length === 1));
315264
315320
  const enqueueThread = (input) => {
315265
315321
  const now = (/* @__PURE__ */ new Date()).toISOString();
315266
315322
  const baseGenerationId = runtimeThreadGenerationId(input.runtimeId, input.streamKey);
@@ -315269,6 +315325,7 @@ const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(functi
315269
315325
  const previousActiveGeneration = activeRuntimeThreadGenerationId(baseGenerationId, previousCursor?.generationId);
315270
315326
  const generationId = input.historyFinalized ? runtimeThreadFinalGenerationId(input.runtimeId, input.streamKey, input.events) : previousActiveGeneration;
315271
315327
  const replacesGeneration = previousCursor !== void 0 && previousCursor.generationId !== generationId;
315328
+ if (!replacesGeneration && (yield* isThreadStreamParkedStuck(input))) return;
315272
315329
  yield* initializeThreadCursor({
315273
315330
  runtimeId: input.runtimeId,
315274
315331
  streamKey: input.streamKey,
@@ -315569,12 +315626,16 @@ const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(functi
315569
315626
  })).pipe(orDie);
315570
315627
  /**
315571
315628
  * Recovers from a CMMD-reported thread stream conflict (see
315572
- * THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` when this item is
315573
- * not eligible (its payload does not decode, or `refusedResetIsStuck`
315574
- * says the exact reset shape already failed once -- in which case the
315575
- * caller falls back to blocking) and `false` when the row was superseded
315576
- * before this could apply, mirroring `fail`'s own supersession signal.
315577
- * Otherwise replaces the local cursor with a brand-new repair generation
315629
+ * THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` only when this
315630
+ * item's payload does not decode, in which case the caller falls back to
315631
+ * `failGeneric`. When `refusedResetIsStuck` says the exact reset shape
315632
+ * already failed once with zero progress, parks the row under
315633
+ * `THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE` directly (see that code's
315634
+ * doc comment) and returns `true`/`false` the same way the normal repair
315635
+ * path below does -- never `null` -- so the caller never falls through to
315636
+ * `failGeneric`'s raw conflict code (forge#3635). `false` means the row
315637
+ * was superseded before this could apply, mirroring `fail`'s own
315638
+ * supersession signal. Otherwise replaces the local cursor with a brand-new repair generation
315578
315639
  * from sequence 1, empty previous head -- the shape CMMD's reset rule
315579
315640
  * accepts unconditionally -- and parks the row blocked under
315580
315641
  * `THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE`, deferring the
@@ -315593,13 +315654,30 @@ const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(functi
315593
315654
  runtimeId: item.runtimeId,
315594
315655
  streamKey: item.streamKey
315595
315656
  });
315657
+ const now = (/* @__PURE__ */ new Date()).toISOString();
315596
315658
  if (refusedResetIsStuck({
315597
315659
  baseGenerationId,
315598
315660
  refusedGenerationId: parsedBatch.generationId,
315599
315661
  refusedFromSequence: parsedBatch.fromSequence,
315600
315662
  cursor
315601
- })) return null;
315602
- const now = (/* @__PURE__ */ new Date()).toISOString();
315663
+ })) {
315664
+ if ((yield* sql`
315665
+ UPDATE runtime_replication_outbox SET
315666
+ status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
315667
+ last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_STUCK"},
315668
+ last_error = ${`Runtime replication conflict (${failure.code}) refused a thread-events reset that had already been refused once with zero acknowledged progress under this generation. Parked as stuck rather than resent; use the manual retry endpoint to reset it to a fresh generation.`}, updated_at = ${now}
315669
+ WHERE runtime_id = ${item.runtimeId} AND stream_key = ${item.streamKey}
315670
+ AND payload_hash = ${item.payloadHash}
315671
+ RETURNING runtime_id AS "runtimeId"
315672
+ `).length !== 1) return false;
315673
+ yield* logWarning$1("Runtime replication parked a thread-stream repair as stuck after its reset was refused with zero progress.", {
315674
+ runtimeId: item.runtimeId,
315675
+ streamKey: item.streamKey,
315676
+ errorCode: failure.code,
315677
+ refusedGenerationId: parsedBatch.generationId
315678
+ });
315679
+ return true;
315680
+ }
315603
315681
  const repairGenerationId = `${baseGenerationId}:repair:${Crypto.randomUUID()}`;
315604
315682
  const message = `Runtime replication conflict (${failure.code}) refused a thread-events batch; deferring to a full-history re-projection under a new repair generation.`;
315605
315683
  if (!(yield* sql.withTransaction(gen(function* () {
@@ -315667,6 +315745,30 @@ const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(functi
315667
315745
  AND status = 'blocked'
315668
315746
  AND last_error_code = 'RUNTIME_REVOKED'
315669
315747
  `.pipe(asVoid, orDie);
315748
+ const resetStuckThreadGeneration = (input) => {
315749
+ const now = (/* @__PURE__ */ new Date()).toISOString();
315750
+ const repairGenerationId = `${runtimeThreadGenerationId(input.runtimeId, input.streamKey)}:repair:${Crypto.randomUUID()}`;
315751
+ const message = "Runtime replication operator retry reset a stuck thread-stream repair to a new generation.";
315752
+ return sql.withTransaction(gen(function* () {
315753
+ if ((yield* sql`
315754
+ UPDATE runtime_replication_outbox SET
315755
+ status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
315756
+ last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING"},
315757
+ last_error = ${message}, updated_at = ${now}
315758
+ WHERE runtime_id = ${input.runtimeId} AND stream_key = ${input.streamKey}
315759
+ AND operation = 'thread-events'
315760
+ AND last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_STUCK"}
315761
+ RETURNING runtime_id AS "runtimeId"
315762
+ `).length !== 1) return false;
315763
+ yield* replaceThreadCursor({
315764
+ runtimeId: input.runtimeId,
315765
+ streamKey: input.streamKey,
315766
+ generationId: repairGenerationId,
315767
+ now
315768
+ });
315769
+ return true;
315770
+ })).pipe(orDie);
315771
+ };
315670
315772
  const getStatus = () => readRuntimeReplicationOutboxStatus.pipe(provideService(SqlClient, sql));
315671
315773
  const getDiagnostics = () => gen(function* () {
315672
315774
  const [diagnostics] = decodeDiagnostics(yield* sql`
@@ -315697,6 +315799,7 @@ const RuntimeReplicationOutboxLive = effect(RuntimeReplicationOutbox, gen(functi
315697
315799
  acknowledge,
315698
315800
  fail,
315699
315801
  retryBlocked,
315802
+ resetStuckThreadGeneration,
315700
315803
  recover,
315701
315804
  recoverLegacyRevokedInventory,
315702
315805
  getStatus,
@@ -315828,10 +315931,14 @@ const retryHandler = gen(function* () {
315828
315931
  const replication = yield* serviceOption(RuntimeReplication);
315829
315932
  const requeued = yield* retryBlockedRuntimeReplicationOutbox;
315830
315933
  const reprojectionPendingStreamKeys = yield* readReprojectionPendingThreadStreamKeys;
315831
- if (isSome(replication)) yield* replication.value.retryReprojectionPending(reprojectionPendingStreamKeys);
315934
+ const stuckStreamKeys = yield* readStuckThreadStreamKeys;
315935
+ if (isSome(replication)) {
315936
+ yield* replication.value.retryReprojectionPending(reprojectionPendingStreamKeys);
315937
+ yield* replication.value.retryStuckThreadStreams(stuckStreamKeys);
315938
+ }
315832
315939
  const status = yield* readRuntimeReplicationOutboxStatus;
315833
315940
  return jsonUnsafe({
315834
- requeued: requeued + (isSome(replication) ? reprojectionPendingStreamKeys.length : 0),
315941
+ requeued: requeued + (isSome(replication) ? reprojectionPendingStreamKeys.length + stuckStreamKeys.length : 0),
315835
315942
  ...responseFromStatus(status)
315836
315943
  });
315837
315944
  }).pipe(catchTag("AuthError", respondToAuthError));
@@ -325104,7 +325211,7 @@ const connectLoginCommand = make$33("login", {
325104
325211
  })).json();
325105
325212
  },
325106
325213
  openBrowser: async (url) => {
325107
- const open = (await import("./open-yHTUkd2Y.mjs")).default;
325214
+ const open = (await import("./open-2-reBrpv.mjs")).default;
325108
325215
  await open(url);
325109
325216
  return true;
325110
325217
  },