memorix 1.5.0 → 1.6.0

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 (49) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +3 -3
  3. package/README.zh-CN.md +3 -3
  4. package/dist/cli/index.js +452 -109
  5. package/dist/index.js +376 -87
  6. package/dist/maintenance-runner.js +174 -41
  7. package/dist/media-audio-runner.js +174 -41
  8. package/dist/media-video-runner.js +174 -41
  9. package/dist/memcode-runtime/CHANGELOG.md +37 -0
  10. package/dist/sdk.js +375 -86
  11. package/dist/types.d.ts +2 -0
  12. package/dist/vector-backfill-runner.js +174 -41
  13. package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
  14. package/docs/API_REFERENCE.md +1 -1
  15. package/docs/DEVELOPMENT.md +5 -3
  16. package/package.json +1 -1
  17. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  18. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  19. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  20. package/plugins/copilot/memorix/plugin.json +1 -1
  21. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  22. package/plugins/omp/memorix/package.json +1 -1
  23. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  24. package/plugins/pi/memorix/package.json +1 -1
  25. package/server.json +2 -2
  26. package/src/cli/commands/codegraph.ts +48 -5
  27. package/src/cli/commands/context.ts +20 -4
  28. package/src/cli/commands/resume.ts +4 -0
  29. package/src/codegraph/auto-context.ts +4 -1
  30. package/src/codegraph/context-pack.ts +3 -1
  31. package/src/codegraph/external-provider.ts +93 -0
  32. package/src/embedding/api-provider.ts +105 -22
  33. package/src/embedding/provider.ts +2 -0
  34. package/src/hooks/installers/index.ts +1 -1
  35. package/src/hooks/official-skills.ts +3 -3
  36. package/src/hooks/rules/memorix-agent-rules.md +1 -1
  37. package/src/knowledge/context-receipt.ts +41 -0
  38. package/src/knowledge/workflows.ts +6 -3
  39. package/src/knowledge/workset.ts +56 -2
  40. package/src/llm/provider.ts +17 -8
  41. package/src/memory/formation/extract.ts +6 -3
  42. package/src/memory/formation/index.ts +2 -1
  43. package/src/memory/formation/resolve.ts +10 -6
  44. package/src/memory/formation/types.ts +3 -1
  45. package/src/memory/long-term.ts +7 -1
  46. package/src/server.ts +29 -13
  47. package/src/store/orama-store.ts +6 -4
  48. package/src/timeout.ts +67 -0
  49. package/src/types.ts +2 -0
package/dist/sdk.js CHANGED
@@ -3991,6 +3991,8 @@ function getPreferredBatchSize(config) {
3991
3991
  }
