memorix 1.2.0 → 1.2.2

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 (212) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +18 -4
  3. package/README.zh-CN.md +18 -4
  4. package/TEAM.md +86 -86
  5. package/dist/cli/index.js +15919 -14055
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.js +1997 -1021
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.d.ts +1 -1
  10. package/dist/maintenance-runner.js +8481 -8005
  11. package/dist/maintenance-runner.js.map +1 -1
  12. package/dist/memcode-runtime/CHANGELOG.md +30 -1
  13. package/dist/sdk.d.ts +7 -2
  14. package/dist/sdk.js +2022 -1024
  15. package/dist/sdk.js.map +1 -1
  16. package/dist/types.d.ts +49 -1
  17. package/dist/types.js.map +1 -1
  18. package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
  19. package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
  20. package/docs/API_REFERENCE.md +27 -5
  21. package/docs/DESIGN_DECISIONS.md +357 -357
  22. package/docs/DEVELOPMENT.md +4 -0
  23. package/docs/README.md +1 -1
  24. package/docs/SETUP.md +7 -1
  25. package/docs/dev-log/progress.txt +91 -11
  26. package/docs/knowledge/workflows/memorix-release.md +57 -0
  27. package/package.json +1 -1
  28. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  29. package/src/audit/index.ts +156 -156
  30. package/src/cli/command-guide.ts +192 -0
  31. package/src/cli/commands/audit-list.ts +89 -89
  32. package/src/cli/commands/audit.ts +9 -4
  33. package/src/cli/commands/background.ts +659 -659
  34. package/src/cli/commands/cleanup.ts +5 -1
  35. package/src/cli/commands/codegraph.ts +17 -8
  36. package/src/cli/commands/context.ts +3 -2
  37. package/src/cli/commands/doctor.ts +4 -2
  38. package/src/cli/commands/explain.ts +9 -3
  39. package/src/cli/commands/formation.ts +48 -48
  40. package/src/cli/commands/git-hook-install.ts +111 -111
  41. package/src/cli/commands/handoff.ts +75 -61
  42. package/src/cli/commands/hooks-status.ts +63 -63
  43. package/src/cli/commands/identity.ts +116 -0
  44. package/src/cli/commands/ingest-commit.ts +153 -153
  45. package/src/cli/commands/ingest-image.ts +71 -69
  46. package/src/cli/commands/ingest-log.ts +180 -180
  47. package/src/cli/commands/ingest.ts +44 -44
  48. package/src/cli/commands/integrate-shared.ts +15 -15
  49. package/src/cli/commands/knowledge.ts +40 -0
  50. package/src/cli/commands/lock.ts +93 -92
  51. package/src/cli/commands/memory.ts +58 -21
  52. package/src/cli/commands/message.ts +123 -118
  53. package/src/cli/commands/operator-shared.ts +98 -3
  54. package/src/cli/commands/poll.ts +74 -64
  55. package/src/cli/commands/purge-all-memory.ts +85 -85
  56. package/src/cli/commands/purge-project-memory.ts +83 -83
  57. package/src/cli/commands/reasoning.ts +135 -121
  58. package/src/cli/commands/retention.ts +9 -4
  59. package/src/cli/commands/serve-http.ts +22 -43
  60. package/src/cli/commands/serve-shared.ts +118 -118
  61. package/src/cli/commands/session.ts +29 -3
  62. package/src/cli/commands/setup.ts +9 -3
  63. package/src/cli/commands/skills.ts +124 -119
  64. package/src/cli/commands/status.ts +4 -3
  65. package/src/cli/commands/task.ts +193 -184
  66. package/src/cli/commands/team.ts +14 -10
  67. package/src/cli/commands/transfer.ts +108 -55
  68. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  69. package/src/cli/identity.ts +89 -0
  70. package/src/cli/index.ts +96 -19
  71. package/src/cli/invocation.ts +115 -0
  72. package/src/cli/tui/ChatView.tsx +234 -234
  73. package/src/cli/tui/CommandBar.tsx +312 -312
  74. package/src/cli/tui/ContextRail.tsx +118 -118
  75. package/src/cli/tui/HeaderBar.tsx +72 -72
  76. package/src/cli/tui/LogoBanner.tsx +51 -51
  77. package/src/cli/tui/Sidebar.tsx +179 -179
  78. package/src/cli/tui/chat-service.ts +41 -18
  79. package/src/cli/tui/data.ts +23 -44
  80. package/src/cli/tui/index.ts +41 -41
  81. package/src/cli/tui/markdown-render.tsx +371 -371
  82. package/src/cli/tui/operator-context.ts +60 -0
  83. package/src/cli/tui/use-mouse.ts +157 -157
  84. package/src/cli/tui/useNavigation.ts +56 -56
  85. package/src/cli/tui/views/MemoryView.tsx +10 -8
  86. package/src/cli/update-checker.ts +211 -211
  87. package/src/cli/version.ts +7 -7
  88. package/src/cli/workbench.ts +1 -1
  89. package/src/codegraph/auto-context.ts +34 -17
  90. package/src/codegraph/context-pack.ts +1 -0
  91. package/src/codegraph/current-facts.ts +19 -1
  92. package/src/codegraph/project-context.ts +2 -0
  93. package/src/codegraph/task-lens.ts +49 -5
  94. package/src/compact/engine.ts +26 -10
  95. package/src/compact/index-format.ts +25 -2
  96. package/src/compact/token-budget.ts +74 -74
  97. package/src/dashboard/project-classification.ts +64 -64
  98. package/src/dashboard/server.ts +58 -52
  99. package/src/embedding/fastembed-provider.ts +142 -142
  100. package/src/embedding/transformers-provider.ts +111 -111
  101. package/src/git/extractor.ts +209 -209
  102. package/src/git/hooks-path.ts +85 -85
  103. package/src/hooks/admission.ts +117 -0
  104. package/src/hooks/handler.ts +98 -91
  105. package/src/hooks/pattern-detector.ts +173 -173
  106. package/src/hooks/significance-filter.ts +250 -250
  107. package/src/knowledge/claims.ts +51 -1
  108. package/src/knowledge/context-assembly.ts +97 -0
  109. package/src/knowledge/types.ts +1 -0
  110. package/src/knowledge/workflows.ts +34 -3
  111. package/src/knowledge/workset.ts +179 -10
  112. package/src/llm/memory-manager.ts +328 -328
  113. package/src/llm/provider.ts +885 -885
  114. package/src/llm/quality.ts +248 -248
  115. package/src/memory/admission.ts +57 -0
  116. package/src/memory/attribution-guard.ts +249 -249
  117. package/src/memory/auto-relations.ts +21 -0
  118. package/src/memory/consolidation.ts +13 -2
  119. package/src/memory/disclosure-policy.ts +140 -135
  120. package/src/memory/entity-extractor.ts +197 -197
  121. package/src/memory/export-import.ts +11 -3
  122. package/src/memory/formation/evaluate.ts +217 -217
  123. package/src/memory/formation/extract.ts +361 -361
  124. package/src/memory/formation/index.ts +417 -417
  125. package/src/memory/formation/resolve.ts +344 -344
  126. package/src/memory/formation/types.ts +315 -315
  127. package/src/memory/freshness.ts +122 -122
  128. package/src/memory/graph-context.ts +8 -2
  129. package/src/memory/graph-scope.ts +46 -0
  130. package/src/memory/graph.ts +197 -197
  131. package/src/memory/observations.ts +162 -4
  132. package/src/memory/quality-audit.ts +2 -0
  133. package/src/memory/refs.ts +94 -94
  134. package/src/memory/retention.ts +22 -2
  135. package/src/memory/secret-filter.ts +79 -79
  136. package/src/memory/session.ts +5 -2
  137. package/src/memory/visibility.ts +80 -0
  138. package/src/multimodal/image-loader.ts +143 -143
  139. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  140. package/src/orchestrate/adapters/claude.ts +111 -111
  141. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  142. package/src/orchestrate/adapters/codex.ts +41 -41
  143. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  144. package/src/orchestrate/adapters/gemini.ts +42 -42
  145. package/src/orchestrate/adapters/index.ts +73 -73
  146. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  147. package/src/orchestrate/adapters/opencode.ts +47 -47
  148. package/src/orchestrate/adapters/spawn-helper.ts +286 -286
  149. package/src/orchestrate/adapters/types.ts +77 -77
  150. package/src/orchestrate/capability-router.ts +284 -284
  151. package/src/orchestrate/context-compact.ts +188 -188
  152. package/src/orchestrate/cost-tracker.ts +219 -219
  153. package/src/orchestrate/error-recovery.ts +191 -191
  154. package/src/orchestrate/evidence.ts +140 -140
  155. package/src/orchestrate/ledger.ts +110 -110
  156. package/src/orchestrate/memorix-bridge.ts +378 -340
  157. package/src/orchestrate/output-budget.ts +80 -80
  158. package/src/orchestrate/permission.ts +152 -152
  159. package/src/orchestrate/pipeline-trace.ts +131 -131
  160. package/src/orchestrate/prompt-builder.ts +155 -155
  161. package/src/orchestrate/ring-buffer.ts +37 -37
  162. package/src/orchestrate/task-graph.ts +389 -389
  163. package/src/orchestrate/verify-gate.ts +33 -10
  164. package/src/orchestrate/worktree.ts +232 -232
  165. package/src/project/aliases.ts +374 -374
  166. package/src/project/detector.ts +268 -268
  167. package/src/rules/adapters/claude-code.ts +99 -99
  168. package/src/rules/adapters/codex.ts +97 -97
  169. package/src/rules/adapters/copilot.ts +124 -124
  170. package/src/rules/adapters/cursor.ts +114 -114
  171. package/src/rules/adapters/kiro.ts +126 -126
  172. package/src/rules/adapters/trae.ts +56 -56
  173. package/src/rules/adapters/windsurf.ts +83 -83
  174. package/src/rules/syncer.ts +235 -235
  175. package/src/runtime/control-plane-maintenance.ts +1 -0
  176. package/src/runtime/isolated-maintenance.ts +1 -0
  177. package/src/runtime/lifecycle.ts +18 -0
  178. package/src/runtime/maintenance-jobs.ts +1 -0
  179. package/src/runtime/maintenance-runner.ts +2 -0
  180. package/src/runtime/project-maintenance.ts +89 -0
  181. package/src/sdk.ts +334 -304
  182. package/src/search/intent-detector.ts +289 -289
  183. package/src/search/query-expansion.ts +52 -52
  184. package/src/server/formation-timeout.ts +27 -27
  185. package/src/server.ts +334 -93
  186. package/src/skills/mini-skills.ts +386 -386
  187. package/src/store/chat-store.ts +119 -119
  188. package/src/store/graph-store.ts +249 -249
  189. package/src/store/mini-skill-store.ts +349 -349
  190. package/src/store/orama-store.ts +61 -6
  191. package/src/store/persistence-json.ts +212 -212
  192. package/src/store/persistence.ts +291 -291
  193. package/src/store/project-affinity.ts +195 -195
  194. package/src/store/sqlite-db.ts +23 -1
  195. package/src/store/sqlite-store.ts +12 -2
  196. package/src/team/event-bus.ts +76 -76
  197. package/src/team/file-locks.ts +173 -173
  198. package/src/team/handoff.ts +168 -161
  199. package/src/team/messages.ts +203 -203
  200. package/src/team/poll.ts +132 -132
  201. package/src/team/tasks.ts +211 -211
  202. package/src/types.ts +51 -0
  203. package/src/wiki/generator.ts +2 -0
  204. package/src/workspace/mcp-adapters/codex.ts +191 -191
  205. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  206. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  207. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  208. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  209. package/src/workspace/mcp-adapters/trae.ts +134 -134
  210. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  211. package/src/workspace/sanitizer.ts +60 -60
  212. package/src/workspace/workflow-sync.ts +131 -131
