@bendyline/gezel-service 1.0.3 → 1.0.4

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 (82) hide show
  1. package/dist/bin/gezeld.js +486 -84
  2. package/dist/handboek-content/whats-new/1.26226.md +89 -0
  3. package/dist/index.d.ts +45 -2
  4. package/dist/index.js +486 -84
  5. package/dist/memory/embed-worker.js +22 -3
  6. package/dist/ui/assets/{TerminalCodeEditor-CXspNipu.js → TerminalCodeEditor-BuMcfTS4.js} +2 -2
  7. package/dist/ui/assets/{_.contribution-Bi8h5rS7.js → _.contribution-CZO2XAKt.js} +1 -1
  8. package/dist/ui/assets/{chunk-6N2J7C2B-Du7clJkV.js → chunk-6N2J7C2B-BNHR_rFP.js} +1 -1
  9. package/dist/ui/assets/chunk-ILCJ3WFD-CUnKgI_I.js +4 -0
  10. package/dist/ui/assets/chunk-S5PCVMKU-i2wfHmvU.js +6 -0
  11. package/dist/ui/assets/{chunk-WATAOG4Y-BnCgSncI.js → chunk-WATAOG4Y-BbL-tDgQ.js} +1 -1
  12. package/dist/ui/assets/{cpp.contribution-CIgWElWw.js → cpp.contribution-BJzhk0Zj.js} +1 -1
  13. package/dist/ui/assets/{csharp.contribution-DDiJzXzB.js → csharp.contribution-BR9K2qKj.js} +1 -1
  14. package/dist/ui/assets/{css.contribution-Dc5E_mNx.js → css.contribution-CfkVmdah.js} +1 -1
  15. package/dist/ui/assets/{cssMode-BN_VZB6O.js → cssMode-MMeGUmV2.js} +1 -1
  16. package/dist/ui/assets/dist-8uH4DcHx.js +2 -0
  17. package/dist/ui/assets/{dist-D4PZf1Q3.js → dist-CFOmf7nD.js} +2 -2
  18. package/dist/ui/assets/{dockerfile.contribution-BQsaSuAJ.js → dockerfile.contribution-IZMl57B5.js} +1 -1
  19. package/dist/ui/assets/docx-BEJpkI6I.js +2 -0
  20. package/dist/ui/assets/extract-UTAOG7LV-BY3SCkzz.js +1 -0
  21. package/dist/ui/assets/{go.contribution-CPvqBST7.js → go.contribution-BxCBwhZs.js} +1 -1
  22. package/dist/ui/assets/{handlebars-DJvRMy-I.js → handlebars-CreIkams.js} +1 -1
  23. package/dist/ui/assets/handlebars.contribution-BGlyL-dm.js +2 -0
  24. package/dist/ui/assets/{html-Becff5XU.js → html-BSrz1IKi.js} +1 -1
  25. package/dist/ui/assets/html.contribution-CucOMFQ_.js +2 -0
  26. package/dist/ui/assets/{htmlMode-BFmyC6t-.js → htmlMode-DRG7XY5Z.js} +1 -1
  27. package/dist/ui/assets/{index-w_CaRVtV.js → index-Bqu-32S3.js} +177 -172
  28. package/dist/ui/assets/infer-D6e75m-s.js +2 -0
  29. package/dist/ui/assets/{ini.contribution-DFSSA0eY.js → ini.contribution-CGX5OdXM.js} +1 -1
  30. package/dist/ui/assets/{java.contribution-Bdvvskrf.js → java.contribution-CVhfJ_us.js} +1 -1
  31. package/dist/ui/assets/{javascript-DByfaclN.js → javascript-QelFp9t5.js} +1 -1
  32. package/dist/ui/assets/javascript.contribution--no0ijCu.js +2 -0
  33. package/dist/ui/assets/{jsonMode-CxkV6dJT.js → jsonMode-T4Ss3lDu.js} +1 -1
  34. package/dist/ui/assets/{kotlin.contribution-D8eqi2Fi.js → kotlin.contribution-DxFbGDYA.js} +1 -1
  35. package/dist/ui/assets/layouts-2A7QNF6Y-CWQp8HDd.js +1 -0
  36. package/dist/ui/assets/{less.contribution-DFRb86Ld.js → less.contribution-g9R8XRZf.js} +1 -1
  37. package/dist/ui/assets/{lua.contribution-BgktkcsF.js → lua.contribution-YqDsEy8Z.js} +1 -1
  38. package/dist/ui/assets/{markdown.contribution-BKqBKD_g.js → markdown.contribution-DboXnPnx.js} +1 -1
  39. package/dist/ui/assets/{monaco-CyhM025v.js → monaco-BdFw56DI.js} +3 -3
  40. package/dist/ui/assets/{monaco-base-hW9ht_BG.js → monaco-base-DrP3D13F.js} +1 -1
  41. package/dist/ui/assets/{monaco-setup-Cquhh1OE.js → monaco-setup-QyF22y9h.js} +1 -1
  42. package/dist/ui/assets/{monaco.contribution-D9-bPyBf.js → monaco.contribution-BkqIxESv.js} +2 -2
  43. package/dist/ui/assets/{monaco.contribution-Cle0Lbgx.js → monaco.contribution-CMzPFHtO.js} +2 -2
  44. package/dist/ui/assets/{monaco.contribution-Dl8lHLi5.js → monaco.contribution-FA_ESP-W.js} +2 -2
  45. package/dist/ui/assets/{monaco.contribution-BVrZ1Anc.js → monaco.contribution-XEpc63ty.js} +2 -2
  46. package/dist/ui/assets/{php.contribution-CcYxMawn.js → php.contribution-rnpik0Kk.js} +1 -1
  47. package/dist/ui/assets/pptx-DZdEoDP9.js +6 -0
  48. package/dist/ui/assets/{python-DivQov6J.js → python-CF7X5t9Q.js} +1 -1
  49. package/dist/ui/assets/python.contribution-CyJGCfSq.js +2 -0
  50. package/dist/ui/assets/{ruby.contribution-DNudSRrU.js → ruby.contribution-4ZyPGu2E.js} +1 -1
  51. package/dist/ui/assets/{rust.contribution-BqG2lQpP.js → rust.contribution-Cl_B3X2G.js} +1 -1
  52. package/dist/ui/assets/{scss.contribution-DDxPnF-K.js → scss.contribution-BO02gVLZ.js} +1 -1
  53. package/dist/ui/assets/{shell.contribution-Q7wVL7zv.js → shell.contribution-Dn9Y0uHs.js} +1 -1
  54. package/dist/ui/assets/{sql.contribution-B9ndj3QH.js → sql.contribution-Bog_5Rc8.js} +1 -1
  55. package/dist/ui/assets/{swift.contribution-D_FDdL6S.js → swift.contribution-CfiAr7AX.js} +1 -1
  56. package/dist/ui/assets/{terminal-monaco-setup-BdF0mUfG.js → terminal-monaco-setup-B-EnkGiy.js} +1 -1
  57. package/dist/ui/assets/{tsMode-JVR2Jo5q.js → tsMode-B2WuLuPL.js} +1 -1
  58. package/dist/ui/assets/{typescript-1ZOjdUnd.js → typescript-Bv_J1tKT.js} +1 -1
  59. package/dist/ui/assets/typescript.contribution-CM0XQCYi.js +2 -0
  60. package/dist/ui/assets/xlsx-DDBRN-IU.js +4 -0
  61. package/dist/ui/assets/{xml-DrdFGpo2.js → xml-DMV-f4tm.js} +1 -1
  62. package/dist/ui/assets/xml.contribution-QqmOB9gT.js +2 -0
  63. package/dist/ui/assets/{yaml-DYQPB5Mj.js → yaml-Bb6mZXY5.js} +1 -1
  64. package/dist/ui/assets/yaml.contribution-DCDnWhHR.js +2 -0
  65. package/dist/ui/index.html +10 -10
  66. package/package.json +11 -11
  67. package/dist/ui/assets/chunk-ILCJ3WFD-fHFoZ9wm.js +0 -4
  68. package/dist/ui/assets/chunk-S5PCVMKU-DopU3Q9j.js +0 -6
  69. package/dist/ui/assets/dist-BwDZUtiH.js +0 -2
  70. package/dist/ui/assets/docx-IZY6hX8A.js +0 -2
  71. package/dist/ui/assets/extract-UTAOG7LV-BZ_L7lLL.js +0 -1
  72. package/dist/ui/assets/handlebars.contribution-BNbstxN6.js +0 -2
  73. package/dist/ui/assets/html.contribution-CPDzzJ0M.js +0 -2
  74. package/dist/ui/assets/infer-v-bCXeT9.js +0 -2
  75. package/dist/ui/assets/javascript.contribution-CPCuprn0.js +0 -2
  76. package/dist/ui/assets/layouts-2A7QNF6Y-D94iKLLQ.js +0 -1
  77. package/dist/ui/assets/pptx-CsdVR0xn.js +0 -6
  78. package/dist/ui/assets/python.contribution-CiRaN0sm.js +0 -2
  79. package/dist/ui/assets/typescript.contribution-_s-8TvIE.js +0 -2
  80. package/dist/ui/assets/xlsx-CdLmEC42.js +0 -4
  81. package/dist/ui/assets/xml.contribution-DUPYAKAC.js +0 -2
  82. package/dist/ui/assets/yaml.contribution-CRBo99nF.js +0 -2
package/dist/index.js CHANGED
@@ -320,6 +320,13 @@ function queryInstruction() {
320
320
  if (/bge-/i.test(model)) return "Represent this sentence for searching relevant passages: ";
321
321
  return "";
322
322
  }