3992
3992
  function parseBatchLimit(error) {
3993
3993
  if (!(error instanceof Error)) return null;
3994
+ const status = error instanceof EmbeddingAPIError ? error.status : Number(error.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
3995
+ if (status > 0 && status !== 400) return null;
3994
3996
  const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
3995
3997
  if (explicit) return parseInt(explicit[1], 10);
3996
3998
  if (/batch size/i.test(error.message)) {
@@ -4003,37 +4005,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
4003
4005
  const controller = new AbortController();
4004
4006
  const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
4005
4007
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
4008
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
4006
4009
  let response;
4007
4010
  try {
4008
4011
  response = await fetch(url, {
4009
4012
  method: "POST",
4010
4013
  headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
4011
4014
  body: JSON.stringify(body),
4012
- signal: controller.signal
4015
+ signal
4013
4016
  });
4014
4017
  } catch (err) {
4015
4018
  clearTimeout(timeout);
4016
- if (err instanceof Error && err.name === "AbortError") {
4019
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
4017
4020
  throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
4018
4021
  }
4019
4022
  throw err;
4020
4023
  }
4021
- clearTimeout(timeout);
4022
- if (response.ok) {
4023
- return response.json();
4024
+ try {
4025
+ if (response.ok) {
4026
+ return await raceWithAbort(
4027
+ response.json(),
4028
+ signal
4029
+ );
4030
+ }
4031
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
4032
+ const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
4033
+ const retryAfter = response.headers.get("retry-after");
4034
+ const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
4035
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
4036
+ console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
4037
+ await waitForRetry(waitMs, options.signal);
4038
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
4039
+ }
4040
+ const errorText2 = await raceWithAbort(
4041
+ response.text().catch(() => "unknown error"),
4042
+ signal
4043
+ );
4044
+ throw new EmbeddingAPIError(response.status, errorText2);
4045
+ } catch (err) {
4046
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
4047
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
4048
+ }
4049
+ throw err;
4050
+ } finally {
4051
+ clearTimeout(timeout);
4024
4052
  }
4025
- if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
4026
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
4027
- const retryAfter = response.headers.get("retry-after");
4028
- const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay2;
4029
- console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
4053
+ }
4054
+ function abortError() {
4055
+ const error = new Error("Embedding API request aborted");
4056
+ error.name = "AbortError";
4057
+ return error;
4058
+ }
4059
+ function raceWithAbort(promise, signal) {
4060
+ if (signal.aborted) return Promise.reject(abortError());
4061
+ return new Promise((resolve2, reject) => {
4062
+ const onAbort = () => {
4063
+ signal.removeEventListener("abort", onAbort);
4064
+ reject(abortError());
4065
+ };
4066
+ signal.addEventListener("abort", onAbort, { once: true });
4067
+ promise.then(
4068
+ (value) => {
4069
+ signal.removeEventListener("abort", onAbort);
4070
+ resolve2(value);
4071
+ },
4072
+ (error) => {
4073
+ signal.removeEventListener("abort", onAbort);
4074
+ reject(error);
4075
+ }
4076
+ );
4077
+ });
4078
+ }
4079
+ async function waitForRetry(waitMs, signal) {
4080
+ if (!signal) {
4030
4081
  await new Promise((resolve2) => setTimeout(resolve2, waitMs));
4031
- return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
4082
+ return;
4032
4083
  }
4033
- const errorText2 = await response.text().catch(() => "unknown error");
4034
- throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
4084
+ const abortSignal = signal;
4085
+ if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
4086
+ await new Promise((resolve2, reject) => {
4087
+ const timer = setTimeout(done, waitMs);
4088
+ const onAbort = () => {
4089
+ clearTimeout(timer);
4090
+ abortSignal.removeEventListener("abort", onAbort);
4091
+ reject(new Error("Embedding API retry aborted"));
4092
+ };
4093
+ function done() {
4094
+ abortSignal.removeEventListener("abort", onAbort);
4095
+ resolve2();
4096
+ }
4097
+ abortSignal.addEventListener("abort", onAbort, { once: true });
4098
+ });
4035
4099
  }
4036
- var cache3, MAX_CACHE_SIZE3, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, APIEmbeddingProvider;
4100
+ var cache3, MAX_CACHE_SIZE3, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
4037
4101
  var init_api_provider = __esm({
4038
4102
  "src/embedding/api-provider.ts"() {
4039
4103
  "use strict";
@@ -4052,6 +4116,14 @@ var init_api_provider = __esm({
4052
4116
  DASHSCOPE_MAX_BATCH_SIZE = 10;
4053
4117
  MAX_RETRIES2 = 3;
4054
4118
  BASE_DELAY_MS = 500;
4119
+ EmbeddingAPIError = class extends Error {
4120
+ status;
4121
+ constructor(status, detail) {
4122
+ super(`Embedding API error (${status}): ${detail}`);
4123
+ this.name = "EmbeddingAPIError";
4124
+ this.status = status;
4125
+ }
4126
+ };
4055
4127
  APIEmbeddingProvider = class _APIEmbeddingProvider {
4056
4128
  name;
4057
4129
  dimensions;
@@ -4263,15 +4335,14 @@ var init_api_provider = __esm({
4263
4335
  }
4264
4336
  } catch (error) {
4265
4337
  const providerLimit = parseBatchLimit(error);
4266
- const fallbackSize = providerLimit ?? Math.ceil(chunkTexts.length / 2);
4267
- if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
4338
+ if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
4268
4339
  console.error(
4269
- `[memorix] Embedding batch too large for provider, retrying in chunks of ${fallbackSize}`
4340
+ `[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
4270
4341
  );
4271
- for (let start = 0; start < chunkTexts.length; start += fallbackSize) {
4342
+ for (let start = 0; start < chunkTexts.length; start += providerLimit) {
4272
4343
  await processChunk(
4273
- chunkTexts.slice(start, start + fallbackSize),
4274
- chunkIndices.slice(start, start + fallbackSize)
4344
+ chunkTexts.slice(start, start + providerLimit),
4345
+ chunkIndices.slice(start, start + providerLimit)
4275
4346
  );
4276
4347
  }
4277
4348
  return;
@@ -5135,22 +5206,23 @@ function getLLMConfig() {
5135
5206
  function setLLMConfig(config) {
5136
5207
  currentConfig = config;
5137
5208
  }
5138
- async function callLLM(systemPrompt, userMessage) {
5209
+ async function callLLM(systemPrompt, userMessage, signal) {
5139
5210
  if (!currentConfig) {
5140
5211
  throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
5141
5212
  }
5142
5213
  if (currentConfig.provider === "anthropic") {
5143
- return callAnthropic(systemPrompt, userMessage);
5214
+ return callAnthropic(systemPrompt, userMessage, signal);
5144
5215
  }
5145
- return callOpenAICompatible(systemPrompt, userMessage);
5216
+ return callOpenAICompatible(systemPrompt, userMessage, signal);
5146
5217
  }
5147
- async function callOpenAICompatible(systemPrompt, userMessage) {
5218
+ async function callOpenAICompatible(systemPrompt, userMessage, signal) {
5148
5219
  const config = currentConfig;
5149
5220
  let base = config.baseUrl.replace(/\/+$/, "");
5150
5221
  if (!base.endsWith("/v1")) base += "/v1";
5151
5222
  const url = `${base}/chat/completions`;
5223
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
5152
5224
  const response = await fetch(url, {
5153
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
5225
+ signal: fetchSignal,
5154
5226
  method: "POST",
5155
5227
  headers: {
5156
5228
  "Content-Type": "application/json",
@@ -5167,10 +5239,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
5167
5239
  })
5168
5240
  });
5169
5241
  if (!response.ok) {
5170
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
5242
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
5171
5243
  throw new Error(`LLM API error (${response.status}): ${error}`);
5172
5244
  }
5173
- const data = await readResponseJson(response);
5245
+ const data = await readResponseJson(response, signal);
5174
5246
  return {
5175
5247
  content: data.choices[0]?.message?.content ?? "",
5176
5248
  usage: data.usage ? {
@@ -5179,11 +5251,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
5179
5251
  } : void 0
5180
5252
  };
5181
5253
  }
5182
- async function callAnthropic(systemPrompt, userMessage) {
5254
+ async function callAnthropic(systemPrompt, userMessage, signal) {
5183
5255
  const config = currentConfig;
5184
5256
  const url = `${config.baseUrl}/messages`;
5257
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
5185
5258
  const response = await fetch(url, {
5186
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
5259
+ signal: fetchSignal,
5187
5260
  method: "POST",
5188
5261
  headers: {
5189
5262
  "Content-Type": "application/json",
@@ -5201,10 +5274,10 @@ async function callAnthropic(systemPrompt, userMessage) {
5201
5274
  })
5202
5275
  });
5203
5276
  if (!response.ok) {
5204
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
5277
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
5205
5278
  throw new Error(`Anthropic API error (${response.status}): ${error}`);
5206
5279
  }
5207
- const data = await readResponseJson(response);
5280
+ const data = await readResponseJson(response, signal);
5208
5281
  return {
5209
5282
  content: data.content[0]?.text ?? "",
5210
5283
  usage: data.usage ? {
@@ -5709,6 +5782,57 @@ function withTimeout(promise, ms, label) {
5709
5782
  );
5710
5783
  });
5711
5784
  }
5785
+ function withTimeoutSignal(operation, ms, label, parentSignal) {
5786
+ const controller = new AbortController();
5787
+ const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
5788
+ return new Promise((resolve2, reject) => {
5789
+ let settled = false;
5790
+ let timedOut = false;
5791
+ let timer;
5792
+ const cleanup = () => {
5793
+ if (timer !== void 0) clearTimeout(timer);
5794
+ parentSignal?.removeEventListener("abort", onParentAbort);
5795
+ };
5796
+ const onParentAbort = () => {
5797
+ if (settled || timedOut) return;
5798
+ settled = true;
5799
+ cleanup();
5800
+ reject(parentSignal?.reason ?? new Error(`${label} aborted`));
5801
+ };
5802
+ parentSignal?.addEventListener("abort", onParentAbort, { once: true });
5803
+ timer = setTimeout(() => {
5804
+ if (settled) return;
5805
+ timedOut = true;
5806
+ controller.abort(new Error(`${label} timed out after ${ms}ms`));
5807
+ settled = true;
5808
+ cleanup();
5809
+ reject(new Error(`${label} timed out after ${ms}ms`));
5810
+ }, ms);
5811
+ let pending;
5812
+ try {
5813
+ pending = operation(signal);
5814
+ } catch (error) {
5815
+ settled = true;
5816
+ cleanup();
5817
+ reject(error);
5818
+ return;
5819
+ }
5820
+ pending.then(
5821
+ (value) => {
5822
+ if (settled) return;
5823
+ settled = true;
5824
+ cleanup();
5825
+ resolve2(value);
5826
+ },
5827
+ (error) => {
5828
+ if (settled) return;
5829
+ settled = true;
5830
+ cleanup();
5831
+ reject(timedOut ? new Error(`${label} timed out after ${ms}ms`) : error);
5832
+ }
5833
+ );
5834
+ });
5835
+ }
5712
5836
  var init_timeout = __esm({
5713
5837
  "src/timeout.ts"() {
5714
5838
  "use strict";
@@ -6573,16 +6697,18 @@ async function searchObservations(options) {
6573
6697
  );
6574
6698
  } else {
6575
6699
  const EMBEDDING_TIMEOUT_MS = 15e3;
6576
- queryVector = await withTimeout(
6577
- provider2.embedInput ? provider2.embedInput(
6700
+ queryVector = await withTimeoutSignal(
6701
+ (signal) => provider2.embedInput ? provider2.embedInput(
6578
6702
  { modality: "text", text: expandedEmbeddingQuery },
6579
- { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
6703
+ { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
6580
6704
  ) : provider2.embed(expandedEmbeddingQuery, {
6581
6705
  timeoutMs: EMBEDDING_TIMEOUT_MS,
6582
- retry: false
6706
+ retry: false,
6707
+ signal
6583
6708
  }),
6584
6709
  EMBEDDING_TIMEOUT_MS,
6585
- "Embedding"
6710
+ "Embedding",
6711
+ options.signal
6586
6712
  );
6587
6713
  mark("embedding");
6588
6714
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
@@ -145002,7 +145128,13 @@ async function maintainLongTermMemories(input) {
145002
145128
  }
145003
145129
  for (const group of groups.values()) {
145004
145130
  if (group.length < 2) continue;
145005
- group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
145131
+ group.sort((a, b) => {
145132
+ const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
145133
+ if (byCreatedAt !== 0) return byCreatedAt;
145134
+ const byUpdatedAt = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
145135
+ if (byUpdatedAt !== 0) return byUpdatedAt;
145136
+ return b.id.localeCompare(a.id);
145137
+ });
145006
145138
  const newest = group[0];
145007
145139
  for (const older of group.slice(1)) {
145008
145140
  try {
@@ -148730,6 +148862,7 @@ var init_task_lens = __esm({
148730
148862
  var workflows_exports = {};
148731
148863
  __export(workflows_exports, {
148732
148864
  WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
148865
+ WORKFLOW_AGENT_TARGETS: () => WORKFLOW_AGENT_TARGETS,
148733
148866
  applyWorkflowAdapter: () => applyWorkflowAdapter,
148734
148867
  importWindsurfWorkflows: () => importWindsurfWorkflows,
148735
148868
  parseWorkflowMarkdown: () => parseWorkflowMarkdown,
@@ -148795,7 +148928,7 @@ function optionalStatus(data) {
148795
148928
  return value;
148796
148929
  }
148797
148930
  function normalizeAgents(values) {
148798
- const unknown3 = values.filter((value) => !KNOWN_AGENTS.includes(value));
148931
+ const unknown3 = values.filter((value) => !WORKFLOW_AGENT_TARGETS.includes(value));
148799
148932
  if (unknown3.length > 0) {
148800
148933
  throw new Error("workflow frontmatter has unsupported agent: " + unknown3[0]);
148801
148934
  }
@@ -149071,7 +149204,7 @@ function taskScore(workflow, task) {
149071
149204
  return { score, reasons: [...new Set(reasons)] };
149072
149205
  }
149073
149206
  function selectWorkflows(input) {
149074
- const selected = input.workflows.filter((workflow) => workflow.status === "active").map((workflow) => {
149207
+ const selected = input.workflows.filter((workflow) => workflow.status === "active" && (!input.agent || workflow.compatibleAgents.includes(input.agent))).map((workflow) => {
149075
149208
  const match2 = taskScore(workflow, input.task);
149076
149209
  const cautions = input.store && input.projectId ? input.store.recentFailureCautions(input.projectId, workflow.id) : [];
149077
149210
  return {
@@ -149351,7 +149484,7 @@ async function selectWorkspaceWorkflows(input) {
149351
149484
  errors: synced.errors
149352
149485
  };
149353
149486
  }
149354
- var KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
149487
+ var WORKFLOW_AGENT_TARGETS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
149355
149488
  var init_workflows2 = __esm({
149356
149489
  "src/knowledge/workflows.ts"() {
149357
149490
  "use strict";
@@ -149363,7 +149496,7 @@ var init_workflows2 = __esm({
149363
149496
  init_workflow_store();
149364
149497
  init_outcome_store();
149365
149498
  init_task_lens();
149366
- KNOWN_AGENTS = [
149499
+ WORKFLOW_AGENT_TARGETS = [
149367
149500
  "windsurf",
149368
149501
  "cursor",
149369
149502
  "claude-code",
@@ -151488,13 +151621,13 @@ function verifyType(declaredType, narrative, title) {
151488
151621
  }
151489
151622
  return { type: declaredType, corrected: false };
151490
151623
  }
151491
- async function extractFactsWithLLM(narrative, title, existingFacts) {
151624
+ async function extractFactsWithLLM(narrative, title, existingFacts, signal) {
151492
151625
  try {
151493
151626
  const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
151494
151627
  const input = `Title: ${title}
151495
151628
  Content: ${narrative}${existingFacts.length > 0 ? `
151496
151629
  Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
151497
- const response = await callLLM2(LLM_EXTRACT_PROMPT, input);
151630
+ const response = await callLLM2(LLM_EXTRACT_PROMPT, input, signal);
151498
151631
  const text2 = response.content.trim();
151499
151632
  const jsonMatch = text2.match(/\{[\s\S]*\}/);
151500
151633
  if (!jsonMatch) return [];
@@ -151503,15 +151636,16 @@ Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
151503
151636
  if (!Array.isArray(facts)) return [];
151504
151637
  const existingLower = new Set(existingFacts.map((f3) => f3.toLowerCase().trim()));
151505
151638
  return facts.filter((f3) => typeof f3 === "string" && f3.length >= 5).filter((f3) => !existingLower.has(f3.toLowerCase().trim())).slice(0, 10);
151506
- } catch {
151639
+ } catch (error) {
151640
+ if (signal?.aborted) throw error;
151507
151641
  return [];
151508
151642
  }
151509
151643
  }
151510
- async function runExtract(input, existingEntities, useLLM = false) {
151644
+ async function runExtract(input, existingEntities, useLLM = false, signal) {
151511
151645
  const callerFacts = input.facts ?? [];
151512
151646
  let extractedFacts;
151513
151647
  if (useLLM) {
151514
- extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts);
151648
+ extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts, signal);
151515
151649
  if (extractedFacts.length === 0) {
151516
151650
  extractedFacts = extractFacts2(input.narrative, callerFacts);
151517
151651
  }
@@ -151731,7 +151865,7 @@ function mergeFacts2(oldFacts, newFacts) {
151731
151865
  }
151732
151866
  return merged;
151733
151867
  }
151734
- async function resolveWithLLM(extracted, hits, getObservation2) {
151868
+ async function resolveWithLLM(extracted, hits, getObservation2, signal) {
151735
151869
  try {
151736
151870
  const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
151737
151871
  const existingMemories = hits.slice(0, 5).map((h2, i2) => ({
@@ -151748,7 +151882,7 @@ Facts: ${extracted.facts.join("; ")}
151748
151882
 
151749
151883
  EXISTING MEMORIES:
151750
151884
  ${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Facts: ${m2.facts}`).join("\n")}`;
151751
- const response = await callLLM2(LLM_RESOLVE_PROMPT, input);
151885
+ const response = await callLLM2(LLM_RESOLVE_PROMPT, input, signal);
151752
151886
  const text2 = response.content.trim();
151753
151887
  const jsonMatch = text2.match(/\{[\s\S]*\}/);
151754
151888
  if (!jsonMatch) return null;
@@ -151785,7 +151919,8 @@ ${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Fact
151785
151919
  };
151786
151920
  }
151787
151921
  return null;
151788
- } catch {
151922
+ } catch (error) {
151923
+ if (signal?.aborted) throw error;
151789
151924
  return null;
151790
151925
  }
151791
151926
  }
@@ -151803,19 +151938,20 @@ function scoreCandidate(extracted, candidate) {
151803
151938
  const contradiction = hasContradiction(candidate.narrative, extracted.narrative);
151804
151939
  return { score, searchSimilarity, entityMatch, richer, contradiction };
151805
151940
  }
151806
- async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false) {
151941
+ async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false, signal) {
151807
151942
  const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
151808
151943
  let hits;
151809
151944
  try {
151810
- hits = await searchMemories(query, 5, projectId);
151811
- } catch {
151945
+ hits = await searchMemories(query, 5, projectId, signal);
151946
+ } catch (error) {
151947
+ if (signal?.aborted) throw error;
151812
151948
  return { action: "new", reason: "Search unavailable, defaulting to new" };
151813
151949
  }
151814
151950
  if (hits.length === 0) {
151815
151951
  return { action: "new", reason: "No similar existing memories found" };
151816
151952
  }
151817
151953
  if (useLLM) {
151818
- const llmResult = await resolveWithLLM(extracted, hits, getObservation2);
151954
+ const llmResult = await resolveWithLLM(extracted, hits, getObservation2, signal);
151819
151955
  if (llmResult) return llmResult;
151820
151956
  }
151821
151957
  const scored = hits.map((hit) => ({
@@ -152237,7 +152373,7 @@ async function runFormation(input, config) {
152237
152373
  const existingEntities = config.getEntityNames();
152238
152374
  const extractStartTime = Date.now();
152239
152375
  emitStageEvent("extract", "start");
152240
- const extraction = await runExtract(input, existingEntities, config.useLLM);
152376
+ const extraction = await runExtract(input, existingEntities, config.useLLM, config.signal);
152241
152377
  stageDurationsMs.extract = Date.now() - extractStartTime;
152242
152378
  emitStageEvent("extract", "success", stageDurationsMs.extract);
152243
152379
  stagesCompleted = 1;
@@ -152257,7 +152393,8 @@ async function runFormation(input, config) {
152257
152393
  input.projectId,
152258
152394
  config.searchMemories,
152259
152395
  config.getObservation,
152260
- config.useLLM
152396
+ config.useLLM,
152397
+ config.signal
152261
152398
  );
152262
152399
  stageDurationsMs.resolve = Date.now() - resolveStartTime;
152263
152400
  emitStageEvent("resolve", "success", stageDurationsMs.resolve);
@@ -153010,6 +153147,15 @@ function workflowOutput(selection) {
153010
153147
  cautions: selection.cautions.map((caution) => short(caution, 22))
153011
153148
  };
153012
153149
  }
153150
+ function workflowLoadout(agent, selections) {
153151
+ if (!agent) return void 0;
153152
+ return {
153153
+ agent,
153154
+ workflowIds: selections.map((selection) => selection.workflow.id),
153155
+ requiredContext: unique(selections.flatMap((selection) => selection.workflow.requiredContext)).map((item) => short(item, 18)).slice(0, 4),
153156
+ allowedTools: unique(selections.flatMap((selection) => selection.workflow.allowedTools)).map((item) => short(item, 12)).slice(0, 6)
153157
+ };
153158
+ }
153013
153159
  function appendLine(lines, candidate, maxTokens, omitted, omittedKind, selected, receiptSelection) {
153014
153160
  const next = lines.length ? lines.join("\n") + "\n" + candidate : candidate;
153015
153161
  if (countTextTokens(next) <= maxTokens) {
@@ -153470,6 +153616,22 @@ function renderTaskWorksetPrompt(input) {
153470
153616
  );
153471
153617
  }
153472
153618
  }
153619
+ if (input.agentLoadout) {
153620
+ appendLine(lines, "", maxTokens, omitted, "agent-loadout-heading");
153621
+ appendLine(lines, "Agent loadout (" + input.agentLoadout.agent + ")", maxTokens, omitted, "agent-loadout-heading");
153622
+ for (const required of input.agentLoadout.requiredContext.slice(0, 2)) {
153623
+ appendLine(lines, "- Need: " + required, maxTokens, omitted, "agent-loadout-context");
153624
+ }
153625
+ if (input.agentLoadout.allowedTools.length > 0) {
153626
+ appendLine(
153627
+ lines,
153628
+ "- Workflow tools: " + input.agentLoadout.allowedTools.join(", "),
153629
+ maxTokens,
153630
+ omitted,
153631
+ "agent-loadout-tools"
153632
+ );
153633
+ }
153634
+ }
153473
153635
  if (input.verification.length > 0) {
153474
153636
  appendLine(lines, "", maxTokens, omitted, "verification-heading");
153475
153637
  appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
@@ -153561,6 +153723,7 @@ async function buildTaskWorkset(input) {
153561
153723
  let workspace;
153562
153724
  let pages = [];
153563
153725
  let workflows = [];
153726
+ let workflowSelections = [];
153564
153727
  try {
153565
153728
  workspace = await preferredWorkspace(input.projectId, input.dataDir);
153566
153729
  if (workspace) {
@@ -153577,14 +153740,15 @@ async function buildTaskWorkset(input) {
153577
153740
  if (task) {
153578
153741
  const workflowStore = new WorkflowStore();
153579
153742
  await workflowStore.init(input.dataDir);
153580
- const selected = selectWorkflows({
153743
+ workflowSelections = selectWorkflows({
153581
153744
  workflows: workflowStore.listWorkflows(workspace.id, "active"),
153582
153745
  task,
153583
153746
  projectId: input.projectId,
153584
153747
  store: workflowStore,
153748
+ ...input.agent ? { agent: input.agent } : {},
153585
153749
  limit: 2
153586
153750
  });
153587
- workflows = selected.map(workflowOutput);
153751
+ workflows = workflowSelections.map(workflowOutput);
153588
153752
  for (const workflow of workflows) {
153589
153753
  for (const caution of workflow.cautions) {
153590
153754
  cautions.push({ kind: "workflow-failed-verification", message: caution });
@@ -153606,6 +153770,10 @@ async function buildTaskWorkset(input) {
153606
153770
  workflows
153607
153771
  });
153608
153772
  const governedDurableIds = new Set(governed.durableMemory.map((memory) => memory.id));
153773
+ const agentLoadout = workflowLoadout(
153774
+ input.agent,
153775
+ workflowSelections.filter((selection2) => governed.workflows.some((workflow) => workflow.id === selection2.workflow.id))
153776
+ );
153609
153777
  const evidenceIds = unique(governed.claims.flatMap((claim) => evidenceIdsForClaim(
153610
153778
  claim,
153611
153779
  evidenceByClaim.get(claim.id) ?? []
@@ -153650,6 +153818,7 @@ async function buildTaskWorkset(input) {
153650
153818
  pages: pages.filter((page) => page.claimIds.some((claimId) => governed.claims.some((claim) => claim.id === claimId))),
153651
153819
  durableMemory: governed.durableMemory,
153652
153820
  workflows: governed.workflows,
153821
+ ...agentLoadout ? { agentLoadout } : {},
153653
153822
  cautions: normalizedCautions,
153654
153823
  verification,
153655
153824
  evidenceIds,
@@ -153868,10 +154037,12 @@ var init_current_facts = __esm({
153868
154037
  var external_provider_exports = {};
153869
154038
  __export(external_provider_exports, {
153870
154039
  DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS: () => DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS,
154040
+ EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS: () => EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS,
153871
154041
  MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES: () => MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES,
153872
154042
  MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS: () => MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS,
153873
154043
  getExternalCodeGraphContext: () => getExternalCodeGraphContext,
153874
- inspectExternalCodeGraph: () => inspectExternalCodeGraph
154044
+ inspectExternalCodeGraph: () => inspectExternalCodeGraph,
154045
+ runExternalCodeGraphLifecycle: () => runExternalCodeGraphLifecycle
153875
154046
  });
153876
154047
  import { spawn as spawn5 } from "child_process";
153877
154048
  import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
@@ -154070,6 +154241,66 @@ async function inspectExternalCodeGraph(input) {
154070
154241
  const health = healthFromStatus(status);
154071
154242
  return { health, quality: providerQuality({ mode, health }) };
154072
154243
  }
154244
+ async function runExternalCodeGraphLifecycle(input) {
154245
+ const mode = input.mode ?? "auto";
154246
+ if (mode === "off") {
154247
+ const health = { state: "disabled", reason: "External CodeGraph is disabled by configuration." };
154248
+ return {
154249
+ action: input.action,
154250
+ performed: false,
154251
+ message: "External CodeGraph is disabled by configuration.",
154252
+ health,
154253
+ quality: providerQuality({ mode, health })
154254
+ };
154255
+ }
154256
+ const indexPath = join20(input.projectRoot, ".codegraph");
154257
+ if (input.action === "sync" && !existsSync15(indexPath)) {
154258
+ const health = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
154259
+ return {
154260
+ action: input.action,
154261
+ performed: false,
154262
+ message: 'Initialize CodeGraph first with "memorix codegraph init".',
154263
+ health,
154264
+ quality: providerQuality({ mode, health })
154265
+ };
154266
+ }
154267
+ if (input.action === "init" && existsSync15(indexPath)) {
154268
+ const inspected2 = await inspectExternalCodeGraph(input);
154269
+ return {
154270
+ action: input.action,
154271
+ performed: false,
154272
+ message: "A local CodeGraph directory already exists; no initialization was performed.",
154273
+ ...inspected2
154274
+ };
154275
+ }
154276
+ const result = await runSafely(input.runner ?? defaultRunner, {
154277
+ command: input.command,
154278
+ args: input.action === "init" ? ["init", input.projectRoot] : ["sync", input.projectRoot, "--quiet"],
154279
+ cwd: input.projectRoot,
154280
+ // A context read must stay fast; an explicit user-requested index sync is
154281
+ // a different operation and gets its own bounded maintenance window.
154282
+ timeoutMs: EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS,
154283
+ maxOutputBytes: MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES
154284
+ });
154285
+ if (!result.ok) {
154286
+ const health = unavailableHealth(result);
154287
+ return {
154288
+ action: input.action,
154289
+ performed: false,
154290
+ message: "CodeGraph " + input.action + " did not complete: " + (health.reason ?? "unknown local error"),
154291
+ health,
154292
+ quality: providerQuality({ mode, health })
154293
+ };
154294
+ }
154295
+ const inspected = await inspectExternalCodeGraph(input);
154296
+ const remainsStale = inspected.health.state === "stale";
154297
+ return {
154298
+ action: input.action,
154299
+ performed: true,
154300
+ message: input.action === "init" ? 'Local CodeGraph initialization completed. Run "memorix codegraph sync" when source changes.' : remainsStale ? "Local CodeGraph synchronization completed, but the graph remains stale against the current worktree; this task will use Lite evidence." : "Local CodeGraph synchronization completed and is ready for task-scoped semantic context.",
154301
+ ...inspected
154302
+ };
154303
+ }
154073
154304
  function safeRelativePath(projectRoot, value, exclude) {
154074
154305
  const path34 = safeText(value, 1024);
154075
154306
  if (!path34 || isAbsolute(path34)) return void 0;
@@ -154195,7 +154426,7 @@ async function getExternalCodeGraphContext(input) {
154195
154426
  quality: providerQuality({ mode, health: inspected.health, ...contributes ? { selected: "external" } : {} })
154196
154427
  };
154197
154428
  }
154198
- var DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES, MAX_EXTERNAL_NODE_COUNT, MAX_EXTERNAL_EDGE_COUNT, MAX_EXTERNAL_FILE_COUNT, MAX_EXTERNAL_TEXT_LENGTH, LITE_SUPPORTED_LANGUAGES, defaultRunner;
154429
+ var DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS, EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES, MAX_EXTERNAL_NODE_COUNT, MAX_EXTERNAL_EDGE_COUNT, MAX_EXTERNAL_FILE_COUNT, MAX_EXTERNAL_TEXT_LENGTH, LITE_SUPPORTED_LANGUAGES, defaultRunner;
154199
154430
  var init_external_provider = __esm({
154200
154431
  "src/codegraph/external-provider.ts"() {
154201
154432
  "use strict";
@@ -154205,6 +154436,7 @@ var init_external_provider = __esm({
154205
154436
  init_ids();
154206
154437
  DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 1200;
154207
154438
  MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 5e3;
154439
+ EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS = 3e4;
154208
154440
  MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES = 256 * 1024;
154209
154441
  MAX_EXTERNAL_NODE_COUNT = 16;
154210
154442
  MAX_EXTERNAL_EDGE_COUNT = 24;
@@ -155030,6 +155262,7 @@ async function buildAutoProjectContext(input) {
155030
155262
  projectId: input.project.id,
155031
155263
  dataDir: input.dataDir,
155032
155264
  ...task ? { task } : {},
155265
+ ...input.agent ? { agent: input.agent } : {},
155033
155266
  lens: lens.id,
155034
155267
  startHere,
155035
155268
  ...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
@@ -155366,6 +155599,34 @@ var init_auto_context = __esm({
155366
155599
  }
155367
155600
  });
155368
155601
 
155602
+ // src/knowledge/context-receipt.ts
155603
+ var context_receipt_exports = {};
155604
+ __export(context_receipt_exports, {
155605
+ buildBoundedContextReceipt: () => buildBoundedContextReceipt
155606
+ });
155607
+ function buildBoundedContextReceipt(input) {
155608
+ const provider2 = input.providerQuality ?? input.workset.provenance.codeProvider;
155609
+ return {
155610
+ schemaVersion: "1",
155611
+ task: input.workset.task,
155612
+ brief: input.workset.prompt,
155613
+ receipt: input.workset.receipt,
155614
+ code: {
155615
+ selected: provider2?.selected ?? "lite",
155616
+ quality: provider2?.selectedQuality ?? "heuristic",
155617
+ externalState: provider2?.external.state ?? "not-detected",
155618
+ ...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
155619
+ },
155620
+ ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
155621
+ };
155622
+ }
155623
+ var init_context_receipt = __esm({
155624
+ "src/knowledge/context-receipt.ts"() {
155625
+ "use strict";
155626
+ init_esm_shims();
155627
+ }
155628
+ });
155629
+
155369
155630
  // src/codegraph/context-pack.ts
155370
155631
  var context_pack_exports = {};
155371
155632
  __export(context_pack_exports, {
@@ -155524,6 +155785,7 @@ async function attachTaskWorkset(input) {
155524
155785
  projectId: input.projectId,
155525
155786
  dataDir: input.dataDir,
155526
155787
  task: input.pack.task,
155788
+ ...input.agent ? { agent: input.agent } : {},
155527
155789
  lens: input.lens,
155528
155790
  currentFacts: input.currentFacts ?? [],
155529
155791
  ...input.codeState ? { codeState: input.codeState } : {},
@@ -159916,8 +160178,8 @@ var init_official_skills = __esm({
159916
160178
  "",
159917
160179
  "| Situation | Prefer | CLI fallback |",
159918
160180
  "|---|---|---|",
159919
- '| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --json` |',
159920
- '| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --json` |',
160181
+ '| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --brief-json` |',
160182
+ '| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --brief-json` |',
159921
160183
  '| Need structured refs/freshness for code-bound memories | `memorix_context_pack` | `memorix codegraph context-pack --task "<topic>"` |',
159922
160184
  '| Explicit memory graph question | `memorix_graph_context` | `memorix memory graph-context --query "<topic>"` |',
159923
160185
  '| Specific past decision, bug, file, or change | `memorix_search` | `memorix memory search --query "<topic>"` |',
@@ -159930,7 +160192,7 @@ var init_official_skills = __esm({
159930
160192
  "",
159931
160193
  "- Default first step for non-trivial coding work: call `memorix_project_context` before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Pass the user's actual task so Memorix can choose a bugfix, feature, release, onboarding, refactor, docs, test, or general brief. Start by reading its suggested files.",
159932
160194
  "- In Claude Code headless/print-mode, an initial MCP `pending` status is not a failure by itself. Prefer MCP first: use the client's tool discovery/dynamic loading to find `memorix_project_context` when it is not in the first visible tool list.",
159933
- '- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --json` instead. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
160195
+ '- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --brief-json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --brief-json` instead. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
159934
160196
  "- After a successful `memorix_project_context`, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use Context Pack, search, or detail only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
159935
160197
  "- If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
159936
160198
  "- Use `memorix_search` for a specific decision, bug, file, or prior change that the project context did not answer. Fetch detail only for a result you still need after the brief.",
@@ -161189,7 +161451,7 @@ alwaysApply: true
161189
161451
  "",
161190
161452
  `Default first step for non-trivial coding work: call \`memorix_project_context\` with the user's actual task before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Memorix will choose a task-lensed brief (bugfix, feature, release, onboarding, refactor, docs, test, or general). When the task is continuing prior work, the same brief also includes a bounded prior-work projection. Treat its "Start here" files as the first ${contextNoun} files to inspect.`,
161191
161453
  "",
161192
- 'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --json`. For a new task, use `memorix context "<task>" --json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
161454
+ 'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --brief-json`. For a new task, use `memorix context "<task>" --brief-json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
161193
161455
  "",
161194
161456
  "After a successful `memorix_project_context` result, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use `memorix_context_pack`, `memorix_search`, or `memorix_detail` only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
161195
161457
  "If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
@@ -165642,7 +165904,7 @@ The path should point to a directory containing a .git folder.`
165642
165904
  };
165643
165905
  const server = existingServer ?? new McpServer({
165644
165906
  name: "memorix",
165645
- version: true ? "1.5.0" : "1.0.1"
165907
+ version: true ? "1.6.0" : "1.0.1"
165646
165908
  });
165647
165909
  const originalRegisterTool = server.registerTool.bind(server);
165648
165910
  server.registerTool = ((name, ...args) => {
@@ -165831,8 +166093,8 @@ The path should point to a directory containing a .git folder.`
165831
166093
  mode: "active",
165832
166094
  useLLM: isLLMEnabled(),
165833
166095
  minValueScore: 0.3,
165834
- searchMemories: async (q, limit2, pid) => {
165835
- const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader });
166096
+ searchMemories: async (q, limit2, pid, signal) => {
166097
+ const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader, signal });
165836
166098
  if (result.entries.length === 0) return [];
165837
166099
  const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
165838
166100
  return details.documents.map((d, i2) => ({
@@ -165862,16 +166124,19 @@ The path should point to a directory containing a .git folder.`
165862
166124
  getEntityNames: () => graphManager.getEntityNames(),
165863
166125
  onStageEvent: onFormationStageEvent
165864
166126
  };
165865
- formationResult = await withTimeout(
165866
- runFormation({
165867
- entityName,
165868
- type,
165869
- title,
165870
- narrative,
165871
- facts: safeFacts,
165872
- projectId: project.id,
165873
- source: "explicit"
165874
- }, formationConfig),
166127
+ formationResult = await withTimeoutSignal(
166128
+ async (signal) => {
166129
+ formationConfig.signal = signal;
166130
+ return runFormation({
166131
+ entityName,
166132
+ type,
166133
+ title,
166134
+ narrative,
166135
+ facts: safeFacts,
166136
+ projectId: project.id,
166137
+ source: "explicit"
166138
+ }, formationConfig);
166139
+ },
165875
166140
  FORMATION_TIMEOUT_MS,
165876
166141
  "Formation pipeline"
165877
166142
  );
@@ -166221,8 +166486,8 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
166221
166486
  mode: formationMode,
166222
166487
  useLLM: isLLMEnabled(),
166223
166488
  minValueScore: 0.3,
166224
- searchMemories: async (q, limit2, pid) => {
166225
- const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader });
166489
+ searchMemories: async (q, limit2, pid, signal) => {
166490
+ const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader, signal });
166226
166491
  if (result.entries.length === 0) return [];
166227
166492
  const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
166228
166493
  return details.documents.map((d, i2) => ({
@@ -166243,8 +166508,11 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
166243
166508
  },
166244
166509
  getEntityNames: () => graphManager.getEntityNames()
166245
166510
  };
166246
- const formed = await withTimeout(
166247
- runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
166511
+ const formed = await withTimeoutSignal(
166512
+ async (signal) => {
166513
+ formationConfig.signal = signal;
166514
+ return runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig);
166515
+ },
166248
166516
  FORMATION_TIMEOUT_MS,
166249
166517
  "Shadow formation"
166250
166518
  );
@@ -166470,13 +166738,28 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
166470
166738
  refresh: z626.enum(["auto", "always", "never"]).optional().default("auto").describe(
166471
166739
  "Code Memory refresh policy. auto refreshes only when missing or stale."
166472
166740
  ),
166473
- format: z626.enum(["prompt", "summary", "json"]).optional().default("prompt").describe(
166474
- 'Output format. "prompt" is agent-ready; "summary" is human-readable; "json" is structured.'
166741
+ format: z626.enum(["prompt", "summary", "json", "receipt"]).optional().default("prompt").describe(
166742
+ 'Output format. "prompt" is agent-ready; "summary" is human-readable; "receipt" is bounded JSON; "json" is detailed diagnostics.'
166475
166743
  ),
166744
+ agent: z626.enum([
166745
+ "windsurf",
166746
+ "cursor",
166747
+ "claude-code",
166748
+ "codex",
166749
+ "copilot",
166750
+ "antigravity",
166751
+ "gemini-cli",
166752
+ "openclaw",
166753
+ "hermes",
166754
+ "omp",
166755
+ "kiro",
166756
+ "opencode",
166757
+ "trae"
166758
+ ]).optional().describe("Optional target agent for compatible workflow selection."),
166476
166759
  limit: z626.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
166477
166760
  }
166478
166761
  },
166479
- async ({ task, refresh, format }) => {
166762
+ async ({ task, refresh, format, agent }) => {
166480
166763
  const unresolved = requireResolvedProject("build project context for the current project");
166481
166764
  if (unresolved) return unresolved;
166482
166765
  const [
@@ -166488,12 +166771,14 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
166488
166771
  },
166489
166772
  { getObservationStore: getObservationStore2 },
166490
166773
  { MaintenanceJobStore: MaintenanceJobStore2 },
166491
- { enqueueCodegraphRefresh: enqueueCodegraphRefresh2 }
166774
+ { enqueueCodegraphRefresh: enqueueCodegraphRefresh2 },
166775
+ { buildBoundedContextReceipt: buildBoundedContextReceipt2 }
166492
166776
  ] = await Promise.all([
166493
166777
  Promise.resolve().then(() => (init_auto_context(), auto_context_exports)),
166494
166778
  Promise.resolve().then(() => (init_obs_store(), obs_store_exports)),
166495
166779
  Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
166496
- Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
166780
+ Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports)),
166781
+ Promise.resolve().then(() => (init_context_receipt(), context_receipt_exports))
166497
166782
  ]);
166498
166783
  const observations2 = filterReadableObservations(
166499
166784
  await getObservationStore2().loadByProject(project.id, { status: "active" }),
@@ -166504,6 +166789,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
166504
166789
  dataDir: projectDir2,
166505
166790
  observations: observations2,
166506
166791
  task,
166792
+ agent,
166507
166793
  refresh: refresh ?? "auto",
166508
166794
  reader: getObservationReader(),
166509
166795
  enqueueRefresh: () => {
@@ -166516,7 +166802,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
166516
166802
  });
166517
166803
  }
166518
166804
  });
166519
- const text2 = format === "json" ? JSON.stringify({ ...context, brief: buildAutoProjectBrief2(context) }, null, 2) : format === "summary" ? formatAutoProjectContextSummary2(context) : formatAutoProjectContextPrompt2(context);
166805
+ const text2 = format === "json" ? JSON.stringify({ ...context, brief: buildAutoProjectBrief2(context) }, null, 2) : format === "receipt" ? JSON.stringify(buildBoundedContextReceipt2({
166806
+ workset: context.workset,
166807
+ providerQuality: context.providerQuality
166808
+ }), null, 2) : format === "summary" ? formatAutoProjectContextSummary2(context) : formatAutoProjectContextPrompt2(context);
166520
166809
  autopilotRetrievalBoundary = {
166521
166810
  projectId: project.id,
166522
166811
  issuedAt: Date.now(),