@@ -59,6 +59,7 @@ export type KnowledgeClaimOrigin = 'explicit' | 'git' | 'derived' | 'model';
59
59
  export const CLAIM_EVENT_KINDS = [
60
60
  'created',
61
61
  'evidence-added',
62
+ 'reviewed',
62
63
  'conflicted',
63
64
  'superseded',
64
65
  'requalified',
@@ -7,6 +7,7 @@ import { atomicWriteFile, withFileLock } from '../store/file-lock.js';
7
7
  import { WorkflowSyncer } from '../workspace/workflow-sync.js';
8
8
  import { getKnowledgeWorkspacePaths, resolveKnowledgeWorkspaceFile } from './workspace.js';
9
9
  import { WorkflowStore } from './workflow-store.js';
10
+ import { containsTaskKeyword } from '../codegraph/task-lens.js';
10
11
  import type {
11
12
  WorkflowAdapterPreview,
12
13
  WorkflowAdapterTarget,
@@ -414,6 +415,9 @@ const LENS_TERMS: Record<string, string[]> = {
414
415
  test: ['test', 'verify', 'smoke', '测试', '验证'],
415
416
  };
416
417
 
418
+ const GENERIC_WORKFLOW_LENSES = new Set(['review', 'test']);
419
+ const GENERIC_WORKFLOW_TERMS = new Set(['review', 'audit', 'test', 'verify', 'smoke']);
420
+
417
421
  function inferTaskLenses(value: string): string[] {
418
422
  const text = value.toLowerCase();
419
423
  return Object.entries(LENS_TERMS)
@@ -425,20 +429,28 @@ function taskScore(workflow: WorkflowSpec, task: string): { score: number; reaso
425
429
  const text = task.toLowerCase();
426
430
  let score = 0;
427
431
  const reasons: string[] = [];
432
+ const matchedLenses = new Set<string>();
433
+ const matchedTriggers = new Set<string>();
428
434
  for (const lens of workflow.taskLenses) {
429
435
  const terms = LENS_TERMS[lens.toLowerCase()] ?? [lens.toLowerCase()];
430
- if (terms.some(term => text.includes(term))) {
436
+ if (terms.some(term => containsTaskKeyword(task, term))) {
431
437
  score += 20;
432
438
  reasons.push('matches ' + lens + ' workflow');
439
+ matchedLenses.add(lens.toLowerCase());
433
440
  }
434
441
  }
435
442
  for (const trigger of workflow.triggers) {
436
443
  const term = trigger.toLowerCase().trim();
437
- if (term.length >= 2 && text.includes(term)) {
444
+ if (term.length >= 2 && containsTaskKeyword(task, term)) {
438
445
  score += 8;
439
446
  reasons.push('matches trigger "' + trigger + '"');
447
+ matchedTriggers.add(term);
440
448
  }
441
449
  }
450
+ const hasSpecificLens = workflow.taskLenses.some(lens => !GENERIC_WORKFLOW_LENSES.has(lens.toLowerCase()));
451
+ const hasSpecificMatch = [...matchedLenses].some(lens => !GENERIC_WORKFLOW_LENSES.has(lens))
452
+ || [...matchedTriggers].some(trigger => !GENERIC_WORKFLOW_TERMS.has(trigger));
453
+ if (hasSpecificLens && !hasSpecificMatch) return { score: 0, reasons: [] };
442
454
  return { score, reasons: [...new Set(reasons)] };
443
455
  }
444
456
 
@@ -475,9 +487,28 @@ function importedWorkflowSpec(input: {
475
487
  sourcePath: string;
476
488
  raw: string;
477
489
  }): WorkflowSpec {
490
+ let sourceMatter: matter.GrayMatterFile<string>;
491
+ try {
492
+ sourceMatter = matter(input.raw);
493
+ } catch (error) {
494
+ throw new Error('Malformed Windsurf workflow Markdown: ' + (error instanceof Error ? error.message : String(error)));
495
+ }
496
+ if (sourceMatter.data.id !== undefined) {
497
+ const id = requiredText(sourceMatter.data as Record<string, unknown>, 'id');
498
+ const sourcePath = 'workflows/' + slug(id) + '.md';
499
+ const canonical = parsedWorkflow(sourceMatter.data as Record<string, unknown>, sourceMatter.content, {
500
+ workspaceId: input.workspace.id,
501
+ sourcePath,
502
+ contentHash: hash(input.raw),
503
+ });
504
+ return materializeWorkflow({
505
+ ...canonical,
506
+ importedFrom: input.sourcePath.replace(/\\/g, '/'),
507
+ });
508
+ }
478
509
  const entry = new WorkflowSyncer().parseWindsurfWorkflow(input.sourceName, input.raw);
479
510
  const content = entry.content.trim() || '## Execute\n\nFollow the imported workflow.';
480
- const lenses = inferTaskLenses(entry.name + '\n' + entry.description + '\n' + content);
511
+ const lenses = inferTaskLenses(entry.name + '\n' + entry.description);
481
512
  const createdAt = now();
482
513
  const sourcePath = 'workflows/' + slug(entry.name) + '.md';
483
514
  const spec: WorkflowSpec = {
@@ -6,6 +6,14 @@ import { KnowledgeWorkspaceStore } from './workspace-store.js';
6
6
  import { loadKnowledgeWorkspace } from './workspace.js';
7
7
  import { WorkflowStore } from './workflow-store.js';
8
8
  import { selectWorkflows } from './workflows.js';
9
+ import type {
10
+ ContextCandidateFreshness,
11
+ ContextCandidateKind,
12
+ ContextDeliveryTarget,
13
+ ContextReceipt,
14
+ ContextReceiptOmission,
15
+ ContextReceiptSelection,
16
+ } from './context-assembly.js';
9
17
  import type { CodeGraphProviderQuality, ExternalCodeGraphOutline } from '../codegraph/types.js';
10
18
  import type { KnowledgeClaim, ClaimEvidenceRef } from './types.js';
11
19
  import type { KnowledgePageRecord, KnowledgeWorkspace } from './workspace-types.js';
@@ -98,6 +106,8 @@ export interface TaskWorkset {
98
106
  tokenCount: number;
99
107
  omitted: string[];
100
108
  };
109
+ /** Privacy-safe selection metadata for diagnostics, never appended to the prompt. */
110
+ receipt: ContextReceipt;
101
111
  prompt: string;
102
112
  }
103
113
 
@@ -128,6 +138,8 @@ export interface BuildTaskWorksetInput {
128
138
  };
129
139
  runtimeCautions?: WorksetCaution[];
130
140
  maxTokens?: number;
141
+ /** Delivery surface controls receipt semantics without changing prompt shape. */
142
+ deliveryTarget?: ContextDeliveryTarget;
131
143
  }
132
144
 
133
145
  function unique(values: string[]): string[] {
@@ -228,35 +240,104 @@ function appendLine(
228
240
  maxTokens: number,
229
241
  omitted: string[],
230
242
  omittedKind: string,
243
+ selected?: ContextReceiptSelection[],
244
+ receiptSelection?: ContextReceiptSelection,
231
245
  ): boolean {
232
246
  const next = lines.length ? lines.join('\n') + '\n' + candidate : candidate;
233
247
  if (countTextTokens(next) <= maxTokens) {
234
248
  lines.push(candidate);
249
+ if (receiptSelection) selected?.push(receiptSelection);
235
250
  return true;
236
251
  }
237
252
  omitted.push(omittedKind);
238
253
  return false;
239
254
  }
240
255
 
256
+ function freshnessForMemory(status: WorksetMemorySource['status']): ContextCandidateFreshness {
257
+ if (status === 'current' || status === 'suspect' || status === 'stale') return status;
258
+ return 'unknown';
259
+ }
260
+
261
+ function receiptOmissionKind(raw: string): ContextCandidateKind | undefined {
262
+ if (raw.includes('task')) return 'task';
263
+ if (raw.includes('fact')) return 'current-fact';
264
+ if (raw.includes('state')) return 'code-state';
265
+ if (raw.includes('semantic')) return 'semantic-code';
266
+ if (raw.includes('start')) return 'start-here';
267
+ if (raw.includes('memory')) return 'memory';
268
+ if (raw.includes('claim')) return 'claim';
269
+ if (raw.includes('knowledge-page')) return 'knowledge-page';
270
+ if (raw.includes('workflow')) return 'workflow';
271
+ if (raw.includes('verification')) return 'verification';
272
+ if (raw.includes('caution')) return 'caution';
273
+ return undefined;
274
+ }
275
+
276
+ function receiptOmissions(omitted: string[], hiddenCautionMemoryCount: number): ContextReceiptOmission[] {
277
+ const counts = new Map<ContextCandidateKind, number>();
278
+ for (const raw of omitted) {
279
+ const kind = receiptOmissionKind(raw);
280
+ if (!kind || raw.endsWith('-heading')) continue;
281
+ counts.set(kind, (counts.get(kind) ?? 0) + 1);
282
+ }
283
+ const receipt: ContextReceiptOmission[] = [...counts.entries()].map(([kind, count]) => ({
284
+ kind,
285
+ reason: 'token-budget',
286
+ count,
287
+ }));
288
+ if (hiddenCautionMemoryCount > 0) {
289
+ receipt.push({
290
+ kind: 'caution',
291
+ reason: 'hidden-by-task-lens',
292
+ count: hiddenCautionMemoryCount,
293
+ });
294
+ }
295
+ return receipt;
296
+ }
297
+
298
+ function scheduledActions(cautions: WorksetCaution[]): string[] {
299
+ return cautions
300
+ .filter(caution => caution.kind === 'codegraph-refresh-queued')
301
+ .map(caution => short(caution.message, 28));
302
+ }
303
+
241
304
  /**
242
305
  * Render a bounded prompt from whole evidence items. It never cuts a page,
243
306
  * workflow, or claim body into an untraceable partial fragment.
244
307
  */
245
- export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'budget'> & {
308
+ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'budget' | 'receipt'> & {
246
309
  budget?: Partial<TaskWorkset['budget']>;
247
- }): { prompt: string; tokenCount: number; omitted: string[] } {
310
+ }): {
311
+ prompt: string;
312
+ tokenCount: number;
313
+ /** Compatibility summary: unique omission categories. */
314
+ omitted: string[];
315
+ /** Internal receipt input: one entry per candidate that did not fit. */
316
+ omittedItems: string[];
317
+ selected: ContextReceiptSelection[];
318
+ } {
248
319
  const maxTokens = input.budget?.maxTokens ?? 180;
249
320
  const omitted: string[] = [];
321
+ const selected: ContextReceiptSelection[] = [];
250
322
  const lines: string[] = ['Memorix Autopilot Brief'];
251
323
  const task = short(input.task || 'Continue the current task.', 34);
252
- appendLine(lines, 'Task: ' + task, maxTokens, omitted, 'task-detail');
324
+ appendLine(lines, 'Task: ' + task, maxTokens, omitted, 'task-detail', selected, {
325
+ kind: 'task',
326
+ reason: 'current task supplied by the caller',
327
+ trust: 'source-backed',
328
+ });
253
329
  appendLine(lines, 'Task lens: ' + input.lens, maxTokens, omitted, 'lens');
254
330
 
255
331
  if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
256
332
  appendLine(lines, '', maxTokens, omitted, 'caution-heading');
257
333
  appendLine(lines, 'Cautions', maxTokens, omitted, 'caution-heading');
258
334
  for (const caution of input.cautions.slice(0, 6)) {
259
- appendLine(lines, '- ' + short(caution.message, 22), maxTokens, omitted, 'caution');
335
+ appendLine(lines, '- ' + short(caution.message, 22), maxTokens, omitted, 'caution', selected, {
336
+ kind: 'caution',
337
+ id: 'caution:' + caution.kind,
338
+ reason: 'current project caution',
339
+ trust: 'source-backed',
340
+ });
260
341
  }
261
342
  for (const memory of input.cautionMemory.slice(0, 3)) {
262
343
  const location = memory.path
@@ -269,6 +350,14 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
269
350
  maxTokens,
270
351
  omitted,
271
352
  'caution-memory',
353
+ selected,
354
+ {
355
+ kind: 'memory',
356
+ id: 'memory:' + memory.id,
357
+ reason: 'task-relevant memory requiring source verification',
358
+ freshness: freshnessForMemory(memory.status),
359
+ trust: 'historical',
360
+ },
272
361
  );
273
362
  }
274
363
  if (input.hiddenCautionMemoryCount > 0) {
@@ -280,14 +369,23 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
280
369
  appendLine(lines, '', maxTokens, omitted, 'facts-heading');
281
370
  appendLine(lines, 'Current project facts', maxTokens, omitted, 'facts-heading');
282
371
  for (const fact of input.currentFacts.slice(0, 4)) {
283
- appendLine(lines, '- ' + short(fact, 40), maxTokens, omitted, 'current-fact');
372
+ appendLine(lines, '- ' + short(fact, 40), maxTokens, omitted, 'current-fact', selected, {
373
+ kind: 'current-fact',
374
+ reason: 'current project state',
375
+ trust: 'source-backed',
376
+ });
284
377
  }
285
378
  }
286
379
 
287
380
  if (input.codeState) {
288
381
  appendLine(lines, '', maxTokens, omitted, 'state-heading');
289
382
  appendLine(lines, 'Project state', maxTokens, omitted, 'state-heading');
290
- appendLine(lines, input.codeState, maxTokens, omitted, 'code-state');
383
+ appendLine(lines, input.codeState, maxTokens, omitted, 'code-state', selected, {
384
+ kind: 'code-state',
385
+ ...(input.provenance.snapshotId ? { id: 'snapshot:' + input.provenance.snapshotId } : {}),
386
+ reason: 'latest available Code State snapshot',
387
+ trust: 'source-backed',
388
+ });
291
389
  }
292
390
 
293
391
  if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
@@ -301,6 +399,13 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
301
399
  maxTokens,
302
400
  omitted,
303
401
  'semantic-relation',
402
+ selected,
403
+ {
404
+ kind: 'semantic-code',
405
+ id: 'code:' + location,
406
+ reason: 'validated optional semantic code relation',
407
+ trust: 'derived',
408
+ },
304
409
  );
305
410
  }
306
411
  if (input.semanticCode.relations.length === 0) {
@@ -312,6 +417,13 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
312
417
  maxTokens,
313
418
  omitted,
314
419
  'semantic-entry',
420
+ selected,
421
+ {
422
+ kind: 'semantic-code',
423
+ id: 'code:' + location,
424
+ reason: 'validated optional semantic code entry point',
425
+ trust: 'derived',
426
+ },
315
427
  );
316
428
  }
317
429
  }
@@ -321,7 +433,12 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
321
433
  appendLine(lines, '', maxTokens, omitted, 'start-heading');
322
434
  appendLine(lines, 'Start here', maxTokens, omitted, 'start-heading');
323
435
  for (const source of input.startHere.slice(0, 5)) {
324
- appendLine(lines, '- ' + source, maxTokens, omitted, 'start-here');
436
+ appendLine(lines, '- ' + source, maxTokens, omitted, 'start-here', selected, {
437
+ kind: 'start-here',
438
+ id: 'path:' + source,
439
+ reason: 'task-lensed starting point',
440
+ trust: 'derived',
441
+ });
325
442
  }
326
443
  }