323
+ function isRetryablePipelineLoadFailure(error) {
324
+ const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
325
+ const code = error?.code ?? "";
326
+ return /(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|UND_ERR_|fetch failed|network(?: request)? failed|socket hang up|timed?\s*out|HTTP\s+(?:408|425|429|5\d\d)|TLS|SSL|certificate)/i.test(
327
+ `${code} ${message}`
328
+ );
329
+ }
323
330
  async function loadPipeline() {
324
331
  if (!pipelinePromise) {
325
332
  pipelinePromise = (async () => {
@@ -333,7 +340,11 @@ async function loadPipeline() {
333
340
  } catch (err) {
334
341
  const missing = isMissingModule(err, TRANSFORMERS_MODULE);
335
342
  const message = missing ? "Local memory embeddings are an optional npm feature. Install @huggingface/transformers@^3.8.1 alongside @bendyline/gezel-service (see the service README)." : err instanceof Error ? err.message : String(err);
336
- throw new PipelineLoadError(message, missing);
343
+ throw new PipelineLoadError(
344
+ message,
345
+ missing,
346
+ !missing && isRetryablePipelineLoadFailure(err)
347
+ );
337
348
  }
338
349
  })();
339
350
  pipelinePromise.catch(() => {
@@ -374,10 +385,17 @@ var init_embed_core = __esm({
374
385
  * damaged runtime.
375
386
  */
376
387
  optionalPeerMissing;
377
- constructor(message, optionalPeerMissing = false) {
388
+ /**
389
+ * Network/model-registry failures can clear without a process restart. The
390
+ * host uses this bit to apply a short retry cooldown instead of permanently
391
+ * disabling embeddings after one failed first-use download.
392
+ */
393
+ retryable;
394
+ constructor(message, optionalPeerMissing = false, retryable = false) {
378
395
  super(message);
379
396
  this.name = "PipelineLoadError";
380
397
  this.optionalPeerMissing = optionalPeerMissing;
398
+ this.retryable = retryable;
381
399
  }
382
400
  };
383
401
  pipelinePromise = null;
@@ -388,6 +406,7 @@ var init_embed_core = __esm({
388
406
  var embeddings_exports = {};
389
407
  __export(embeddings_exports, {
390
408
  EmbeddingsDisabledError: () => EmbeddingsDisabledError,
409
+ EmbeddingsUnavailableError: () => EmbeddingsUnavailableError,
391
410
  embed: () => embed,
392
411
  embedBatch: () => embedBatch,
393
412
  embedQuery: () => embedQuery,
@@ -400,7 +419,7 @@ function disabledByEnv() {
400
419
  return raw === "1" || raw?.toLowerCase() === "true";
401
420
  }
402
421
  function embeddingsDisabledReason() {
403
- return disabledByEnv() ? ENV_DISABLED_REASON : disabledReason;
422
+ return disabledByEnv() ? ENV_DISABLED_REASON : disabledReason ?? currentUnavailableReason();
404
423
  }
405
424
  function workerEntry() {
406
425
  if (process.env.VITEST) return null;
@@ -436,7 +455,12 @@ function onMessage(msg) {
436
455
  pending.delete(msg.id);
437
456
  if (msg.error) {
438
457
  if (msg.fatal) markDisabled(msg.error, msg.optionalPeerMissing ?? false);
439
- p.reject(new Error(msg.error));
458
+ if (msg.retryable) {
459
+ markTemporarilyUnavailable(msg.error);
460
+ p.reject(new EmbeddingsUnavailableError(unavailableReason ?? msg.error));
461
+ } else {
462
+ p.reject(new Error(msg.error));
463
+ }
440
464
  return;
441
465
  }
442
466
  p.resolve(msg.vectors ?? []);
@@ -469,12 +493,31 @@ async function embedInProcess(texts) {
469
493
  return await runEmbed(texts);
470
494
  } catch (err) {
471
495
  if (err instanceof PipelineLoadError) {
496
+ if (err.retryable) {
497
+ markTemporarilyUnavailable(err.message);
498
+ throw new EmbeddingsUnavailableError(unavailableReason ?? err.message);
499
+ }
472
500
  markDisabled(err.message, err.optionalPeerMissing);
473
501
  throw new EmbeddingsDisabledError(disabledReason ?? err.message);
474
502
  }
475
503
  throw err;
476
504
  }
477
505
  }
506
+ function currentUnavailableReason() {
507
+ if (!unavailableReason) return null;
508
+ if (Date.now() < unavailableUntil) return unavailableReason;
509
+ unavailableReason = null;
510
+ unavailableUntil = 0;
511
+ return null;
512
+ }
513
+ function markTemporarilyUnavailable(message) {
514
+ unavailableReason = firstLine(message);
515
+ unavailableUntil = Date.now() + EMBEDDING_RETRY_COOLDOWN_MS;
516
+ log10.warn(
517
+ `[memory] embedding model load failed; semantic search will retry after ${Math.ceil(EMBEDDING_RETRY_COOLDOWN_MS / 1e3)}s.`
518
+ );
519
+ log10.warn("[memory] underlying transient error:", message);
520
+ }
478
521
  function markDisabled(message, optionalPeerMissing = false) {
479
522
  if (disabledReason) return;
480
523
  disabledReason = firstLine(message);
@@ -490,11 +533,14 @@ function markDisabled(message, optionalPeerMissing = false) {
490
533
  async function embedMany(texts) {
491
534
  if (disabledByEnv()) throw new EmbeddingsDisabledError(ENV_DISABLED_REASON);
492
535
  if (disabledReason) throw new EmbeddingsDisabledError(disabledReason);
536
+ const temporaryReason = currentUnavailableReason();
537
+ if (temporaryReason) throw new EmbeddingsUnavailableError(temporaryReason);
493
538
  const w = ensureWorker();
494
539
  if (w) {
495
540
  try {
496
541
  return await sendToWorker(w, texts);
497
542
  } catch (err) {
543
+ if (err instanceof EmbeddingsUnavailableError) throw err;
498
544
  if (disabledReason) throw new EmbeddingsDisabledError(disabledReason);
499
545
  log10.warn(`[memory] embed via worker failed; retrying in-process: ${describe(err)}`);
500
546
  }
@@ -519,7 +565,7 @@ async function embedBatch(texts) {
519
565
  if (texts.length === 0) return [];
520
566
  return embedMany(texts);
521
567
  }
522
- var log10, EmbeddingsDisabledError, disabledReason, ENV_DISABLED_REASON, worker, workerUsable, crashCount, nextId, pending;
568
+ var log10, EmbeddingsDisabledError, EmbeddingsUnavailableError, disabledReason, unavailableReason, unavailableUntil, EMBEDDING_RETRY_COOLDOWN_MS, ENV_DISABLED_REASON, worker, workerUsable, crashCount, nextId, pending;
523
569
  var init_embeddings = __esm({
524
570
  "src/memory/embeddings.ts"() {
525
571
  "use strict";
@@ -528,12 +574,25 @@ var init_embeddings = __esm({
528
574
  log10 = createLogger10("memory");
529
575
  EmbeddingsDisabledError = class extends Error {
530
576
  code = "EMBEDDINGS_DISABLED";
577
+ retryable = false;
531
578
  constructor(reason) {
532
579
  super(`memory embeddings disabled: ${reason}`);
533
580
  this.name = "EmbeddingsDisabledError";
534
581
  }
535
582
  };
583
+ EmbeddingsUnavailableError = class extends EmbeddingsDisabledError {
584
+ code = "EMBEDDINGS_UNAVAILABLE";
585
+ retryable = true;
586
+ constructor(reason) {
587
+ super(reason);
588
+ this.name = "EmbeddingsUnavailableError";
589
+ this.message = `memory embeddings temporarily unavailable: ${reason}`;
590
+ }
591
+ };
536
592
  disabledReason = null;
593
+ unavailableReason = null;
594
+ unavailableUntil = 0;
595
+ EMBEDDING_RETRY_COOLDOWN_MS = 6e4;
537
596
  ENV_DISABLED_REASON = "disabled by GEZEL_DISABLE_EMBEDDINGS";
538
597
  worker = null;
539
598
  workerUsable = true;
@@ -14085,15 +14144,13 @@ import { createLogger as createLogger4 } from "@bendyline/gezel";
14085
14144
  // src/model-profile/behaviors/fabrication-detect-claim-without-tool.ts
14086
14145
  var PROJECT_CREATION_VERBS = [
14087
14146
  "created",
14088
- "initiated",
14089
14147
  "initialized",
14090
14148
  "set\\s+up",
14091
14149
  "spun\\s+up",
14092
- "started",
14093
14150
  "made",
14094
- "bootstrapped",
14095
- "kicked\\s+off"
14151
+ "bootstrapped"
14096
14152
  ].join("|");
14153
+ var PROJECT_WORK_START_VERBS = ["initiated", "started", "kicked\\s+off"].join("|");
14097
14154
  var TASK_CREATION_VERBS = ["created", "added", "queued", "set\\s+up"].join("|");
14098
14155
  var GEZEL_CREATION_VERBS = [
14099
14156
  "created",
@@ -14154,6 +14211,18 @@ var CLAIM_RULES = [
14154
14211
  claim: "recruited a voorman",
14155
14212
  nudge: "You told the user you recruited a voorman, but you did not call `create_gezel`, `ensure_gezel`, or `start_project` this turn. Call the right one now, and only report recruitment after it succeeds."
14156
14213
  },
14214
+ {
14215
+ // Ambiguous kickoff verbs only become a container-creation claim when
14216
+ // the prose explicitly says this is a new/separate project. Otherwise
14217
+ // the next rule treats them as starting work in the current project.
14218
+ pattern: new RegExp(
14219
+ String.raw`\bI(?:'ve|\s+have|\s+just|\s+already)?\s+(?:` + PROJECT_WORK_START_VERBS + String.raw`)\s+(?:the|a|an)\s+(?:new|fresh|separate|another|dedicated)\s+project\b`,
14220
+ "i"
14221
+ ),
14222
+ requiredTools: ["start_project"],
14223
+ claim: "created a project",
14224
+ nudge: "You told the user you started a new project, but you did not call `start_project` this turn. Call it now with the user-intended name and project brief, or correct the claim."
14225
+ },
14157
14226
  {
14158
14227
  pattern: new RegExp(
14159
14228
  String.raw`\bI(?:'ve|\s+have|\s+just|\s+already)?\s+(?:` + PROJECT_CREATION_VERBS + String.raw`)\s+(?:the|a|an)\s+` + // Optional name placed between "the" and "project". Forms
@@ -14175,6 +14244,15 @@ var CLAIM_RULES = [
14175
14244
  claim: "created a project",
14176
14245
  nudge: "You told the user you created the project, but you did not call `start_project` this turn. Call it now with the user-intended name and a real `about` + `missionObjectives` derived from their request."
14177
14246
  },
14247
+ {
14248
+ pattern: new RegExp(
14249
+ String.raw`\bI(?:'ve|\s+have|\s+just|\s+already)?\s+(?:` + PROJECT_WORK_START_VERBS + String.raw`)\s+(?:(?:work|the\s+work)\s+(?:on|for)\s+)?(?:the|this|that|our|your|a|an)\s+project\b`,
14250
+ "i"
14251
+ ),
14252
+ requiredTools: ["invoke_craftbook", "create_task", "start_project"],
14253
+ claim: "started work in the current project",
14254
+ nudge: "You told the user project work started, but no project-local action succeeded this turn. Use `invoke_craftbook` for a selected procedure, `create_task` for an ad-hoc task, or `start_project` only when the user explicitly requested a separate project. Do not repeat the kickoff claim until the action returns successfully."
14255
+ },
14178
14256
  {
14179
14257
  pattern: new RegExp(
14180
14258
  String.raw`\bI(?:'ve|\s+have|\s+just|\s+already)?\s+(?:` + GEZEL_CREATION_VERBS + String.raw`)\s+(?:the|a|an)\s+(?:new\s+)?gezel\b`,
@@ -15097,13 +15175,26 @@ var BUILD_REQUEST_RE = new RegExp(
15097
15175
  function looksLikeNewBuildRequest(text) {
15098
15176
  return BUILD_REQUEST_RE.test(text);
15099
15177
  }
15178
+ function explicitlyRequestsSeparateProject(text) {
15179
+ return /\b(?:new|fresh|separate|another|dedicated)\s+(?:(?:standalone|independent)\s+)?(?:project|workspace)\b/i.test(
15180
+ text
15181
+ ) || /\b(?:as|in|into|under|for)\s+(?:a|an)\s+(?:new|fresh|separate|another|dedicated)\s+(?:project|workspace)\b/i.test(
15182
+ text
15183
+ );
15184
+ }
15100
15185
  var MEESTER_BUILD_PRELUDE = '(System note for this turn: the user is asking for a new substantive build. Make ONE tool call:\n \u2022 `start_project({ name, about, missionObjectives, taskDescription })`. Creates a fresh project, selects an appropriate lead/team for the effective execution mode, creates the kickoff task, and hands it off (the work starts in a task-scoped session). Use this for every "build me a website / game / app / dashboard" request, from a single-file prototype through multimodal work. `taskDescription` must ask the lead to ship the actual requested deliverable, not to create a plan. For browser games/sites, name `index.html` and the acceptance criteria (workspace-relative path; do not prefix `workspace/`).\nAfter the macro returns, your turn is done \u2014 tell the user the lead is on it. Do NOT call `create_gezel`, `update_project`, `create_task`, or `message_gezel` separately \u2014 the macro handles them. Do NOT reuse the existing "Default" project; the macro creates a fresh dedicated one.)';
15101
15186
  var PromptMeesterBuildPrelude = {
15102
15187
  id: "prompt.meester-build-prelude",
15103
15188
  description: 'Prepends a system note to the user prompt when the active gezel is the Meester and the message looks like a "build me X" request, anchoring the first-call expectation on `start_project`.',
15104
15189
  userPromptPrelude(ctx) {
15105
15190
  if (!ctx.isMeester) return null;
15191
+ if (ctx.messageOrigin !== "direct-user") return null;
15192
+ if (/^\s*\[Answer to:/i.test(ctx.userText)) return null;
15106
15193
  if (!looksLikeNewBuildRequest(ctx.userText)) return null;
15194
+ if (ctx.projectId !== "default" && !explicitlyRequestsSeparateProject(ctx.userText)) {
15195
+ return null;
15196
+ }
15197
+ if (!ctx.availableToolNames.includes("start_project")) return null;
15107
15198
  return MEESTER_BUILD_PRELUDE;
15108
15199
  }
15109
15200
  };
@@ -18152,6 +18243,12 @@ var AnthropicCliSession = class extends StreamingSessionBase {
18152
18243
  providerState() {
18153
18244
  return this.claudeSessionIdCache ? { claudeCliSessionId: this.claudeSessionIdCache } : {};
18154
18245
  }
18246
+ getRegisteredToolNames() {
18247
+ const context = this.deps.context;
18248
+ return [
18249
+ .../* @__PURE__ */ new Set([...context.allowedBuiltinTools ?? [], ...context.allowedMcpTools ?? []])
18250
+ ];
18251
+ }
18155
18252
  async disconnect() {
18156
18253
  this.aborted = true;
18157
18254
  try {
@@ -19700,6 +19797,10 @@ var AnthropicSession = class extends StreamingSessionBase {
19700
19797
  * resolves and persisted on `ChatMessage.reasoning`.
19701
19798
  */
19702
19799
  lastTurnReasoning = "";
19800
+ getRegisteredToolNames() {
19801
+ const bridgeNames = this.deps.bridges.isEmpty() ? [] : this.deps.bridges.getAnthropicTools().map((tool) => tool.name);
19802
+ return [.../* @__PURE__ */ new Set([...bridgeNames, ...this.externalToolNames])];
19803
+ }
19703
19804
  capturedToolCalls() {
19704
19805
  return [...this.capturedCalls];
19705
19806
  }
@@ -33321,7 +33422,7 @@ var PROJECT_MACRO_INTERCEPT_CAP = 2;
33321
33422
  var PROJECT_MACRO_FAILURE_CAP = 2;
33322
33423
  function deriveProjectMacroClosing(firstResult) {
33323
33424
  const projectMatch = firstResult.match(/Started (?:project|job) "([^"]+)"/);
33324
- const leadMatch = firstResult.match(/Recruited (\S+) as (voorman|ambachtsman|lead)/);
33425
+ const leadMatch = firstResult.match(/Recruited (\S+) as (voorman|builder|lead)/i);
33325
33426
  const projectName = projectMatch?.[1];
33326
33427
  const leadName = leadMatch?.[1];
33327
33428
  const leadRole = leadMatch?.[2];
@@ -37273,6 +37374,10 @@ var OpenAISession = class extends StreamingSessionBase {
37273
37374
  capturedToolCalls() {
37274
37375
  return [...this.capturedCalls];
37275
37376
  }
37377
+ getRegisteredToolNames() {
37378
+ const bridgeNames = this.deps.bridges.isEmpty() ? [] : this.deps.bridges.getOpenAITools().map((tool) => tool.name);
37379
+ return [.../* @__PURE__ */ new Set([...bridgeNames, ...this.externalToolNames])];
37380
+ }
37276
37381
  async sendAndWait(prompt, opts) {
37277
37382
  return runInQueue(this.deps.queue, opts?.queue, () => this.sendAndWaitInner(prompt, opts));
37278
37383
  }
@@ -40190,14 +40295,15 @@ async function evalCheck(c, ws, deps) {
40190
40295
  }
40191
40296
  async function evalCheckInner(c, ws, deps) {
40192
40297
  const usesArtifact = c.artifact === true;
40193
- const reader = usesArtifact ? {
40298
+ const artifactReader = {
40194
40299
  read: ws.readArtifact ?? (async () => null),
40195
40300
  list: ws.listArtifacts ?? (async () => []),
40196
40301
  // Carry the byte reader across the artifact swap; without it a
40197
40302
  // `verifyImageBytes` check on an artifact deliverable would report
40198
40303
  // "no binary reads" even though the surface supports them.
40199
40304
  ...ws.readArtifactBytes ? { readBytes: ws.readArtifactBytes.bind(ws) } : {}
40200
- } : ws;
40305
+ };
40306
+ const reader = usesArtifact ? artifactReader : ws;
40201
40307
  switch (c.kind) {
40202
40308
  case "minBytes": {
40203
40309
  const r = await fileMinBytes(reader, c.file, c.bytes);
@@ -40625,7 +40731,12 @@ ${hint}` : ""}`,
40625
40731
  };
40626
40732
  }
40627
40733
  case "markdownHeadingsMatch": {
40628
- const result = await markdownHeadingsMatch(reader, c.file, c.outlineFile);
40734
+ const result = await markdownHeadingsMatch(
40735
+ reader,
40736
+ c.file,
40737
+ c.outlineFile,
40738
+ c.outlineArtifact ? artifactReader : ws
40739
+ );
40629
40740
  return {
40630
40741
  ok: result.ok,
40631
40742
  detail: result.detail,
@@ -42540,6 +42651,11 @@ function filterPromptToolDirectives(args) {
42540
42651
  if (rejectedLines.size === 0) return args.prompt;
42541
42652
  return args.prompt.split("\n").filter((_, index) => !rejectedLines.has(index + 1)).join("\n").replace(/\n{4,}/g, "\n\n\n");
42542
42653
  }
42654
+ function capabilitySafeCorrectivePrompt(args) {
42655
+ const filtered = filterPromptToolDirectives(args).trim();
42656
+ if (filtered) return filtered;
42657
+ return "Your previous reply claimed an action succeeded without live evidence. Use only an appropriate action tool visible in your current roster, or explain that the action cannot be performed here. Do not repeat the success claim until an available action returns successfully.";
42658
+ }
42543
42659
 
42544
42660
  // src/chat/provider-capabilities.ts
42545
42661
  function providerUsesManagedMcpBridge(name) {
@@ -46378,6 +46494,21 @@ function sameFromBucket(a, b) {
46378
46494
  if (!a || !b) return false;
46379
46495
  return a.gezelId === b.gezelId;
46380
46496
  }
46497
+ function resolveTurnMessageOrigin(opts) {
46498
+ if (opts?.messageOrigin) return opts.messageOrigin;
46499
+ if (opts?.from) return "cross-gezel";
46500
+ if (opts?.ambient || opts?.nudge || opts?.lane === "background") return "background-nudge";
46501
+ return "direct-user";
46502
+ }
46503
+ function liveTurnToolNames(session) {
46504
+ const names = /* @__PURE__ */ new Set();
46505
+ for (const name of session?.getRegisteredToolNames?.() ?? []) {
46506
+ names.add(name);
46507
+ const qualified = name.match(/^mcp__.+?__(.+)$/)?.[1];
46508
+ if (qualified && qualified !== "*") names.add(qualified);
46509
+ }
46510
+ return [...names];
46511
+ }
46381
46512
  async function resolveCatalogParameterSize(catalog, catalogId) {
46382
46513
  if (!catalogId) return void 0;
46383
46514
  try {
@@ -47779,6 +47910,7 @@ ${JSON.stringify(run.output, null, 2)}`;
47779
47910
  coalescable: false,
47780
47911
  lane: void 0,
47781
47912
  ambient: false,
47913
+ messageOrigin: "direct-user",
47782
47914
  continuationMaxTokens: void 0,
47783
47915
  hidden: false,
47784
47916
  nudge: false,
@@ -48084,11 +48216,14 @@ ${JSON.stringify(run.output, null, 2)}`;
48084
48216
  const full = await this.store.getSession(args.gezelId, active.id);
48085
48217
  if (full) return full;
48086
48218
  }
48219
+ const parsedTaskRef = parseTaskRef2(args.taskRef);
48220
+ const task = parsedTaskRef ? await this.store.readTask(parsedTaskRef.projectId, parsedTaskRef.num).catch(() => null) : null;
48087
48221
  return this.createSession({
48088
48222
  gezelId: args.gezelId,
48089
48223
  projectId: args.projectId,
48090
48224
  taskRef: args.taskRef,
48091
- ...args.stepId ? { stepId: args.stepId } : {}
48225
+ ...args.stepId ? { stepId: args.stepId } : {},
48226
+ ...task?.roleBasedNameOnlyMode !== void 0 ? { roleBasedNameOnlyMode: task.roleBasedNameOnlyMode } : {}
48092
48227
  });
48093
48228
  }
48094
48229
  /** Late-bind the fitness manager (see the field's docblock). */
@@ -48215,6 +48350,7 @@ ${JSON.stringify(run.output, null, 2)}`;
48215
48350
  */
48216
48351
  async startHandoffSession(args) {
48217
48352
  const dispatchConfig = await this.store.readConfig();
48353
+ const roleBasedNameOnlyMode = args.roleBasedNameOnlyMode ?? dispatchConfig.roleBasedNameOnlyMode ?? false;
48218
48354
  const dispatchGezel = await this.store.getGezel(args.gezelId);
48219
48355
  const dispatchProviderName = await this.resolveProviderName(
48220
48356
  args.gezelId,
@@ -48263,6 +48399,7 @@ ${JSON.stringify(run.output, null, 2)}`;
48263
48399
  projectId: args.projectId,
48264
48400
  taskRef: args.taskRef,
48265
48401
  stepId: dispatchStepId,
48402
+ roleBasedNameOnlyMode,
48266
48403
  ...args.nightShift ? { nightShift: true } : {},
48267
48404
  ...routed && args.capabilityFloor ? {
48268
48405
  routedModel: {
@@ -48319,9 +48456,15 @@ ${stepArc}
48319
48456
  }
48320
48457
  }
48321
48458
  const selfHandoff = args.fromGezelId !== void 0 && args.fromGezelId === args.gezelId;
48322
- const seed = args.kind === "entry" ? `${entryPreface}You've been assigned task ${args.taskRef} (step \`${dispatchStepId}\`). Follow the step instructions already in your prompt \u2014 make the first tool call they name this turn. Append focused notes with \`write_task_note\` as you go. When the step is done, call \`advance_task_step\` to hand off to whoever's next.` : selfHandoff ? `Task ${args.taskRef} has advanced to the next step \u2014 \`${dispatchStepId}\`, which is yours as well. Please continue: follow the step instructions already in your prompt \u2014 make the first tool call they name this turn. Append focused notes with \`write_task_note\` as you go so the next gezel can pick up where you left off. When the step is done, call \`advance_task_step\` to hand off to whoever's next.` : `${args.fromGezelName ? `${args.fromGezelName} has` : "The previous step has been completed and"} handed step \`${dispatchStepId}\` of task ${args.taskRef} to you. Follow the step instructions already in your prompt \u2014 make the first tool call they name this turn. Append focused notes with \`write_task_note\` as you go so the next gezel can pick up where you left off. When the step is done, call \`advance_task_step\` to hand off to whoever's next.`;
48459
+ const previousGezel = !selfHandoff && args.fromGezelId ? await this.store.getGezel(args.fromGezelId).catch(() => null) : null;
48460
+ const fromGezelDisplayName = previousGezel ? displayName2(
48461
+ { name: previousGezel.name, roleBasedName: previousGezel.roleBasedName },
48462
+ roleBasedNameOnlyMode
48463
+ ) : roleBasedNameOnlyMode ? void 0 : args.fromGezelName;
48464
+ const seed = args.kind === "entry" ? `${entryPreface}You've been assigned task ${args.taskRef} (step \`${dispatchStepId}\`). Follow the step instructions already in your prompt \u2014 make the first tool call they name this turn. Append focused notes with \`write_task_note\` as you go. When the step is done, call \`advance_task_step\` to hand off to whoever's next.` : selfHandoff ? `Task ${args.taskRef} has advanced to the next step \u2014 \`${dispatchStepId}\`, which is yours as well. Please continue: follow the step instructions already in your prompt \u2014 make the first tool call they name this turn. Append focused notes with \`write_task_note\` as you go so the next gezel can pick up where you left off. When the step is done, call \`advance_task_step\` to hand off to whoever's next.` : `${fromGezelDisplayName ? `${fromGezelDisplayName} has` : "The previous step has been completed and"} handed step \`${dispatchStepId}\` of task ${args.taskRef} to you. Follow the step instructions already in your prompt \u2014 make the first tool call they name this turn. Append focused notes with \`write_task_note\` as you go so the next gezel can pick up where you left off. When the step is done, call \`advance_task_step\` to hand off to whoever's next.`;
48323
48465
  this.trackBackground(
48324
48466
  this.sendWithBusyRetry(session.id, seed, {
48467
+ messageOrigin: "system",
48325
48468
  ...args.lane ? { lane: args.lane } : {},
48326
48469
  ...args.ambient ? { ambient: true } : {}
48327
48470
  }).catch((err) => {
@@ -49167,11 +49310,10 @@ ${stepArc}
49167
49310
  log52.info(
49168
49311
  `[questions] delivering answer to session ${args.sessionId.slice(0, 8)} (gezel ${session.gezelId}, project ${session.projectId})`
49169
49312
  );
49170
- await this.sendWithBusyRetry(
49171
- args.sessionId,
49172
- args.seed,
49173
- args.coalescable ? { coalescable: true } : void 0
49174
- );
49313
+ await this.sendWithBusyRetry(args.sessionId, args.seed, {
49314
+ messageOrigin: "question-answer",
49315
+ ...args.coalescable ? { coalescable: true } : {}
49316
+ });
49175
49317
  }
49176
49318
  /**
49177
49319
  * System-authored reaction seed into a gezel's live (gezel, project)
@@ -49197,6 +49339,7 @@ ${stepArc}
49197
49339
  this.send(session.id, args.seed, {
49198
49340
  coalescable: true,
49199
49341
  lane: "background",
49342
+ messageOrigin: "system",
49200
49343
  ...leanReactionCap ? { continuationMaxTokens: leanReactionCap } : {},
49201
49344
  ...args.hidden ? { hidden: true } : {}
49202
49345
  }).catch((err) => {
@@ -49671,12 +49814,13 @@ ${stepArc}
49671
49814
  throw new Error("engagement-off: AI is disabled in settings; re-enable to send");
49672
49815
  }
49673
49816
  const existingQueue = this.pendingSends.get(sessionId2);
49817
+ const messageOrigin = resolveTurnMessageOrigin(opts);
49674
49818
  const shouldQueue = this.inflight.has(sessionId2) || existingQueue !== void 0 && existingQueue.length > 0;
49675
49819
  if (shouldQueue) {
49676
49820
  return new Promise((resolve18, reject) => {
49677
49821
  const q2 = this.pendingSends.get(sessionId2) ?? [];
49678
49822
  const tail = q2.length > 0 ? q2[q2.length - 1] : void 0;
49679
- const canMerge = opts?.coalescable === true && opts?.nudge !== true && tail?.coalescable === true && tail.nudge !== true && sameFromBucket(tail.from, opts.from);
49823
+ const canMerge = opts?.coalescable === true && opts?.nudge !== true && tail?.coalescable === true && tail.nudge !== true && tail.messageOrigin === messageOrigin && sameFromBucket(tail.from, opts.from);
49680
49824
  if (canMerge && tail) {
49681
49825
  tail.userText = `${tail.userText}
49682
49826
 
@@ -49703,6 +49847,7 @@ ${userText}`;
49703
49847
  coalescable: opts?.coalescable === true,
49704
49848
  lane: opts?.lane,
49705
49849
  ambient: opts?.ambient === true,
49850
+ messageOrigin,
49706
49851
  continuationMaxTokens: opts?.continuationMaxTokens,
49707
49852
  hidden: opts?.hidden === true,
49708
49853
  nudge: opts?.nudge === true,
@@ -49719,7 +49864,7 @@ ${userText}`;
49719
49864
  return this.runSendAndDrain(
49720
49865
  sessionId2,
49721
49866
  userText,
49722
- opts?.nudge ? { ...opts, nudge: false } : opts
49867
+ opts?.nudge ? { ...opts, nudge: false, messageOrigin } : { ...opts, messageOrigin }
49723
49868
  );
49724
49869
  }
49725
49870
  /**
@@ -49818,7 +49963,7 @@ ${userText}`;
49818
49963
  if (next.nudge) {
49819
49964
  while (q2.length > 0) {
49820
49965
  const peek = q2[0];
49821
- if (!peek.nudge || peek.hidden !== next.hidden || !sameFromBucket(peek.from, next.from)) {
49966
+ if (!peek.nudge || peek.hidden !== next.hidden || peek.messageOrigin !== next.messageOrigin || !sameFromBucket(peek.from, next.from)) {
49822
49967
  break;
49823
49968
  }
49824
49969
  q2.shift();
@@ -49841,6 +49986,7 @@ ${peek.userText}`;
49841
49986
  if (next.continuationMaxTokens) runOpts.continuationMaxTokens = next.continuationMaxTokens;
49842
49987
  if (next.hidden) runOpts.hidden = true;
49843
49988
  if (next.nudge) runOpts.nudge = true;
49989
+ runOpts.messageOrigin = next.messageOrigin;
49844
49990
  void this.runSendAndDrain(sessionId2, next.userText, runOpts).then((msg) => {
49845
49991
  for (const w of next.waiters) {
49846
49992
  try {
@@ -50171,6 +50317,18 @@ ${peek.userText}`;
50171
50317
  try {
50172
50318
  let lastAssistantMessage = null;
50173
50319
  let promptForTurn = spliceIntoText(userText, pendingDigests);
50320
+ const correctivePromptForLiveRoster = (prompt, source) => {
50321
+ const safe = capabilitySafeCorrectivePrompt({
50322
+ prompt,
50323
+ availableTools: liveTurnToolNames(liveSession)
50324
+ });
50325
+ if (safe !== prompt.trim()) {
50326
+ log52.warn(
50327
+ `session ${sessionId2}: filtered ${source} corrective against the live tool roster`
50328
+ );
50329
+ }
50330
+ return safe;
50331
+ };
50174
50332
  const freshGameState = await this.refreshLeanGameState(state.record, userText);
50175
50333
  if (freshGameState) {
50176
50334
  promptForTurn = `${freshGameState}
@@ -50181,7 +50339,8 @@ ${promptForTurn}`;
50181
50339
  this.taskBudget.reset(state.record.taskRef);
50182
50340
  this.pendingBudgetNudge.delete(state.record.taskRef);
50183
50341
  }
50184
- const preludeForTurn = await this.resolveUserPromptPrelude(state, userText);
50342
+ const messageOrigin = resolveTurnMessageOrigin(opts);
50343
+ const preludeForTurn = await this.resolveUserPromptPrelude(state, userText, messageOrigin);
50185
50344
  if (preludeForTurn) {
50186
50345
  promptForTurn = `${preludeForTurn.text}
50187
50346
 
@@ -50204,7 +50363,7 @@ ${promptForTurn}`;
50204
50363
  }
50205
50364
  if (state.record.taskRef && this.pendingBudgetNudge.has(state.record.taskRef)) {
50206
50365
  this.pendingBudgetNudge.delete(state.record.taskRef);
50207
- promptForTurn = `${TASK_BUDGET_NUDGE}
50366
+ promptForTurn = `${correctivePromptForLiveRoster(TASK_BUDGET_NUDGE, "task-budget")}
50208
50367
 
50209
50368
  ${promptForTurn}`;
50210
50369
  log52.info(
@@ -50213,6 +50372,7 @@ ${promptForTurn}`;
50213
50372
  }
50214
50373
  let continuations = 0;
50215
50374
  const maxContinuations = resolveContinuationBudget(state);
50375
+ let voormanIdleNudged = false;
50216
50376
  const toolsAcrossContinuations = [];
50217
50377
  let compactionsThisSend = 0;
50218
50378
  let keurmeesterConsultedThisSend = false;
@@ -50476,6 +50636,7 @@ ${state.aboutSnapshot}`);
50476
50636
  const detectorVerdict = runPostTurnDetectors(state, {
50477
50637
  sessionId: sessionId2,
50478
50638
  isMeester: cfgForDetector?.meesterGezelId === state.record.gezelId,
50639
+ messageOrigin,
50479
50640
  userText,
50480
50641
  drained: toolsAcrossContinuations,
50481
50642
  assistantContent: assistantMessage.content,
@@ -50489,7 +50650,10 @@ ${state.aboutSnapshot}`);
50489
50650
  this.events.publish(scope, { type: "warning", message: warningMessage });
50490
50651
  }
50491
50652
  if (detectorVerdict?.promptForNextTurn) {
50492
- detectorReprompt = detectorVerdict.promptForNextTurn;
50653
+ detectorReprompt = correctivePromptForLiveRoster(
50654
+ detectorVerdict.promptForNextTurn,
50655
+ "post-turn detector"
50656
+ );
50493
50657
  }
50494
50658
  const advanceOutcome = await this.maybeAutoAdvanceOnObservableProgress(
50495
50659
  state,
@@ -50640,7 +50804,10 @@ ${state.aboutSnapshot}`);
50640
50804
  log52.info(
50641
50805
  `session ${sessionId2}: keurmeester intervention applied (case ${consult.caseId}) \u2014 granting one recovery continuation`
50642
50806
  );
50643
- promptForTurn = consult.correctivePrompt;
50807
+ promptForTurn = correctivePromptForLiveRoster(
50808
+ consult.correctivePrompt,
50809
+ "keurmeester"
50810
+ );
50644
50811
  continue;
50645
50812
  }
50646
50813
  }
@@ -50658,7 +50825,10 @@ ${state.aboutSnapshot}`);
50658
50825
  log52.info(
50659
50826
  `session ${sessionId2}: ${detectorVerdict?.reason ?? "detector"} \u2014 re-prompting`
50660
50827
  );
50661
- promptForTurn = detectorReprompt;
50828
+ promptForTurn = correctivePromptForLiveRoster(
50829
+ detectorReprompt,
50830
+ detectorVerdict ? "post-turn detector" : "deliverable salvage"
50831
+ );
50662
50832
  continue;
50663
50833
  }
50664
50834
  if (advanceOutcome.gateRejected && !advanceOutcome.gateRejected.paused && advanceOutcome.gateRejected.fingerprint !== state.lastGateNudgeFingerprint && !drained.some((d) => d.name === "ask_user_question" && d.success)) {
@@ -50668,7 +50838,10 @@ ${state.aboutSnapshot}`);
50668
50838
  log52.info(
50669
50839
  `session ${sessionId2}: ${advanceOutcome.gateRejected.taskRef} step "${advanceOutcome.gateRejected.stepId}" gate rejected \u2014 re-prompting with the verdict${stage > 0 ? ` (escalation stage ${stage})` : ""}`
50670
50840
  );
50671
- promptForTurn = stage >= 1 ? advanceOutcome.gateRejected.message : buildGateRejectionNudge(advanceOutcome.gateRejected.message);
50841
+ promptForTurn = correctivePromptForLiveRoster(
50842
+ stage >= 1 ? advanceOutcome.gateRejected.message : buildGateRejectionNudge(advanceOutcome.gateRejected.message),
50843
+ "task-gate"
50844
+ );
50672
50845
  continue;
50673
50846
  }
50674
50847
  if (deliverableOutcome.deliverableRejected?.stopRetrying) {
@@ -50719,7 +50892,10 @@ ${state.aboutSnapshot}`);
50719
50892
  log52.info(
50720
50893
  `session ${sessionId2}: keurmeester intervention applied on deliverable plateau (case ${consult.caseId}) \u2014 granting one recovery continuation`
50721
50894
  );
50722
- promptForTurn = consult.correctivePrompt;
50895
+ promptForTurn = correctivePromptForLiveRoster(
50896
+ consult.correctivePrompt,
50897
+ "keurmeester deliverable-plateau"
50898
+ );
50723
50899
  continue;
50724
50900
  }
50725
50901
  }
@@ -50743,7 +50919,10 @@ ${rej.message.split("\n").slice(0, 4).join("\n")}`;
50743
50919
  ...dRej.filePath ? { filePath: dRej.filePath } : {},
50744
50920
  failingVerdict: dRej.message
50745
50921
  });
50746
- promptForTurn = clampNudge ?? (dStage >= 1 ? dRej.message : buildGateRejectionNudge(dRej.message));
50922
+ promptForTurn = correctivePromptForLiveRoster(
50923
+ clampNudge ?? (dStage >= 1 ? dRej.message : buildGateRejectionNudge(dRej.message)),
50924
+ "deliverable-gate"
50925
+ );
50747
50926
  continue;
50748
50927
  }
50749
50928
  if (advanceOutcome.unmetEditGate && (looksStalled(finalContent) || claimsCompletion(finalContent)) && !drained.some((d) => d.name === "ask_user_question" && d.success)) {
@@ -50751,7 +50930,10 @@ ${rej.message.split("\n").slice(0, 4).join("\n")}`;
50751
50930
  log52.info(
50752
50931
  `session ${sessionId2}: ${advanceOutcome.unmetEditGate.taskRef} edit gate unmet (${advanceOutcome.unmetEditGate.file} not written) but reply claims progress \u2014 nudging to edit`
50753
50932
  );
50754
- promptForTurn = buildDeliverableEditNudge(advanceOutcome.unmetEditGate.file);
50933
+ promptForTurn = correctivePromptForLiveRoster(
50934
+ buildDeliverableEditNudge(advanceOutcome.unmetEditGate.file),
50935
+ "deliverable-edit"
50936
+ );
50755
50937
  continue;
50756
50938
  }
50757
50939
  const unresolvedToolFailures = unresolvedFailedToolCalls(drained);
@@ -50795,7 +50977,7 @@ ${rej.message.split("\n").slice(0, 4).join("\n")}`;
50795
50977
  // the hot path synchronous for the common non-voorman case.
50796
50978
  // Also requires `proactive` — voorman idle nudges advance
50797
50979
  // project work past what the user asked for.
50798
- isProactiveAllowed(nudgeConfig) && state.record.providerName !== "copilot"
50980
+ isProactiveAllowed(nudgeConfig) && state.record.providerName !== "copilot" && !voormanIdleNudged
50799
50981
  ) {
50800
50982
  voormanIdleVerdict = await this.didVoormanIdleStall(state.record, drained, sessionId2);
50801
50983
  if (voormanIdleVerdict !== null) {
@@ -50813,15 +50995,19 @@ ${rej.message.split("\n").slice(0, 4).join("\n")}`;
50813
50995
  );
50814
50996
  break;
50815
50997
  }
50998
+ if (stallReason === "voorman-idle") voormanIdleNudged = true;
50816
50999
  continuations++;
50817
51000
  log52.info(
50818
- `session ${sessionId2}: response looks stalled (${stallReason}) \u2014 nudging the model to actually execute`
51001
+ `session ${sessionId2}: response looks stalled (${stallReason}) \u2014 requesting one recovery pass`
51002
+ );
51003
+ promptForTurn = correctivePromptForLiveRoster(
51004
+ stallReason === "voorman-idle" ? buildContinuationNudge(
51005
+ finalContent,
51006
+ state.record.messages,
51007
+ voormanIdleVerdict === "nothing-built" ? VOORMAN_NOT_DONE_NUDGE : VOORMAN_IDLE_NUDGE
51008
+ ) : stallReason === "tool-only" ? CLOSING_SUMMARY_NUDGE : stallReason === "tool-read-only" ? READ_ONLY_PROGRESS_NUDGE : stallReason === "tool-failed" ? buildFailedToolRecoveryNudge(unresolvedToolFailures) : buildContinuationNudge(finalContent, state.record.messages),
51009
+ `stall-${stallReason}`
50819
51010
  );
50820
- promptForTurn = stallReason === "voorman-idle" ? buildContinuationNudge(
50821
- finalContent,
50822
- state.record.messages,
50823
- voormanIdleVerdict === "nothing-built" ? VOORMAN_NOT_DONE_NUDGE : VOORMAN_IDLE_NUDGE
50824
- ) : stallReason === "tool-only" ? CLOSING_SUMMARY_NUDGE : stallReason === "tool-read-only" ? READ_ONLY_PROGRESS_NUDGE : stallReason === "tool-failed" ? buildFailedToolRecoveryNudge(unresolvedToolFailures) : buildContinuationNudge(finalContent, state.record.messages);
50825
51011
  continue;
50826
51012
  }
50827
51013
  }
@@ -51387,7 +51573,7 @@ ${rej.message.split("\n").slice(0, 4).join("\n")}`;
51387
51573
  return null;
51388
51574
  }
51389
51575
  const tasks = await this.store.listProjectTasks(record.projectId).catch(() => []);
51390
- if (tasks.length > 0 && !tasks.some((t) => t.status === "active")) {
51576
+ if (tasks.length === 0 || !tasks.some((t) => t.status === "active")) {
51391
51577
  return null;
51392
51578
  }
51393
51579
  const workspaceFiles = await this.store.listProjectWorkspaceRecursive(record.projectId).catch(() => []);
@@ -54138,7 +54324,7 @@ ${baseSystem}` : baseSystem;
54138
54324
  return { modelId };
54139
54325
  }
54140
54326
  }
54141
- async resolveUserPromptPrelude(state, userText) {
54327
+ async resolveUserPromptPrelude(state, userText, messageOrigin) {
54142
54328
  const profile = state.profile;
54143
54329
  if (!profile) return null;
54144
54330
  const cfg = await this.store.readConfig().catch(() => null);
@@ -54147,6 +54333,9 @@ ${baseSystem}` : baseSystem;
54147
54333
  ...modelCtxFromProfile(profile, state),
54148
54334
  sessionId: state.record.id,
54149
54335
  isMeester,
54336
+ projectId: state.record.projectId,
54337
+ messageOrigin,
54338
+ availableToolNames: liveTurnToolNames(state.session),
54150
54339
  userText,
54151
54340
  drained: [],
54152
54341
  assistantContent: "",
@@ -55467,6 +55656,7 @@ Allow **${info.toolName}** this time?`,
55467
55656
  GEZEL_SESSION_ID: record.id,
55468
55657
  GEZEL_HOME: this.home,
55469
55658
  GEZEL_EXECUTION_DENSITY: executionDensity,
55659
+ GEZEL_ROLE_BASED_NAME_ONLY_MODE: record.roleBasedNameOnlyMode ?? globalConfig.roleBasedNameOnlyMode ?? false ? "1" : "0",
55470
55660
  ...record.expectedDeliverable ? { GEZEL_EXPECTED_DELIVERABLE: JSON.stringify(record.expectedDeliverable) } : {},
55471
55661
  ...record.stepId ? { GEZEL_STEP_ID: record.stepId } : {},
55472
55662
  // Scope task: lets the MCP task tools default to / recover toward
@@ -55906,6 +56096,9 @@ function runPostTurnDetectors(state, args) {
55906
56096
  ...modelCtxFromProfile(profile, state),
55907
56097
  sessionId: args.sessionId,
55908
56098
  isMeester: args.isMeester,
56099
+ projectId: state.record.projectId,
56100
+ messageOrigin: args.messageOrigin,
56101
+ availableToolNames: liveTurnToolNames(state.session),
55909
56102
  userText: args.userText,
55910
56103
  drained: args.drained,
55911
56104
  assistantContent: args.assistantContent,
@@ -56438,13 +56631,18 @@ Drop conversational filler, pleasantries, and verbatim tool outputs (just note "
56438
56631
  Earlier conversation:
56439
56632
 
56440
56633
  `;
56441
- var VOORMAN_IDLE_NUDGE = 'You\'re the project voorman and this turn ended with no handoff or proactive tool. If there is live work to move, pick one now: `advance_task_step` (next step), `message_gezel` (ping the next gezel), `assign_task` / `ensure_gezel` (wire up an assignee), or `ask_user_question` (surface a real blocker). But if the project is finished, or stable, or waiting on the user, that is already a COMPLETE turn \u2014 reply with ONE short sentence saying so (e.g. "From my perspective the project is done / in a stable state.") and stop. A plain "it\'s done" with no tool call is the correct answer there: it is not an idle failure and not a fabrication. Do not re-verify, do not relitigate the options, do not promise future action.';
56634
+ var VOORMAN_IDLE_NUDGE = "Before you finish, make one quick project check: is there concrete work already in flight that this turn should move? If yes, take the natural next action with the most relevant available tool \u2014 advance a ready step, message or assign the right gezel, or surface a real blocker. If no \u2014 because the project is complete, stable, or waiting on the user \u2014 say that once in one short sentence and end the turn. Do not invent work or call a tool merely to satisfy this check. Do not re-verify or promise future action.";
56442
56635
  var BOOTSTRAP_WORKSPACE_FILES = /* @__PURE__ */ new Set([
56443
56636
  "package.json",
56637
+ "package-lock.json",
56638
+ "pnpm-lock.yaml",
56639
+ "yarn.lock",
56640
+ "bun.lock",
56641
+ "bun.lockb",
56444
56642
  "tsconfig.json",
56445
56643
  ".gitignore"
56446
56644
  ]);
56447
- var VOORMAN_NOT_DONE_NUDGE = "You're the project voorman and this turn ended with no handoff or proactive tool. The workspace still holds only its bootstrap files \u2014 nothing has been built yet, so the project is NOT done, complete, or stable. Do NOT reply that it is. Move the work this turn: `message_gezel` the Builder/Developer who owns this task (or `ensure_gezel` for one first if none exists), with the exact deliverable path and acceptance criteria, and ask them to write the file and reply with the path; or `advance_task_step` if a step is genuinely ready; or, if you are truly blocked on a decision, `ask_user_question` to surface it. Pick one tool \u2014 a prose \"it's stable / done\" over an empty workspace is a false claim, not a complete turn.";
56645
+ var VOORMAN_NOT_DONE_NUDGE = "One project-state check before you finish: an active task remains, but the workspace still contains only setup files, so no deliverable has been built yet. If the user only asked for status or conversation, answer accurately and stop; a tool is not required. If this turn is supposed to move the active work, take one natural next action: message or assign the specialist who owns the deliverable, advance a genuinely ready step, or surface a real blocker with `ask_user_question`. Do not invent work or call a tool merely to appear busy, and do not describe an active empty project as done or stable.";
56448
56646
  var READ_ONLY_MCP_TOOLS = /* @__PURE__ */ new Set([
56449
56647
  "search_memory",
56450
56648
  "list_memories",
@@ -75768,6 +75966,7 @@ ${p.content.trim()}`).join("\n\n---\n\n");
75768
75966
  async createProject(input, opts) {
75769
75967
  const release2 = await this.acquireProjectCreationLock();
75770
75968
  try {
75969
+ if (input.workingDir) this.assertSafeWorkingDir(input.workingDir);
75771
75970
  const id = opts?.id ?? await this.uniqueProjectId(slugify(input.name) || randomUUID10().slice(0, 8));
75772
75971
  if (!/^[a-z0-9][a-z0-9-]*$/.test(id)) {
75773
75972
  throw new Error(`invalid project id "${id}"`);
@@ -75791,14 +75990,22 @@ ${p.content.trim()}`).join("\n\n---\n\n");
75791
75990
  projectName: input.name
75792
75991
  });
75793
75992
  }
75993
+ let github = input.github?.url ? { url: input.github.url } : void 0;
75994
+ if (input.workingDir) {
75995
+ github = await autoDetectGitHubLink(input.workingDir, github) ?? github;
75996
+ }
75794
75997
  const project = {
75795
75998
  id,
75796
75999
  name: input.name,
75797
76000
  description: input.description,
75798
76001
  ...input.workingDir ? { workingDir: input.workingDir } : {},
76002
+ // Opening an existing folder is an intake action, not a declaration
76003
+ // that its objectives are ready for active supervision. Keep the
76004
+ // Meester quiet until the user explicitly opts this project back in.
76005
+ ...input.workingDir ? { nudgeConfig: { enabled: false } } : {},
75799
76006
  ...input.mode && input.mode !== "crew" ? { mode: input.mode } : {},
75800
76007
  ...input.indexingEnabled !== void 0 ? { indexingEnabled: input.indexingEnabled } : {},
75801
- ...input.github?.url ? { github: { url: input.github.url } } : {},
76008
+ ...github ? { github } : {},
75802
76009
  createdAt: nowIso5(),
75803
76010
  updatedAt: nowIso5()
75804
76011
  };
@@ -80250,6 +80457,7 @@ var TaskManager = class {
80250
80457
  ...cron ? { cron } : {},
80251
80458
  ...nightShift ? { nightShift } : {},
80252
80459
  ...fanout ? { fanout } : {},
80460
+ ...input.roleBasedNameOnlyMode !== void 0 ? { roleBasedNameOnlyMode: input.roleBasedNameOnlyMode } : {},
80253
80461
  createdAt: now,
80254
80462
  updatedAt: now,
80255
80463
  createdBy: input.createdBy ?? { kind: "user" }
@@ -82244,6 +82452,7 @@ Fix one of these, then set the task active again:
82244
82452
  ...parent.nightShift?.enabled ? { nightShift: { enabled: true } } : {},
82245
82453
  activeStepId,
82246
82454
  parentTaskRef: parent.ref,
82455
+ ...parent.roleBasedNameOnlyMode !== void 0 ? { roleBasedNameOnlyMode: parent.roleBasedNameOnlyMode } : {},
82247
82456
  createdAt: now,
82248
82457
  updatedAt: now,
82249
82458
  createdBy: parent.createdBy
@@ -96144,19 +96353,19 @@ function memoryRoutes(ctx) {
96144
96353
  const app = new Hono37();
96145
96354
  app.post("/search", async (c) => {
96146
96355
  const body = await c.req.json();
96147
- const results = await ctx.memory.searchAll(
96356
+ const outcome = await ctx.memory.searchAllDetailed(
96148
96357
  body.gezelId,
96149
96358
  body.projectId,
96150
96359
  body.query,
96151
96360
  body.topK ?? 10
96152
96361
  );
96153
- return c.json({ results });
96362
+ return c.json(outcome);
96154
96363
  });
96155
96364
  app.post("/save", async (c) => {
96156
96365
  const body = await c.req.json();
96157
96366
  const kind = isMemoryKind(body.kind) ? body.kind : "fact";
96158
96367
  const outcome = await ctx.memory.save(body.scope, body.id, body.text, kind);
96159
- return c.json({ ok: true, status: outcome?.status ?? "saved" });
96368
+ return c.json({ ok: true, ...outcome });
96160
96369
  });
96161
96370
  app.get("/recent", async (c) => {
96162
96371
  const scope = c.req.query("scope") ?? "gezel";
@@ -101005,6 +101214,7 @@ import {
101005
101214
  projectGezelId as projectGezelId2,
101006
101215
  skillToCraftbookDoc
101007
101216
  } from "@bendyline/gezel";
101217
+ init_ensure();
101008
101218
  init_install();
101009
101219
 
101010
101220
  // src/workspace/instruction-scanner.ts
@@ -101187,6 +101397,30 @@ async function translateShellBlock(chat, skillName, scriptName, code) {
101187
101397
  // src/workspace/import-sync.ts
101188
101398
  var log110 = createLogger114("import-sync");
101189
101399
  var VOORMAN_TEMPLATE_ID = "voorman";
101400
+ var BUILDER_TEMPLATE_ID = "builder";
101401
+ async function ensureFolderProjectBuilder(deps, projectId) {
101402
+ const { store, catalog } = deps;
101403
+ if (!projectId || projectId === "default") return {};
101404
+ const project = await store.getProject(projectId).catch(() => null);
101405
+ if (!project || project.mode !== "solo" || !project.workingDir) return {};
101406
+ if (project.voormanGezelId) return {};
101407
+ const gezels = await store.listGezels().catch(() => []);
101408
+ const existing = gezels.find(
101409
+ (gezel) => gezel.templateId === BUILDER_TEMPLATE_ID || gezel.role?.trim().toLowerCase() === BUILDER_TEMPLATE_ID
101410
+ );
101411
+ const recruited = existing ? null : await recruitFromTemplate(store, catalog, BUILDER_TEMPLATE_ID).catch((err) => {
101412
+ log110.warn(`[import-sync] ${projectId}: Builder recruit failed: ${describe6(err)}`);
101413
+ return null;
101414
+ });
101415
+ const builder = existing ?? recruited;
101416
+ if (!builder) return {};
101417
+ await store.updateProject(projectId, {
101418
+ voormanGezelId: builder.id,
101419
+ voormanAutoAssignedAt: nowIso12()
101420
+ });
101421
+ log110.info(`[import-sync] ${projectId}: assigned ${builder.id} as Builder`);
101422
+ return recruited ? { createdGezel: recruited } : {};
101423
+ }
101190
101424
  async function syncProjectImports(deps, projectId, instructions, skills) {
101191
101425
  const { store } = deps;
101192
101426
  const gate = await store.assertWorkspaceWritable(projectId).catch(() => null);
@@ -101315,16 +101549,20 @@ async function findExistingVoorman(store) {
101315
101549
  return match?.id ?? null;
101316
101550
  }
101317
101551
  async function recruitVoorman(store, catalog) {
101318
- const detail = await catalog.get("gezel-template", VOORMAN_TEMPLATE_ID).catch(() => null);
101319
- if (!detail || detail.manifest.kind !== "gezel-template") return null;
101552
+ return recruitFromTemplate(store, catalog, VOORMAN_TEMPLATE_ID);
101553
+ }
101554
+ async function recruitFromTemplate(store, catalog, templateId) {
101555
+ const template = await resolveGildeTemplateForRole(catalog, templateId).catch(() => null);
101556
+ if (!template || template.templateId !== templateId) return null;
101320
101557
  const { name, gender } = pickRandomNameWithGender4();
101321
101558
  const created = await store.createGezel({
101322
101559
  name,
101323
- role: detail.manifest.role,
101560
+ role: template.role,
101324
101561
  gender,
101325
- about: detail.about ?? "",
101326
- templateId: VOORMAN_TEMPLATE_ID,
101327
- templateVersion: detail.manifest.version
101562
+ about: template.about,
101563
+ templateId,
101564
+ templateVersion: template.templateVersion,
101565
+ ...template.frontmatter ? { frontmatter: template.frontmatter } : {}
101328
101566
  });
101329
101567
  return { id: created.id, name: created.name };
101330
101568
  }
@@ -101799,6 +102037,7 @@ async function resolveActor(ctx, gezelIdHeader) {
101799
102037
  }
101800
102038
  function projectTaskRoutes(ctx) {
101801
102039
  const app = new Hono55();
102040
+ const inflightCraftbookInvocations = /* @__PURE__ */ new Map();
101802
102041
  app.get("/:projectId/tasks", async (c) => {
101803
102042
  const projectId = c.req.param("projectId");
101804
102043
  const filter = ListFilterSchema.parse({
@@ -101814,10 +102053,31 @@ function projectTaskRoutes(ctx) {
101814
102053
  });
101815
102054
  app.post("/:projectId/tasks", async (c) => {
101816
102055
  const projectId = c.req.param("projectId");
101817
- const { dispatchEntry, ...body } = CreateTaskRequestSchema.parse(await c.req.json());
102056
+ const { dispatchEntry, craftbookInvocationKey, ...body } = CreateTaskRequestSchema.parse(
102057
+ await c.req.json()
102058
+ );
101818
102059
  let task;
101819
102060
  try {
101820
- task = await ctx.tasks.create(projectId, body);
102061
+ if (craftbookInvocationKey) {
102062
+ const existing = (await ctx.tasks.list({ projectId })).find(
102063
+ (candidate) => candidate.origin?.kind === "craftbook-invocation" && candidate.origin.key === craftbookInvocationKey && (candidate.status === "draft" || candidate.status === "active" || candidate.status === "paused")
102064
+ );
102065
+ if (existing) return c.json(existing);
102066
+ const inflightKey = `${projectId}:${craftbookInvocationKey}`;
102067
+ const pending5 = inflightCraftbookInvocations.get(inflightKey);
102068
+ if (pending5) return c.json(await pending5);
102069
+ const create = ctx.tasks.create(projectId, body, {
102070
+ origin: { kind: "craftbook-invocation", key: craftbookInvocationKey }
102071
+ });
102072
+ inflightCraftbookInvocations.set(inflightKey, create);
102073
+ try {
102074
+ task = await create;
102075
+ } finally {
102076
+ inflightCraftbookInvocations.delete(inflightKey);
102077
+ }
102078
+ } else {
102079
+ task = await ctx.tasks.create(projectId, body);
102080
+ }
101821
102081
  } catch (err) {
101822
102082
  if (err instanceof CraftbookSetupRequiredError) {
101823
102083
  return c.json(
@@ -104263,12 +104523,13 @@ function projectRoutes(ctx) {
104263
104523
  app.post("/", async (c) => {
104264
104524
  const body = CreateProjectRequestSchema.parse(await c.req.json());
104265
104525
  const created = await ctx.store.createProject(body);
104266
- const ensured2 = await ensureProjectVoorman(
104526
+ const ensureLead = body.workingDir && body.mode === "solo" ? ensureFolderProjectBuilder : ensureProjectVoorman;
104527
+ const ensured2 = await ensureLead(
104267
104528
  { store: ctx.store, chat: ctx.chat, home: ctx.home, catalog: ctx.catalog },
104268
104529
  created.id
104269
104530
  ).catch((err) => {
104270
104531
  const message = err instanceof Error ? err.message : String(err);
104271
- log114.warn(`[projects] ensure-voorman failed for ${created.id}: ${message}`);
104532
+ log114.warn(`[projects] ensure-lead failed for ${created.id}: ${message}`);
104272
104533
  return {};
104273
104534
  });
104274
104535
  if (ensured2.createdGezel) {
@@ -104791,6 +105052,31 @@ function projectRoutes(ctx) {
104791
105052
  return c.json({ error: "not found" }, 404);
104792
105053
  }
104793
105054
  });
105055
+ app.post("/:id/reveal-reference", async (c) => {
105056
+ const id = c.req.param("id");
105057
+ const request = ReferenceFileLocationRequestSchema.parse({
105058
+ kind: c.req.query("kind"),
105059
+ path: c.req.query("path")
105060
+ });
105061
+ const base = request.kind === "artifact" ? ctx.store.projectArtifactsDir(id) : request.kind === "workspace" ? await ctx.store.projectWorkspaceDir(id) : ctx.store.documentsDir();
105062
+ const joined = safeJoin(base, request.path);
105063
+ if (!joined || !await realpathContained(base, joined)) {
105064
+ return c.json({ error: "path traversal" }, 400);
105065
+ }
105066
+ let path2;
105067
+ try {
105068
+ const file = await stat36(joined);
105069
+ if (!file.isFile()) return c.json({ error: "not found" }, 404);
105070
+ path2 = await realpath8(joined);
105071
+ } catch {
105072
+ return c.json({ error: "not found" }, 404);
105073
+ }
105074
+ const { execFile: execFile8 } = await import("child_process");
105075
+ const launcher = process.platform === "darwin" ? { cmd: "open", args: ["-R", path2] } : process.platform === "win32" ? { cmd: "explorer", args: ["/select,", path2] } : { cmd: "xdg-open", args: [dirname44(path2)] };
105076
+ execFile8(launcher.cmd, launcher.args, { windowsHide: true }, () => {
105077
+ });
105078
+ return c.json(ReferenceFileLocationResponseSchema.parse({ path: path2 }));
105079
+ });
104794
105080
  app.get("/:id/artifacts", async (c) => {
104795
105081
  const id = c.req.param("id");
104796
105082
  const subpath = c.req.query("path") ?? "";
@@ -116245,7 +116531,10 @@ function opaqueServerErrors(log189, onUnexpectedHttpError) {
116245
116531
  const raw = await prior.clone().text().catch(() => "");
116246
116532
  try {
116247
116533
  const parsed = JSON.parse(raw);
116248
- if (parsed.error === "internal_error" && typeof parsed.requestId === "string") return;
116534
+ if (parsed.error === "internal_error" && typeof parsed.requestId === "string") {
116535
+ c.header("x-request-id", parsed.requestId);
116536
+ return;
116537
+ }
116249
116538
  if ((parsed.error === "machine_engine_unavailable" || parsed.error === "capacity_denied") && Object.keys(parsed).length === 1) {
116250
116539
  return;
116251
116540
  }
@@ -125156,8 +125445,8 @@ var MemoryManager = class {
125156
125445
  * that text dedups, so we accept the race rather than lock.
125157
125446
  *
125158
125447
  * Degraded mode (embeddings disabled): markdown is still appended (it is
125159
- * the source of truth and the index self-heals on rebuild) and the
125160
- * EmbeddingsDisabledError propagates, preserving the pre-dedup contract.
125448
+ * the source of truth) and the outcome reports deferred indexing. The health
125449
+ * monitor self-heals the derived index once embeddings recover.
125161
125450
  */
125162
125451
  async save(scope, id, text, kind = DEFAULT_MEMORY_KIND) {
125163
125452
  const trimmed = text.trim();
@@ -125172,9 +125461,19 @@ var MemoryManager = class {
125172
125461
  vector = await embed(trimmed);
125173
125462
  } catch (err) {
125174
125463
  await this.store.appendMemory(scope, id, trimmed, kind);
125175
- throw err;
125464
+ log155.warn(
125465
+ `[memory] saved ${scope}/${id} to Markdown; semantic indexing deferred: ${describeError3(err)}`
125466
+ );
125467
+ return deferredIndexOutcome();
125468
+ }
125469
+ let top;
125470
+ try {
125471
+ top = (await searchByVector(indexDir, vector, 1))[0];
125472
+ } catch (error) {
125473
+ log155.warn(
125474
+ `[memory] vector dedup unavailable for ${scope}/${id}; continuing save: ${describeError3(error)}`
125475
+ );
125176
125476
  }
125177
- const top = (await searchByVector(indexDir, vector, 1))[0];
125178
125477
  if (top && top.score >= this.dedupThreshold) {
125179
125478
  log155.info(
125180
125479
  `[memory] dup-skip (${top.score.toFixed(2)} vs "${top.text.slice(0, 40)}") ${scope}/${id}: ${trimmed.slice(0, 60)}`
@@ -125182,20 +125481,27 @@ var MemoryManager = class {
125182
125481
  return { status: "duplicate", match: { text: top.text, score: top.score, via: "vector" } };
125183
125482
  }
125184
125483
  await this.store.appendMemory(scope, id, trimmed, kind);
125185
- await addToIndex(
125186
- indexDir,
125187
- {
125188
- text: trimmed,
125189
- scope,
125190
- id,
125191
- day: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
125192
- at: (/* @__PURE__ */ new Date()).toISOString(),
125193
- kind
125194
- },
125195
- vector
125196
- );
125484
+ try {
125485
+ await addToIndex(
125486
+ indexDir,
125487
+ {
125488
+ text: trimmed,
125489
+ scope,
125490
+ id,
125491
+ day: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
125492
+ at: (/* @__PURE__ */ new Date()).toISOString(),
125493
+ kind
125494
+ },
125495
+ vector
125496
+ );
125497
+ } catch (error) {
125498
+ log155.warn(
125499
+ `[memory] saved ${scope}/${id} to Markdown; semantic index write deferred: ${describeError3(error)}`
125500
+ );
125501
+ return deferredIndexOutcome();
125502
+ }
125197
125503
  log155.info(`[memory] saved ${scope}/${id} [${kind}]: ${trimmed.slice(0, 60)}`);
125198
- return { status: "saved" };
125504
+ return { status: "saved", indexed: true };
125199
125505
  }
125200
125506
  /**
125201
125507
  * Exact-text match against today's and yesterday's daily files. Two days
@@ -125217,7 +125523,15 @@ var MemoryManager = class {
125217
125523
  }
125218
125524
  async search(scope, id, query, topK = 10) {
125219
125525
  const indexDir = this.store.memoryIndexDir(scope, id);
125220
- return searchIndex(indexDir, query, topK);
125526
+ if (!this.hasIndex(scope, id)) return this.searchLexical(scope, id, query, topK);
125527
+ try {
125528
+ return await searchIndex(indexDir, query, topK);
125529
+ } catch (error) {
125530
+ log155.warn(
125531
+ `[memory] semantic search failed for ${scope}/${id}; using lexical fallback: ${describeError3(error)}`
125532
+ );
125533
+ return this.searchLexical(scope, id, query, topK);
125534
+ }
125221
125535
  }
125222
125536
  /**
125223
125537
  * Like {@link search} but with a precomputed query embedding — lets the
@@ -125246,11 +125560,64 @@ var MemoryManager = class {
125246
125560
  return existsSync28(join103(this.store.memoryIndexDir(scope, id), "mem.db"));
125247
125561
  }
125248
125562
  async searchAll(gezelId, projectId, query, topK = 10) {
125249
- const [agentResults, projectResults] = await Promise.all([
125250
- this.search("gezel", gezelId, query, topK),
125251
- this.search("project", projectId, query, topK)
125563
+ return (await this.searchAllDetailed(gezelId, projectId, query, topK)).results;
125564
+ }
125565
+ /**
125566
+ * Search both scopes with one query embedding. Missing indexes fall back to
125567
+ * the daily Markdown source; an unavailable/corrupt semantic cache does the
125568
+ * same and reports a degraded mode instead of turning an optional feature
125569
+ * into an HTTP 500.
125570
+ */
125571
+ async searchAllDetailed(gezelId, projectId, query, topK = 10) {
125572
+ const gezelIndexed = this.hasIndex("gezel", gezelId);
125573
+ const projectIndexed = this.hasIndex("project", projectId);
125574
+ if (!gezelIndexed && !projectIndexed) {
125575
+ return {
125576
+ results: await this.searchAllLexical(gezelId, projectId, query, topK),
125577
+ mode: "lexical"
125578
+ };
125579
+ }
125580
+ try {
125581
+ const vector = await embedQuery(query);
125582
+ const [gezelResults, projectResults] = await Promise.all([
125583
+ gezelIndexed ? this.searchVector("gezel", gezelId, vector, topK) : this.searchLexical("gezel", gezelId, query, topK),
125584
+ projectIndexed ? this.searchVector("project", projectId, vector, topK) : this.searchLexical("project", projectId, query, topK)
125585
+ ]);
125586
+ return {
125587
+ results: rankSearchResults([...gezelResults, ...projectResults], topK),
125588
+ mode: gezelIndexed && projectIndexed ? "semantic" : "hybrid"
125589
+ };
125590
+ } catch (error) {
125591
+ log155.warn(
125592
+ `[memory] semantic search unavailable; using daily-memory lexical fallback: ${describeError3(error)}`
125593
+ );
125594
+ return {
125595
+ results: await this.searchAllLexical(gezelId, projectId, query, topK),
125596
+ mode: "lexical",
125597
+ degraded: {
125598
+ code: "semantic_search_unavailable",
125599
+ message: "Semantic memory search is temporarily unavailable; searched saved memory text directly instead."
125600
+ }
125601
+ };
125602
+ }
125603
+ }
125604
+ async searchAllLexical(gezelId, projectId, query, topK) {
125605
+ const [gezelResults, projectResults] = await Promise.all([
125606
+ this.searchLexical("gezel", gezelId, query, topK),
125607
+ this.searchLexical("project", projectId, query, topK)
125252
125608
  ]);
125253
- return [...agentResults, ...projectResults].sort((a, b) => b.score - a.score).slice(0, topK);
125609
+ return rankSearchResults([...gezelResults, ...projectResults], topK);
125610
+ }
125611
+ async searchLexical(scope, id, query, topK) {
125612
+ const entries = await this.allEntries(scope, id);
125613
+ return entries.map((entry) => ({ entry, score: lexicalScore(query, entry.text) })).filter((candidate) => candidate.score > 0).sort((a, b) => b.score - a.score || b.entry.at.localeCompare(a.entry.at)).slice(0, topK).map(({ entry, score }) => ({
125614
+ text: entry.text,
125615
+ score,
125616
+ day: entry.day,
125617
+ scope: entry.scope,
125618
+ id: entry.id,
125619
+ kind: entry.kind ?? DEFAULT_MEMORY_KIND
125620
+ }));
125254
125621
  }
125255
125622
  async listDays(scope, id) {
125256
125623
  return this.store.listMemoryDays(scope, id);
@@ -125315,6 +125682,36 @@ var MemoryManager = class {
125315
125682
  return entries.length;
125316
125683
  }
125317
125684
  };
125685
+ function rankSearchResults(results, topK) {
125686
+ return results.sort((a, b) => b.score - a.score).slice(0, topK);
125687
+ }
125688
+ function deferredIndexOutcome() {
125689
+ return {
125690
+ status: "saved",
125691
+ indexed: false,
125692
+ degraded: {
125693
+ code: "semantic_index_unavailable",
125694
+ message: "Memory was saved, but semantic indexing is temporarily unavailable."
125695
+ }
125696
+ };
125697
+ }
125698
+ function lexicalScore(query, text) {
125699
+ const normalizedQuery = query.trim().toLocaleLowerCase();
125700
+ const normalizedText = text.toLocaleLowerCase();
125701
+ if (!normalizedQuery || !normalizedText) return 0;
125702
+ if (normalizedText.includes(normalizedQuery)) return 1;
125703
+ const queryTokens = new Set(normalizedQuery.match(/[\p{L}\p{N}_-]+/gu) ?? []);
125704
+ if (queryTokens.size === 0) return 0;
125705
+ const textTokens = new Set(normalizedText.match(/[\p{L}\p{N}_-]+/gu) ?? []);
125706
+ let matches = 0;
125707
+ for (const token of queryTokens) {
125708
+ if (textTokens.has(token)) matches++;
125709
+ }
125710
+ return matches / queryTokens.size;
125711
+ }
125712
+ function describeError3(error) {
125713
+ return error instanceof Error ? error.message : String(error);
125714
+ }
125318
125715
 
125319
125716
  // src/models/install-jobs.ts
125320
125717
  import { createLogger as createLogger166 } from "@bendyline/gezel";
@@ -128723,7 +129120,7 @@ var VideoModelManager = class {
128723
129120
  await rm43(finalPath, { force: true });
128724
129121
  await rename17(partialPath, finalPath);
128725
129122
  } catch (err) {
128726
- yield { type: "error", error: `rename failed for ${file.name}: ${describeError3(err)}` };
129123
+ yield { type: "error", error: `rename failed for ${file.name}: ${describeError4(err)}` };
128727
129124
  return;
128728
129125
  }
128729
129126
  }
@@ -128882,7 +129279,7 @@ var VideoModelSelector = class {
128882
129279
  return (id ? await this.models.resolveModel(id) : null) ?? await this.models.resolveDefaultModel();
128883
129280
  }
128884
129281
  };
128885
- function describeError3(err) {
129282
+ function describeError4(err) {
128886
129283
  return err instanceof Error ? err.message : String(err);
128887
129284
  }
128888
129285
  function isSafeId3(id) {
@@ -133213,6 +133610,7 @@ var TaskRunner = class {
133213
133610
  ...handoff.fromGezelName ? { fromGezelName: handoff.fromGezelName } : {},
133214
133611
  ...handoff.fromGezelId ? { fromGezelId: handoff.fromGezelId } : {},
133215
133612
  ...handoff.kind ? { kind: handoff.kind } : {},
133613
+ ...task.roleBasedNameOnlyMode !== void 0 ? { roleBasedNameOnlyMode: task.roleBasedNameOnlyMode } : {},
133216
133614
  lane,
133217
133615
  ...isNight ? { ambient: true } : {},
133218
133616
  ...isNight ? { nightShift: true } : {},
@@ -133726,6 +134124,10 @@ Step "${step.name}" (\`${step.id}\`) was re-driven ${maxRedrives}\xD7 without pr
133726
134124
  } catch {
133727
134125
  }
133728
134126
  const activeTasks = projectTasks.filter((t) => t.status === "active");
134127
+ if (projectTasks.length === 0) {
134128
+ trace("skip \u2014 project has no tasks to advance");
134129
+ return;
134130
+ }
133729
134131
  if (projectTasks.length > 0 && activeTasks.length === 0) {
133730
134132
  if (projectTasks.some((t) => t.status === "draft")) {
133731
134133
  trace("skip \u2014 only draft task(s) await activation; not nudging or stabilizing");
@@ -137467,7 +137869,7 @@ Address these, then the gate re-checks automatically.`,
137467
137869
  try {
137468
137870
  const existing = await tasks.listChildren(task.ref).catch(() => []);
137469
137871
  if (existing.length === 0) {
137470
- const raw = await store.readProjectWorkspaceFile(projectId, spawn14.overFile).catch(() => null);
137872
+ const raw = await (spawn14.overArtifact ? store.readProjectArtifact(projectId, spawn14.overFile) : store.readProjectWorkspaceFile(projectId, spawn14.overFile)).catch(() => null);
137471
137873
  const items = raw ? extractSpawnItems(raw, spawn14.itemsPath) : [];
137472
137874
  if (items.length === 0) {
137473
137875
  log188.warn(
@@ -137496,7 +137898,7 @@ Address these, then the gate re-checks automatically.`,
137496
137898
 
137497
137899
  ${kids.map((k) => `- ${k.ref}: ${k.title}`).join("\n")}
137498
137900
  `;
137499
- await store.writeProjectWorkspaceFile(projectId, advanceFile, manifest).catch((err) => log188.warn(`[fanout] ${task.ref}: could not write ${advanceFile}:`, err));
137901
+ await (newStep.advanceWhen?.artifact ? store.writeProjectArtifact(projectId, advanceFile, manifest) : store.writeProjectWorkspaceFile(projectId, advanceFile, manifest)).catch((err) => log188.warn(`[fanout] ${task.ref}: could not write ${advanceFile}:`, err));
137500
137902
  }
137501
137903
  const nextStep = newStep.advanceWhen?.goto ?? newStep.next;
137502
137904
  if (nextStep) {