327
444
 
@@ -338,6 +455,14 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
338
455
  maxTokens,
339
456
  omitted,
340
457
  'reliable-memory',
458
+ selected,
459
+ {
460
+ kind: 'memory',
461
+ id: 'memory:' + memory.id,
462
+ reason: 'current code-bound memory',
463
+ freshness: freshnessForMemory(memory.status),
464
+ trust: 'historical',
465
+ },
341
466
  );
342
467
  }
343
468
  }
@@ -346,10 +471,27 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
346
471
  appendLine(lines, '', maxTokens, omitted, 'knowledge-heading');
347
472
  appendLine(lines, 'Project knowledge', maxTokens, omitted, 'knowledge-heading');
348
473
  for (const claim of input.claims.slice(0, 3)) {
349
- appendLine(lines, '- ' + claim.assertion + ' [' + claim.id + ']', maxTokens, omitted, 'claim');
474
+ appendLine(lines, '- ' + claim.assertion + ' [' + claim.id + ']', maxTokens, omitted, 'claim', selected, {
475
+ kind: 'claim',
476
+ id: 'claim:' + claim.id,
477
+ reason: 'source-qualified task match',
478
+ trust: 'source-backed',
479
+ });
350
480
  }
351
481
  for (const page of input.pages.slice(0, 2)) {
352
- appendLine(lines, '- page: ' + page.relativePath, maxTokens, omitted, 'knowledge-page');
482
+ const supportsDeliveredClaim = page.claimIds.some(claimId => selected.some(item => (
483
+ item.kind === 'claim' && item.id === 'claim:' + claimId
484
+ )));
485
+ if (!supportsDeliveredClaim) {
486
+ omitted.push('knowledge-page-dependency');
487
+ continue;
488
+ }
489
+ appendLine(lines, '- page: ' + page.relativePath, maxTokens, omitted, 'knowledge-page', selected, {
490
+ kind: 'knowledge-page',
491
+ id: 'page:' + page.id,
492
+ reason: 'approved page linked to a selected claim',
493
+ trust: 'source-backed',
494
+ });
353
495
  }
354
496
  }
355
497
 
@@ -363,6 +505,13 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
363
505
  maxTokens,
364
506
  omitted,
365
507
  'workflow',
508
+ selected,
509
+ {
510
+ kind: 'workflow',
511
+ id: 'workflow:' + workflow.id,
512
+ reason: 'task-matching project workflow',
513
+ trust: 'source-backed',
514
+ },
366
515
  );
367
516
  }
368
517
  }
@@ -371,7 +520,11 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
371
520
  appendLine(lines, '', maxTokens, omitted, 'verification-heading');
372
521
  appendLine(lines, 'Verify', maxTokens, omitted, 'verification-heading');
373
522
  for (const check of input.verification.slice(0, 4)) {
374
- appendLine(lines, '- ' + short(check, 20), maxTokens, omitted, 'verification');
523
+ appendLine(lines, '- ' + short(check, 20), maxTokens, omitted, 'verification', selected, {
524
+ kind: 'verification',
525
+ reason: 'task-lensed verification guidance',
526
+ trust: 'derived',
527
+ });
375
528
  }
376
529
  }
377
530
 
@@ -379,6 +532,8 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
379
532
  prompt: lines.join('\n'),
380
533
  tokenCount: countTextTokens(lines.join('\n')),
381
534
  omitted: unique(omitted),
535
+ omittedItems: omitted,
536
+ selected,
382
537
  };
383
538
  }
384
539
 
@@ -388,6 +543,7 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
388
543
  * context response.
389
544
  */
390
545
  export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<TaskWorkset> {
546
+ const startedAt = Date.now();
391
547
  const task = input.task?.trim() ?? '';
392
548
  const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
393
549
  const cautions = [...(input.runtimeCautions ?? []), ...snapshotCautions(input)];
@@ -503,6 +659,18 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
503
659
  ...base,
504
660
  budget: { maxTokens },
505
661
  });
662
+ const receipt: ContextReceipt = {
663
+ version: '1.2.2',
664
+ target: input.deliveryTarget ?? 'project-context',
665
+ elapsedMs: Math.max(0, Date.now() - startedAt),
666
+ budget: {
667
+ maxTokens,
668
+ tokenCount: rendered.tokenCount,
669
+ },
670
+ selected: rendered.selected,
671
+ omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
672
+ scheduledActions: scheduledActions(normalizedCautions),
673
+ };
506
674
  return {
507
675
  ...base,
508
676
  budget: {
@@ -510,6 +678,7 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
510
678
  tokenCount: rendered.tokenCount,
511
679
  omitted: rendered.omitted,
512
680
  },
681
+ receipt,
513
682
  prompt: rendered.prompt,
514
683
  };
515
684
  }