@cortexkit/opencode-magic-context 0.31.4 → 0.32.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 (56) hide show
  1. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  2. package/dist/config/project-security.d.ts +2 -0
  3. package/dist/config/project-security.d.ts.map +1 -1
  4. package/dist/config/schema/magic-context.d.ts +9 -0
  5. package/dist/config/schema/magic-context.d.ts.map +1 -1
  6. package/dist/features/magic-context/message-index-async.d.ts +2 -1
  7. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  8. package/dist/features/magic-context/storage-clone.d.ts +34 -0
  9. package/dist/features/magic-context/storage-clone.d.ts.map +1 -0
  10. package/dist/features/magic-context/storage-tags.d.ts +11 -0
  11. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  12. package/dist/features/magic-context/storage.d.ts +2 -1
  13. package/dist/features/magic-context/storage.d.ts.map +1 -1
  14. package/dist/features/magic-context/tagger.d.ts +10 -0
  15. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  16. package/dist/hooks/magic-context/apply-operations.d.ts +24 -0
  17. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  18. package/dist/hooks/magic-context/compartment-trigger.d.ts +7 -1
  19. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  20. package/dist/hooks/magic-context/decay-render.d.ts +2 -0
  21. package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
  22. package/dist/hooks/magic-context/hook.d.ts +3 -0
  23. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  24. package/dist/hooks/magic-context/inject-compartments.d.ts +7 -9
  25. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/m0-token-breakdown.d.ts +5 -4
  27. package/dist/hooks/magic-context/m0-token-breakdown.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/read-session-chunk.d.ts +2 -0
  29. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/read-session-db.d.ts +2 -2
  31. package/dist/hooks/magic-context/read-session-raw.d.ts +8 -0
  32. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/shadow-sender.d.ts +262 -0
  34. package/dist/hooks/magic-context/shadow-sender.d.ts.map +1 -0
  35. package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
  36. package/dist/hooks/magic-context/tag-messages.d.ts +8 -0
  37. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/temporal-awareness.d.ts +3 -3
  39. package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
  40. package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
  41. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +5 -0
  42. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/transform.d.ts +4 -1
  44. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  45. package/dist/index.js +1665 -140
  46. package/dist/plugin/hooks/create-session-hooks.d.ts +48 -175
  47. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  48. package/dist/shared/rpc-server.d.ts.map +1 -1
  49. package/dist/shared/tag-transcript.d.ts +18 -0
  50. package/dist/shared/tag-transcript.d.ts.map +1 -1
  51. package/dist/tools/ctx-expand/constants.d.ts +1 -1
  52. package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/shared/rpc-server.ts +8 -0
  55. package/src/shared/tag-transcript.test.ts +253 -7
  56. package/src/shared/tag-transcript.ts +422 -114
package/dist/index.js CHANGED
@@ -15406,8 +15406,11 @@ var init_magic_context = __esm(() => {
15406
15406
  provider: "local",
15407
15407
  model: DEFAULT_LOCAL_EMBEDDING_MODEL
15408
15408
  }).describe("Embedding provider configuration"),
15409
- temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
15409
+ temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add compact date ranges to compartment headings. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
15410
15410
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
15411
+ shadow_transform: exports_external.object({
15412
+ enabled: exports_external.boolean().default(false).describe("Dev flag: mirror every transform pass to the Magic Context Rust module over subc and byte-compare outputs. No behavior impact; default off.")
15413
+ }).default({ enabled: false }).describe("Developer-only shadow transform lane. When enabled, the plugin fire-and-forgets a copy of each finalized transform to the Rust module for comparison; the live output is unchanged."),
15411
15414
  todowrite: exports_external.object({
15412
15415
  enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
15413
15416
  overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
@@ -150066,6 +150069,12 @@ function parseJsonRecord(value) {
150066
150069
  return null;
150067
150070
  }
150068
150071
  }
150072
+ function isRawCompactionSummaryInfo(info) {
150073
+ if (info === null || typeof info !== "object" || Array.isArray(info))
150074
+ return false;
150075
+ const candidate = info;
150076
+ return candidate.summary === true && candidate.finish === "stop";
150077
+ }
150069
150078
  function parseJsonUnknown(value) {
150070
150079
  try {
150071
150080
  return JSON.parse(value);
@@ -150088,7 +150097,7 @@ function attachRawPartVersion(value, timeUpdated) {
150088
150097
  return value;
150089
150098
  }
150090
150099
  function readRawSessionMessagesFromDb(db, sessionId) {
150091
- const messageRows = db.prepare("SELECT id, data, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
150100
+ const messageRows = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
150092
150101
  const partRows = db.prepare("SELECT message_id, data, time_updated FROM part WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawPartRow);
150093
150102
  const partsByMessageId = new Map;
150094
150103
  for (const part of partRows) {
@@ -150096,10 +150105,7 @@ function readRawSessionMessagesFromDb(db, sessionId) {
150096
150105
  list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
150097
150106
  partsByMessageId.set(part.message_id, list);
150098
150107
  }
150099
- const filtered = messageRows.filter((row) => {
150100
- const info = parseJsonRecord(row.data);
150101
- return !(info?.summary === true && info?.finish === "stop");
150102
- });
150108
+ const filtered = messageRows.filter((row) => !isRawCompactionSummaryInfo(parseJsonRecord(row.data)));
150103
150109
  return filtered.flatMap((row, index) => {
150104
150110
  const info = parseJsonRecord(row.data);
150105
150111
  if (!info)
@@ -150110,10 +150116,21 @@ function readRawSessionMessagesFromDb(db, sessionId) {
150110
150116
  id: row.id,
150111
150117
  role,
150112
150118
  parts: partsByMessageId.get(row.id) ?? [],
150119
+ createdAt: row.time_created ?? null,
150113
150120
  version: row.time_updated ?? null
150114
150121
  };
150115
150122
  });
150116
150123
  }
150124
+ function readRawSessionMessageIdOrdinalsFromDb(db, sessionId) {
150125
+ const messageRows = db.prepare("SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
150126
+ const filtered = messageRows.filter((row) => !isRawCompactionSummaryInfo(parseJsonRecord(row.data)));
150127
+ const ordinalById = new Map;
150128
+ filtered.forEach((row, index) => {
150129
+ if (parseJsonRecord(row.data))
150130
+ ordinalById.set(row.id, index + 1);
150131
+ });
150132
+ return ordinalById;
150133
+ }
150117
150134
  function isAnchorRow(row) {
150118
150135
  return row !== null && typeof row === "object" && typeof row.time_created === "number" && typeof row.id === "string";
150119
150136
  }
@@ -150124,7 +150141,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
150124
150141
  const anchorInfo = parseJsonRecord(anchorRow.data ?? "");
150125
150142
  if (anchorInfo?.summary === true && anchorInfo?.finish === "stop")
150126
150143
  return null;
150127
- const messageRows = db.prepare(`SELECT id, data, time_updated FROM message
150144
+ const messageRows = db.prepare(`SELECT id, data, time_created, time_updated FROM message
150128
150145
  WHERE session_id = ?
150129
150146
  AND (time_created > ? OR (time_created = ? AND id >= ?))
150130
150147
  ORDER BY time_created ASC, id ASC`).all(sessionId, anchorRow.time_created, anchorRow.time_created, anchorRow.id).filter(isRawMessageRow);
@@ -150160,6 +150177,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
150160
150177
  id: row.id,
150161
150178
  role: typeof info.role === "string" ? info.role : "unknown",
150162
150179
  parts: partsByMessageId.get(row.id) ?? [],
150180
+ createdAt: row.time_created ?? null,
150163
150181
  version: row.time_updated ?? null
150164
150182
  });
150165
150183
  ord += 1;
@@ -150223,7 +150241,7 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
150223
150241
  return null;
150224
150242
  }
150225
150243
  const info = parseJsonRecord(row.data);
150226
- if (!info || info.summary === true && info.finish === "stop") {
150244
+ if (!info || isRawCompactionSummaryInfo(info)) {
150227
150245
  return null;
150228
150246
  }
150229
150247
  const ordinalRow = db.prepare(`SELECT COUNT(*) AS ordinal FROM message
@@ -150242,6 +150260,7 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
150242
150260
  id: row.id,
150243
150261
  role,
150244
150262
  parts: partRows.map((part) => attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated)),
150263
+ createdAt: row.time_created,
150245
150264
  version: row.time_updated ?? null
150246
150265
  };
150247
150266
  }
@@ -150758,6 +150777,17 @@ function primeInMemoryTailRawMessageCache(args) {
150758
150777
  activeAbsoluteCountCache?.set(sessionId, absoluteMessageCount);
150759
150778
  return true;
150760
150779
  }
150780
+ function readRawSessionMessageIdOrdinals(sessionId) {
150781
+ const provider = sessionProviders.get(sessionId);
150782
+ if (provider?.readMessageIdOrdinals)
150783
+ return provider.readMessageIdOrdinals();
150784
+ if (provider) {
150785
+ return new Map(provider.readMessages().map((message) => [message.id, message.ordinal]));
150786
+ }
150787
+ if (!openCodeDbExists())
150788
+ return new Map;
150789
+ return withReadOnlySessionDb((db) => readRawSessionMessageIdOrdinalsFromDb(db, sessionId));
150790
+ }
150761
150791
  function readRawSessionMessageById(sessionId, messageId) {
150762
150792
  const provider = sessionProviders.get(sessionId);
150763
150793
  if (provider?.readMessageById) {
@@ -153165,6 +153195,9 @@ var init_project_docs_hash = __esm(() => {
153165
153195
  docsCache = new Map;
153166
153196
  });
153167
153197
 
153198
+ // src/features/magic-context/storage-clone.ts
153199
+ var init_storage_clone = () => {};
153200
+
153168
153201
  // src/shared/stable-json.ts
153169
153202
  function stableStringify(value, seen = new WeakSet) {
153170
153203
  if (value === undefined)
@@ -157047,7 +157080,7 @@ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
157047
157080
  const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
157048
157081
  return result.changes ?? 0;
157049
157082
  }
157050
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, RECLAIM_HINT_EXCLUDED_TOOLS, RECLAIM_HINT_MIN_TOKENS = 250, RECLAIM_HINT_EXCLUDED_LIST, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
157083
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, hasPiFallbackMessageTagStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, RECLAIM_HINT_EXCLUDED_TOOLS, RECLAIM_HINT_MIN_TOKENS = 250, RECLAIM_HINT_EXCLUDED_LIST, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
157051
157084
  var init_storage_tags = __esm(() => {
157052
157085
  insertTagStatements = new WeakMap;
157053
157086
  updateTagStatusStatements = new WeakMap;
@@ -157057,6 +157090,7 @@ var init_storage_tags = __esm(() => {
157057
157090
  deleteTagsByMessageIdStatements = new WeakMap;
157058
157091
  getMaxTagNumberBySessionStatements = new WeakMap;
157059
157092
  getTagNumberByMessageIdStatements = new WeakMap;
157093
+ hasPiFallbackMessageTagStatements = new WeakMap;
157060
157094
  updateTagByteSizeStatements = new WeakMap;
157061
157095
  updateTagInputByteSizeStatements = new WeakMap;
157062
157096
  CONTENT_ID_SUFFIX = /:(?:p|file)\d+$/;
@@ -157093,6 +157127,7 @@ var init_storage = __esm(async () => {
157093
157127
  init_migrations();
157094
157128
  init_project_docs_hash();
157095
157129
  init_project_identity2();
157130
+ init_storage_clone();
157096
157131
  init_storage_m0_mutation_log();
157097
157132
  init_storage_memory_mutation_log();
157098
157133
  init_storage_meta_shared();
@@ -178822,12 +178857,26 @@ var init_decay_curve = __esm(() => {
178822
178857
  });
178823
178858
 
178824
178859
  // src/hooks/magic-context/decay-render.ts
178825
- function escapeXmlAttr2(s) {
178826
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
178827
- }
178828
178860
  function escapeXmlContent2(s) {
178829
178861
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
178830
178862
  }
178863
+ function formatDateRange(startDate, endDate) {
178864
+ if (!startDate || !endDate)
178865
+ return "";
178866
+ if (startDate === endDate)
178867
+ return startDate;
178868
+ if (startDate.slice(0, 7) === endDate.slice(0, 7))
178869
+ return `${startDate}→${endDate.slice(8)}`;
178870
+ return `${startDate}→${endDate}`;
178871
+ }
178872
+ function compartmentHeading(c) {
178873
+ const dateRange = formatDateRange(c.startDate, c.endDate);
178874
+ const dateSegment = dateRange ? ` · ${dateRange}` : "";
178875
+ return `## ${c.startMessage}-${c.endMessage}${dateSegment} · ${c.title}`;
178876
+ }
178877
+ function guardCompartmentBody(body) {
178878
+ return body.replace(/^## /gm, " ## ");
178879
+ }
178831
178880
  function isTieredRow(c) {
178832
178881
  return typeof c.p1 === "string" && c.p1.length > 0;
178833
178882
  }
@@ -178857,25 +178906,22 @@ function renderCompartmentAtTier(c, tier2) {
178857
178906
  return renderOneCompartment(c, tier2);
178858
178907
  }
178859
178908
  function renderOneCompartment(c, tier2) {
178860
- const baseAttrs = `start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr2(c.title)}"`;
178861
178909
  if (tier2 >= 5)
178862
178910
  return "";
178911
+ const heading = compartmentHeading(c);
178863
178912
  if (c.legacy === 1 || !isTieredRow(c)) {
178864
178913
  const flat = (c.content ?? "").trim();
178865
178914
  if (tier2 >= 4 || flat.length === 0)
178866
- return `<compartment ${baseAttrs} />`;
178867
- return [
178868
- `<compartment ${baseAttrs}>`,
178869
- escapeXmlContent2(legacyBodyForTier(flat, tier2)),
178870
- "</compartment>"
178871
- ].join(`
178872
- `);
178915
+ return heading;
178916
+ const body2 = guardCompartmentBody(escapeXmlContent2(legacyBodyForTier(flat, tier2)));
178917
+ return `${heading}
178918
+ ${body2}`;
178873
178919
  }
178874
178920
  const body = tierBody(c, tier2);
178875
178921
  if (body.length === 0)
178876
- return `<compartment ${baseAttrs} />`;
178877
- return [`<compartment ${baseAttrs}>`, escapeXmlContent2(body), "</compartment>"].join(`
178878
- `);
178922
+ return heading;
178923
+ return `${heading}
178924
+ ${guardCompartmentBody(escapeXmlContent2(body))}`;
178879
178925
  }
178880
178926
  function computeTiers(compartments, historyBudgetTokens) {
178881
178927
  const v2Compartments = compartments.map((c, originalIndex) => ({ c, originalIndex })).filter(({ c }) => c.legacy !== 1);
@@ -178934,6 +178980,7 @@ function extractM0Block(m0Text, tag) {
178934
178980
  const m = m0Text.match(new RegExp(`<${tag}>[\\s\\S]*?</${tag}>`));
178935
178981
  return m ? m[0] : null;
178936
178982
  }
178983
+ var DEFAULT_HISTORY_BUDGET_TOKENS = 60000;
178937
178984
  var init_decay_render = __esm(() => {
178938
178985
  init_decay_curve();
178939
178986
  init_read_session_formatting();
@@ -179057,30 +179104,7 @@ function getVisibleMemoryIds(db, sessionId) {
179057
179104
  }
179058
179105
  }
179059
179106
  function renderMemoryBlock(memories) {
179060
- const byCategory = new Map;
179061
- for (const m of memories) {
179062
- const existing = byCategory.get(m.category);
179063
- if (existing) {
179064
- existing.push(m);
179065
- } else {
179066
- byCategory.set(m.category, [m]);
179067
- }
179068
- }
179069
- const sections = [];
179070
- for (const category of CATEGORY_PRIORITY) {
179071
- const categoryMemories = byCategory.get(category);
179072
- if (!categoryMemories || categoryMemories.length === 0) {
179073
- continue;
179074
- }
179075
- sections.push(`<${category}>`, ...categoryMemories.map((m) => `- ${escapeXmlContent(m.content)}`), `</${category}>`);
179076
- }
179077
- if (sections.length === 0) {
179078
- return null;
179079
- }
179080
- return `<project-memory>
179081
- ${sections.join(`
179082
- `)}
179083
- </project-memory>`;
179107
+ return renderMemoryBlockV2(memories) || null;
179084
179108
  }
179085
179109
  function utilityTier(m) {
179086
179110
  if (m.retrievalCount > 0)
@@ -179107,14 +179131,12 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
179107
179131
  return a.id - b.id;
179108
179132
  });
179109
179133
  const result = [];
179110
- let usedTokens = 0;
179111
179134
  for (const memory of sorted) {
179112
- const memoryTokens = estimateTokens(`- ${memory.content}`) + 6;
179113
- if (usedTokens + memoryTokens > budgetTokens) {
179135
+ const candidate = [...result, memory];
179136
+ if (estimateTokens(renderMemoryBlockV2(candidate)) > budgetTokens) {
179114
179137
  break;
179115
179138
  }
179116
179139
  result.push(memory);
179117
- usedTokens += memoryTokens;
179118
179140
  }
179119
179141
  if (result.length < memories.length) {
179120
179142
  sessionLog(sessionId, `trimmed memories from ${memories.length} to ${result.length} to fit injection budget of ${budgetTokens} tokens`);
@@ -179386,11 +179408,18 @@ function memorySelectionOrder(left, right) {
179386
179408
  return left.id - right.id;
179387
179409
  }
179388
179410
  function memoryRenderOrder(left, right) {
179389
- const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[left.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
179390
- const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[right.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
179391
- const categoryDiff = aPriority - bPriority;
179392
- if (categoryDiff !== 0)
179393
- return categoryDiff;
179411
+ const leftPriority = V2_MEMORY_CATEGORIES.indexOf(left.category);
179412
+ const rightPriority = V2_MEMORY_CATEGORIES.indexOf(right.category);
179413
+ if (leftPriority >= 0 || rightPriority >= 0) {
179414
+ if (leftPriority < 0)
179415
+ return 1;
179416
+ if (rightPriority < 0)
179417
+ return -1;
179418
+ if (leftPriority !== rightPriority)
179419
+ return leftPriority - rightPriority;
179420
+ } else if (left.category !== right.category) {
179421
+ return left.category < right.category ? -1 : 1;
179422
+ }
179394
179423
  return left.id - right.id;
179395
179424
  }
179396
179425
  function cachedStatement(cache, db, sql) {
@@ -179534,13 +179563,13 @@ function mustMaterialize(args) {
179534
179563
  function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions = {}) {
179535
179564
  const selectionOrder = [...memories].sort(memorySelectionOrder);
179536
179565
  const selected = [];
179537
- let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
179566
+ const accounting = createMemoryBlockAccounting(renderOptions);
179538
179567
  for (const memory of selectionOrder) {
179539
- const memoryTokens = estimateTokens(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
179540
- if (usedTokens + memoryTokens > budgetTokens)
179568
+ const cost = accounting.candidateCost(memory);
179569
+ if (accounting.usedTokens + cost > budgetTokens)
179541
179570
  continue;
179571
+ accounting.admit(memory, cost);
179542
179572
  selected.push(memory);
179543
- usedTokens += memoryTokens;
179544
179573
  }
179545
179574
  if (selected.length < memories.length) {
179546
179575
  sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
@@ -179554,23 +179583,22 @@ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, work
179554
179583
  }
179555
179584
  const selected = [];
179556
179585
  const selectedIds = new Set;
179557
- let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
179558
- const tokenCost = (memory) => estimateTokens(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
179586
+ const accounting = createMemoryBlockAccounting(renderOptions);
179559
179587
  const trySelect = (memory) => {
179560
179588
  if (selectedIds.has(memory.id))
179561
179589
  return false;
179562
- const tokens = tokenCost(memory);
179563
- if (usedTokens + tokens > budgetTokens)
179590
+ const cost = accounting.candidateCost(memory);
179591
+ if (accounting.usedTokens + cost > budgetTokens)
179564
179592
  return false;
179565
179593
  selected.push(memory);
179566
179594
  selectedIds.add(memory.id);
179567
- usedTokens += tokens;
179595
+ accounting.admit(memory, cost);
179568
179596
  return true;
179569
179597
  };
179570
179598
  for (const memory of memories.filter((candidate) => candidate.status === "permanent").sort(memorySelectionOrder)) {
179571
179599
  trySelect(memory);
179572
179600
  }
179573
- const remainingAfterPermanent = Math.max(0, budgetTokens - usedTokens);
179601
+ const remainingAfterPermanent = Math.max(0, budgetTokens - accounting.usedTokens);
179574
179602
  const floorTokens = remainingAfterPermanent / Math.max(1, workspace.identities.length);
179575
179603
  const byIdentity = new Map;
179576
179604
  for (const memory of memories) {
@@ -179589,15 +179617,15 @@ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, work
179589
179617
  for (const memory of candidates) {
179590
179618
  if (selectedIds.has(memory.id))
179591
179619
  continue;
179592
- const tokens = tokenCost(memory);
179593
- if (memberTokens + tokens > floorTokens)
179620
+ const cost = accounting.candidateCost(memory);
179621
+ if (memberTokens + cost > floorTokens)
179594
179622
  continue;
179595
- if (usedTokens + tokens > budgetTokens)
179623
+ if (accounting.usedTokens + cost > budgetTokens)
179596
179624
  continue;
179597
179625
  selected.push(memory);
179598
179626
  selectedIds.add(memory.id);
179599
- usedTokens += tokens;
179600
- memberTokens += tokens;
179627
+ accounting.admit(memory, cost);
179628
+ memberTokens += cost;
179601
179629
  }
179602
179630
  }
179603
179631
  const remaining = memories.filter((memory) => !selectedIds.has(memory.id)).sort(memorySelectionOrder);
@@ -179642,6 +179670,29 @@ function readM0Compartments(db, sessionId) {
179642
179670
  function nullableString(value) {
179643
179671
  return typeof value === "string" ? value : null;
179644
179672
  }
179673
+ function withCompartmentDates(sessionId, compartments, temporalAwareness) {
179674
+ if (!temporalAwareness || compartments.length === 0)
179675
+ return compartments;
179676
+ const messageIds = new Set;
179677
+ for (const compartment of compartments) {
179678
+ if (compartment.startMessageId)
179679
+ messageIds.add(compartment.startMessageId);
179680
+ if (compartment.endMessageId)
179681
+ messageIds.add(compartment.endMessageId);
179682
+ }
179683
+ const times = getMessageTimesFromOpenCodeDb(sessionId, Array.from(messageIds));
179684
+ return compartments.map((compartment) => {
179685
+ const startMs = times.get(compartment.startMessageId);
179686
+ const endMs = times.get(compartment.endMessageId);
179687
+ if (startMs === undefined || endMs === undefined)
179688
+ return compartment;
179689
+ return {
179690
+ ...compartment,
179691
+ startDate: formatDate(startMs),
179692
+ endDate: formatDate(endMs)
179693
+ };
179694
+ });
179695
+ }
179645
179696
  function rowToM0Compartment(row) {
179646
179697
  return {
179647
179698
  id: Number(row.id ?? 0),
@@ -179684,17 +179735,55 @@ function readNewMemoriesForM1(db, projectPath, afterId, expiryCutoff) {
179684
179735
  ORDER BY ${MEMORY_CATEGORY_ORDER_SQL}, id ASC`).all(projectPath, afterId, expiryCutoff).filter(isMemoryRow);
179685
179736
  return rows.map((row) => ({ ...row }));
179686
179737
  }
179738
+ function createMemoryBlockAccounting(renderOptions) {
179739
+ const seenCategories = new Set;
179740
+ const categoryCost = new Map;
179741
+ return {
179742
+ usedTokens: estimateTokens(`<project-memory>
179743
+ </project-memory>`),
179744
+ candidateCost(memory) {
179745
+ const line = renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id));
179746
+ let cost = estimateTokens(`${line}
179747
+ `);
179748
+ if (!seenCategories.has(memory.category)) {
179749
+ let tags = categoryCost.get(memory.category);
179750
+ if (tags === undefined) {
179751
+ tags = estimateTokens(`<${escapeXmlAttr(memory.category)}>
179752
+ </${escapeXmlAttr(memory.category)}>
179753
+ `);
179754
+ categoryCost.set(memory.category, tags);
179755
+ }
179756
+ cost += tags;
179757
+ }
179758
+ return cost;
179759
+ },
179760
+ admit(memory, cost) {
179761
+ this.usedTokens += cost;
179762
+ seenCategories.add(memory.category);
179763
+ }
179764
+ };
179765
+ }
179687
179766
  function renderMemoryLineV2(memory, sourceName) {
179688
- const sourceAttr = sourceName ? ` source="${escapeXmlAttr(sourceName)}"` : "";
179689
- return ` <memory id="${memory.id}" category="${escapeXmlAttr(memory.category)}"${sourceAttr} importance="${memory.importance ?? 50}">${escapeXmlContent(memory.content)}</memory>`;
179767
+ const source = sourceName ? ` [${escapeXmlContent(sourceName)}]` : "";
179768
+ return `#${memory.id}${source}: ${escapeXmlContent(memory.content)}`;
179690
179769
  }
179691
179770
  function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions = {}) {
179692
179771
  if (memories.length === 0)
179693
179772
  return "";
179773
+ const ordered = [...memories].sort(memoryRenderOrder);
179694
179774
  const lines = [`<${wrapper}>`];
179695
- for (const memory of memories) {
179775
+ let openCategory;
179776
+ for (const memory of ordered) {
179777
+ if (memory.category !== openCategory) {
179778
+ if (openCategory !== undefined)
179779
+ lines.push(`</${escapeXmlAttr(openCategory)}>`);
179780
+ openCategory = memory.category;
179781
+ lines.push(`<${escapeXmlAttr(openCategory)}>`);
179782
+ }
179696
179783
  lines.push(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
179697
179784
  }
179785
+ if (openCategory !== undefined)
179786
+ lines.push(`</${escapeXmlAttr(openCategory)}>`);
179698
179787
  lines.push(`</${wrapper}>`);
179699
179788
  return lines.join(`
179700
179789
  `);
@@ -179723,7 +179812,7 @@ function renderM0(args) {
179723
179812
  const userProfile = renderUserProfileBlock(trimUserMemoriesToBudget(args.userProfileBaseline, args.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS));
179724
179813
  if (userProfile)
179725
179814
  sections.push(userProfile);
179726
- const baseBudget = args.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
179815
+ const baseBudget = args.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2;
179727
179816
  const effectiveBudget = baseBudget / Math.max(1, args.decayPressureMultiplier ?? 1);
179728
179817
  const sessionHistory = renderSessionHistoryWithDecay({
179729
179818
  compartments: args.compartments,
@@ -179813,6 +179902,7 @@ function materializeM0(options) {
179813
179902
  } catch {}
179814
179903
  throw error51;
179815
179904
  }
179905
+ compartments = withCompartmentDates(options.sessionId, compartments, options.temporalAwareness);
179816
179906
  const memoryBudget = options.memoryInjectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
179817
179907
  const memoryRenderOptions = {
179818
179908
  sourceNameByMemoryId: sourceNamesForMemories({
@@ -179830,12 +179920,12 @@ function materializeM0(options) {
179830
179920
  memories: trimmed.renderOrder,
179831
179921
  facts,
179832
179922
  memoryRenderOptions,
179833
- historyBudgetTokens: options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS,
179923
+ historyBudgetTokens: options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2,
179834
179924
  userProfileBudgetTokens: options.userProfileBudgetTokens,
179835
179925
  decayPressureMultiplier
179836
179926
  });
179837
179927
  let attempts = 0;
179838
- const budget = options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
179928
+ const budget = options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2;
179839
179929
  while (budget > 0 && historySliceTokens(m0Text) > budget * 1.05 && attempts < 3) {
179840
179930
  decayPressureMultiplier *= 1.15;
179841
179931
  m0Text = renderM0({
@@ -179995,7 +180085,7 @@ function renderM1WithMetadata(options, markers, renderedMemoryIds) {
179995
180085
  });
179996
180086
  if (memoryUpdates.block)
179997
180087
  blocks.push(memoryUpdates.block);
179998
- const newCompartments = readNewCompartments(options.db, options.sessionId, markers.maxCompartmentSeq);
180088
+ const newCompartments = withCompartmentDates(options.sessionId, readNewCompartments(options.db, options.sessionId, markers.maxCompartmentSeq), options.temporalAwareness);
179999
180089
  if (newCompartments.length > 0) {
180000
180090
  blocks.push(`<new-compartments>
180001
180091
  ${newCompartments.map((compartment) => renderCompartmentAtTier(compartment, 1)).join(`
@@ -180211,7 +180301,7 @@ function renderFreshM0NonPersisted(options) {
180211
180301
  const docs = readProjectDocsForM0(projectDirectory ?? "", options.injectDocs);
180212
180302
  snapshotMarkers.projectDocsHash = docs.canonicalHash;
180213
180303
  snapshotMarkers.materializedAt = options.state.cachedM0MaterializedAt ?? 0;
180214
- const compartments = readM0Compartments(options.db, options.sessionId);
180304
+ const compartments = withCompartmentDates(options.sessionId, readM0Compartments(options.db, options.sessionId), options.temporalAwareness);
180215
180305
  const memories = projectPath ? workspace.isWorkspaced ? getMemoriesByProjects(options.db, workspace.expandedIdentities, ["active", "permanent"], snapshotMarkers.materializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(options.db, projectPath, ["active", "permanent"], snapshotMarkers.materializedAt) : [];
180216
180306
  const userMemories = safeGetActiveUserMemories(options.db);
180217
180307
  const memoryBudget = options.memoryInjectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
@@ -180223,7 +180313,7 @@ function renderFreshM0NonPersisted(options) {
180223
180313
  })
180224
180314
  };
180225
180315
  const trimmed = workspace.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(options.sessionId, memories, memoryBudget, workspace, memoryRenderOptions) : trimMemoriesToBudgetV2(options.sessionId, memories, memoryBudget);
180226
- const budget = options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
180316
+ const budget = options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2;
180227
180317
  let decayPressureMultiplier = 1;
180228
180318
  let m0Text = renderM0({
180229
180319
  projectDocs: docs.renderedBlock,
@@ -180345,7 +180435,7 @@ function injectM0M1(options) {
180345
180435
  const M0_DRIFT_RATIO_FLOOR_TOKENS = 500;
180346
180436
  const M1_DRIFT_RATIO = 0.15;
180347
180437
  const M1_ABSOLUTE_CAP_RATIO = 0.2;
180348
- const m1AbsoluteBudget = (options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS) * M1_ABSOLUTE_CAP_RATIO;
180438
+ const m1AbsoluteBudget = (options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2) * M1_ABSOLUTE_CAP_RATIO;
180349
180439
  const m1HasContent = m1Text !== M1_EMPTY_PLACEHOLDER;
180350
180440
  const m1Tokens = m1HasContent ? estimateTokens(m1Text) : 0;
180351
180441
  const m0Tokens = estimateTokens(m0Text);
@@ -180374,7 +180464,7 @@ function injectM0M1(options) {
180374
180464
  m1Text
180375
180465
  };
180376
180466
  }
180377
- var INJECTION_CACHE_MAX = 100, injectionCache, CONSTRAINT_KEYWORDS, EMPTY_HARD_SIGNALS, MaterializeContentionError, RenderM1InvalidMarkersError, DEFAULT_HISTORY_BUDGET_TOKENS = 60000, DEFAULT_MEMORY_BUDGET_TOKENS = 8000, MEMORY_BLOCK_WRAPPER_TOKENS = 6, DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000, M0_EMPTY_BODY = "<session-history></session-history>", M1_EMPTY_PLACEHOLDER = "<session-history-since>(no new content since last materialization)</session-history-since>", EMPTY_PROJECT_DOCS, maxCompartmentSeqStatements, maxMemoryIdStatements, legacyCompartmentCountStatements, m0CompartmentStatements, newCompartmentStatements;
180467
+ var INJECTION_CACHE_MAX = 100, injectionCache, CONSTRAINT_KEYWORDS, EMPTY_HARD_SIGNALS, MaterializeContentionError, RenderM1InvalidMarkersError, DEFAULT_HISTORY_BUDGET_TOKENS2 = 60000, DEFAULT_MEMORY_BUDGET_TOKENS = 8000, DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000, M0_EMPTY_BODY = "<session-history></session-history>", M1_EMPTY_PLACEHOLDER = "<session-history-since>(no new content since last materialization)</session-history-since>", EMPTY_PROJECT_DOCS, maxCompartmentSeqStatements, maxMemoryIdStatements, legacyCompartmentCountStatements, m0CompartmentStatements, newCompartmentStatements;
180378
180468
  var init_inject_compartments = __esm(async () => {
180379
180469
  init_compartment_storage();
180380
180470
  init_constants();
@@ -186699,6 +186789,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
186699
186789
  delete projectRaw.sqlite;
186700
186790
  warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
186701
186791
  }
186792
+ if ("shadow_transform" in projectRaw) {
186793
+ delete projectRaw.shadow_transform;
186794
+ warnings.push("Ignoring shadow_transform from project config (security: this developer-only mirror lane is user-level only).");
186795
+ }
186702
186796
  const embedding = projectRaw.embedding;
186703
186797
  if (isPlainObject2(embedding)) {
186704
186798
  const removed = [];
@@ -195133,8 +195227,8 @@ function makeToolCompositeKey(ownerMsgId, callId) {
195133
195227
  return `${ownerMsgId}${TOOL_COMPOSITE_KEY_SEP}${callId}`;
195134
195228
  }
195135
195229
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
195136
- var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
195137
- var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
195230
+ var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
195231
+ var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
195138
195232
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
195139
195233
  var probeDataVersionStatements = new WeakMap;
195140
195234
  function getProbeDataVersionStatement(db) {
@@ -195158,6 +195252,14 @@ function isAssignmentRow(row) {
195158
195252
  return false;
195159
195253
  if (candidate.tool_owner_message_id !== null && typeof candidate.tool_owner_message_id !== "string")
195160
195254
  return false;
195255
+ if (typeof candidate.byte_size !== "number")
195256
+ return false;
195257
+ if (candidate.token_count !== null && typeof candidate.token_count !== "number")
195258
+ return false;
195259
+ if (typeof candidate.input_byte_size !== "number")
195260
+ return false;
195261
+ if (candidate.input_token_count !== null && typeof candidate.input_token_count !== "number")
195262
+ return false;
195161
195263
  return true;
195162
195264
  }
195163
195265
  var UPSERT_COUNTER_SQL = `
@@ -195192,6 +195294,7 @@ var MAX_TAG_ALLOC_RETRIES = 5;
195192
195294
  function createTagger() {
195193
195295
  const counters = new Map;
195194
195296
  const assignments = new Map;
195297
+ const toolAccountingBySession = new Map;
195195
195298
  const loadSignatures = new Map;
195196
195299
  function getSessionAssignments(sessionId) {
195197
195300
  let map2 = assignments.get(sessionId);
@@ -195201,6 +195304,14 @@ function createTagger() {
195201
195304
  }
195202
195305
  return map2;
195203
195306
  }
195307
+ function getSessionToolAccounting(sessionId) {
195308
+ let map2 = toolAccountingBySession.get(sessionId);
195309
+ if (!map2) {
195310
+ map2 = new Map;
195311
+ toolAccountingBySession.set(sessionId, map2);
195312
+ }
195313
+ return map2;
195314
+ }
195204
195315
  function isUniqueConstraintError2(error51) {
195205
195316
  return error51 instanceof Error && "code" in error51 && error51.code === "SQLITE_CONSTRAINT_UNIQUE";
195206
195317
  }
@@ -195254,6 +195365,14 @@ function createTagger() {
195254
195365
  }
195255
195366
  counters.set(sessionId, next);
195256
195367
  sessionAssignments.set(mapKey, next);
195368
+ if (type === "tool") {
195369
+ getSessionToolAccounting(sessionId).set(next, {
195370
+ byteSize: byteSize2,
195371
+ tokenCount: tokenCounts?.tokenCount ?? null,
195372
+ inputByteSize,
195373
+ inputTokenCount: tokenCounts?.inputTokenCount ?? null
195374
+ });
195375
+ }
195257
195376
  return next;
195258
195377
  }
195259
195378
  throw new Error(`tagger.allocateTag: failed to allocate tag for session=${sessionId} key=${mapKey} after ${MAX_TAG_ALLOC_RETRIES} retries`);
@@ -195314,6 +195433,13 @@ function createTagger() {
195314
195433
  function getToolTag(sessionId, callId, ownerMsgId) {
195315
195434
  return assignments.get(sessionId)?.get(makeToolCompositeKey(ownerMsgId, callId));
195316
195435
  }
195436
+ function getToolTagAccounting(sessionId, callId, ownerMsgId) {
195437
+ const tagNumber = getToolTag(sessionId, callId, ownerMsgId);
195438
+ return tagNumber === undefined ? undefined : toolAccountingBySession.get(sessionId)?.get(tagNumber);
195439
+ }
195440
+ function setToolTagAccounting(sessionId, tagNumber, accounting) {
195441
+ getSessionToolAccounting(sessionId).set(tagNumber, accounting);
195442
+ }
195317
195443
  function bindTag(sessionId, messageId, tagNumber) {
195318
195444
  getSessionAssignments(sessionId).set(messageId, tagNumber);
195319
195445
  }
@@ -195332,6 +195458,7 @@ function createTagger() {
195332
195458
  function resetCounter(sessionId, db) {
195333
195459
  counters.set(sessionId, 0);
195334
195460
  assignments.delete(sessionId);
195461
+ toolAccountingBySession.delete(sessionId);
195335
195462
  loadSignatures.delete(sessionId);
195336
195463
  getResetCounterStatement(db).run(sessionId, getHarness());
195337
195464
  }
@@ -195354,11 +195481,19 @@ function createTagger() {
195354
195481
  const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
195355
195482
  const sessionAssignments = getSessionAssignments(sessionId);
195356
195483
  sessionAssignments.clear();
195484
+ const sessionToolAccounting = getSessionToolAccounting(sessionId);
195485
+ sessionToolAccounting.clear();
195357
195486
  let maxTagNumber = 0;
195358
195487
  for (const assignment of assignmentRows) {
195359
195488
  if (assignment.type === "tool") {
195360
195489
  if (assignment.tool_owner_message_id !== null) {
195361
195490
  sessionAssignments.set(makeToolCompositeKey(assignment.tool_owner_message_id, assignment.message_id), assignment.tag_number);
195491
+ sessionToolAccounting.set(assignment.tag_number, {
195492
+ byteSize: assignment.byte_size,
195493
+ tokenCount: assignment.token_count,
195494
+ inputByteSize: assignment.input_byte_size,
195495
+ inputTokenCount: assignment.input_token_count
195496
+ });
195362
195497
  }
195363
195498
  } else {
195364
195499
  sessionAssignments.set(assignment.message_id, assignment.tag_number);
@@ -195378,6 +195513,7 @@ function createTagger() {
195378
195513
  function cleanup(sessionId) {
195379
195514
  counters.delete(sessionId);
195380
195515
  assignments.delete(sessionId);
195516
+ toolAccountingBySession.delete(sessionId);
195381
195517
  loadSignatures.delete(sessionId);
195382
195518
  }
195383
195519
  return {
@@ -195385,6 +195521,8 @@ function createTagger() {
195385
195521
  assignToolTag,
195386
195522
  getTag,
195387
195523
  getToolTag,
195524
+ getToolTagAccounting,
195525
+ setToolTagAccounting,
195388
195526
  bindTag,
195389
195527
  unbindTag,
195390
195528
  bindToolTag,
@@ -195639,26 +195777,28 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
195639
195777
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
195640
195778
  return { shouldFire: false };
195641
195779
  }
195642
- if (inMemoryTail) {
195780
+ const lazyInMemoryTail = typeof inMemoryTail === "function" ? inMemoryTail : undefined;
195781
+ let resolvedInMemoryTail = typeof inMemoryTail === "function" ? undefined : inMemoryTail;
195782
+ if (resolvedInMemoryTail) {
195643
195783
  try {
195644
195784
  const policyVersion = loadProtectedTailMeta(db, sessionId).protectedTailPolicyVersion;
195645
195785
  if (policyVersion < 3)
195646
- inMemoryTail = undefined;
195786
+ resolvedInMemoryTail = undefined;
195647
195787
  } catch {
195648
- inMemoryTail = undefined;
195788
+ resolvedInMemoryTail = undefined;
195649
195789
  }
195650
195790
  }
195651
- const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
195791
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : resolvedInMemoryTail ? deriveTagLoadFloor(db, sessionId, resolvedInMemoryTail.messages.map((m) => m.id)) : 0;
195652
195792
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
195653
195793
  if (usage.percentage < proactiveFloorForGate) {
195654
195794
  try {
195655
- const boundFloor = inMemoryTail ? taggerFloor : 0;
195795
+ const boundFloor = resolvedInMemoryTail || lazyInMemoryTail ? taggerFloor : 0;
195656
195796
  const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, boundFloor);
195657
195797
  if (nullCount === 0) {
195658
- const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
195798
+ const untaggedUpperBound = resolvedInMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, resolvedInMemoryTail, taggerFloor) : 0;
195659
195799
  const eligibleUpperBound = persistedBound + untaggedUpperBound;
195660
195800
  if (eligibleUpperBound < triggerBudget) {
195661
- const memorySuffix = inMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
195801
+ const memorySuffix = resolvedInMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
195662
195802
  sessionLog(sessionId, `compartment trigger: cheap-skip at ${usage.percentage.toFixed(1)}% (below proactive floor ${proactiveFloorForGate}%) — live-tail upper bound ${eligibleUpperBound}${memorySuffix} < triggerBudget ${triggerBudget}; no size trigger possible, skipped full raw read`);
195663
195803
  return { shouldFire: false };
195664
195804
  }
@@ -195667,7 +195807,16 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
195667
195807
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
195668
195808
  }
195669
195809
  }
195670
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
195810
+ if (lazyInMemoryTail) {
195811
+ try {
195812
+ const policyVersion = loadProtectedTailMeta(db, sessionId).protectedTailPolicyVersion;
195813
+ if (policyVersion >= 3)
195814
+ resolvedInMemoryTail = lazyInMemoryTail();
195815
+ } catch {
195816
+ resolvedInMemoryTail = undefined;
195817
+ }
195818
+ }
195819
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, resolvedInMemoryTail, taggerFloor);
195671
195820
  if (!tailInfo.hasNewRawHistory) {
195672
195821
  try {
195673
195822
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -195694,11 +195843,11 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
195694
195843
  }
195695
195844
  const scale = usage.percentage >= BLOCK_UNTIL_DONE_PERCENTAGE ? 0.25 : 0.5;
195696
195845
  const scaledBoundary = withRawSessionMessageCache(() => {
195697
- if (inMemoryTail) {
195846
+ if (resolvedInMemoryTail) {
195698
195847
  primeInMemoryTailRawMessageCache({
195699
195848
  sessionId,
195700
- messages: inMemoryTail.messages,
195701
- absoluteMessageCount: inMemoryTail.absoluteMessageCount
195849
+ messages: resolvedInMemoryTail.messages,
195850
+ absoluteMessageCount: resolvedInMemoryTail.absoluteMessageCount
195702
195851
  });
195703
195852
  }
195704
195853
  return resolveOpenCodeProtectedTailBoundary({
@@ -195840,9 +195989,8 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
195840
195989
  const compartments = getCompartments(db, sessionId);
195841
195990
  let historyBlockTokens = 0;
195842
195991
  for (const c of compartments) {
195843
- historyBlockTokens += estimateTokens(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${c.title}">
195992
+ historyBlockTokens += estimateTokens(`## ${c.startMessage}-${c.endMessage} · ${c.title}
195844
195993
  ${c.content}
195845
- </compartment>
195846
195994
  `);
195847
195995
  }
195848
195996
  const budgetTokens = historyBudgetPercentage && displayContextLimit > 0 ? Math.floor(displayContextLimit * (Math.min(executeThresholdPercentage, 80) / 100) * historyBudgetPercentage) : null;
@@ -196345,6 +196493,7 @@ var reconciliationScheduledSessions = new Set;
196345
196493
  var sessionLocks = new Map;
196346
196494
  var incrementalTimers = new Map;
196347
196495
  var pendingIncrementalKeys = new Set;
196496
+ var completedIncrementalKeys = new Set;
196348
196497
  function defer(fn) {
196349
196498
  const immediate = globalThis.setImmediate;
196350
196499
  if (typeof immediate === "function") {
@@ -196412,9 +196561,9 @@ function scheduleReconciliation(db, sessionId, readMessages) {
196412
196561
  });
196413
196562
  });
196414
196563
  }
196415
- function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
196564
+ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
196416
196565
  const key = `${sessionId}\x00${messageId}`;
196417
- if (incrementalTimers.has(key) || pendingIncrementalKeys.has(key)) {
196566
+ if (completedIncrementalKeys.has(key) || incrementalTimers.has(key) || pendingIncrementalKeys.has(key)) {
196418
196567
  return;
196419
196568
  }
196420
196569
  const timer = setTimeout(() => {
@@ -196422,12 +196571,12 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
196422
196571
  pendingIncrementalKeys.add(key);
196423
196572
  let attemptedOrdinal = null;
196424
196573
  runWithSessionLock(sessionId, () => {
196425
- const message = readSingleMessage(sessionId, messageId);
196426
- if (!message) {
196574
+ const message = typeof messageSource === "function" ? messageSource(sessionId, messageId) : messageSource;
196575
+ if (!message)
196427
196576
  return;
196428
- }
196429
196577
  attemptedOrdinal = message.ordinal;
196430
196578
  indexSingleMessage(db, sessionId, message);
196579
+ completedIncrementalKeys.add(key);
196431
196580
  }).catch((error51) => {
196432
196581
  markMessageIndexDirty(db, sessionId, attemptedOrdinal ?? getLastIndexedOrdinal(db, sessionId) + 1);
196433
196582
  reconciledSessions.delete(sessionId);
@@ -196441,6 +196590,11 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
196441
196590
  function scheduleClearAndReindex(db, sessionId, readMessages) {
196442
196591
  reconciledSessions.delete(sessionId);
196443
196592
  reconciliationScheduledSessions.delete(sessionId);
196593
+ const prefix = `${sessionId}\x00`;
196594
+ for (const key of completedIncrementalKeys) {
196595
+ if (key.startsWith(prefix))
196596
+ completedIncrementalKeys.delete(key);
196597
+ }
196444
196598
  defer(() => {
196445
196599
  runWithSessionLock(sessionId, () => {
196446
196600
  clearIndexedMessages(db, sessionId);
@@ -196468,6 +196622,10 @@ function clearSessionTracking(sessionId) {
196468
196622
  pendingIncrementalKeys.delete(key);
196469
196623
  }
196470
196624
  }
196625
+ for (const key of completedIncrementalKeys) {
196626
+ if (key.startsWith(prefix))
196627
+ completedIncrementalKeys.delete(key);
196628
+ }
196471
196629
  }
196472
196630
 
196473
196631
  // src/hooks/magic-context/event-handler.ts
@@ -197437,6 +197595,7 @@ function resolveCtxReduceAvailability(sessionId) {
197437
197595
  }
197438
197596
 
197439
197597
  // src/hooks/magic-context/transform.ts
197598
+ init_decay_render();
197440
197599
  init_derive_budgets();
197441
197600
 
197442
197601
  // src/hooks/magic-context/image-token-estimate.ts
@@ -197657,6 +197816,1295 @@ function replaySentinelByMessageIds(messages, ids, providerID) {
197657
197816
  missingIds.push(id);
197658
197817
  return { replayed, missingIds };
197659
197818
  }
197819
+
197820
+ // src/hooks/magic-context/shadow-sender.ts
197821
+ init_compartment_storage();
197822
+ init_storage_memory();
197823
+ init_storage_memory_mutation_log();
197824
+ init_storage_meta_persisted();
197825
+ init_workspaces();
197826
+ init_data_path();
197827
+ init_logger();
197828
+ init_temporal_awareness();
197829
+ await __promiseAll([
197830
+ init_storage(),
197831
+ init_read_session_chunk()
197832
+ ]);
197833
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
197834
+ import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync8 } from "node:fs";
197835
+ import net from "node:net";
197836
+ import { join as join19 } from "node:path";
197837
+ var DEFAULT_MODULE_ID = "magic-context";
197838
+ function getDefaultConnectionFile() {
197839
+ return join19(getDataDir(), "cortexkit", "run", "subc-connection.json");
197840
+ }
197841
+ var SUBC_HEADER_LEN = 17;
197842
+ var SUBC_PROTOCOL_VERSION = 1;
197843
+ var FRAME_REQUEST = 0;
197844
+ var FRAME_RESPONSE = 1;
197845
+ var FRAME_ERROR = 5;
197846
+ var PRIORITY_BACKGROUND_FLAGS = 2 << 1;
197847
+ var AUTH_CLIENT_DOMAIN = "subc-client-v1";
197848
+ var AUTH_SERVER_DOMAIN = "subc-server-v1";
197849
+ var NONCE_LEN = 32;
197850
+ var PROOF_LEN = 32;
197851
+ var CONNECT_BACKOFF_INITIAL_MS = 1000;
197852
+ var CONNECT_BACKOFF_MAX_MS = 30000;
197853
+ var REQUEST_TIMEOUT_MS = 5000;
197854
+ var HANDSHAKE_TIMEOUT_MS = 2000;
197855
+ var RESEED_COOLDOWN_MS = 30 * 60 * 1000;
197856
+ var RESEED_ATTEMPT_CAP = 5;
197857
+ var declaredTrimBySession = new Map;
197858
+ function emptyCounters() {
197859
+ return {
197860
+ enqueued: 0,
197861
+ dropped_oldest: 0,
197862
+ send_failures: 0,
197863
+ connection_skips: 0,
197864
+ ordinal_unresolved: 0,
197865
+ ordinal_mismatch: 0,
197866
+ state_sync_rejects: 0,
197867
+ generation_rejects: 0,
197868
+ resets_sent: 0,
197869
+ transforms_sent: 0
197870
+ };
197871
+ }
197872
+ function createSessionQueueState() {
197873
+ return {
197874
+ queue: [],
197875
+ running: false,
197876
+ initialized: false,
197877
+ shadowGeneration: 0,
197878
+ lastAckedSeq: 0,
197879
+ lastAckedWatermarks: null,
197880
+ idOrdinalMemoGeneration: 0,
197881
+ idOrdinalMemo: new Map,
197882
+ requireResetReason: "cold_start",
197883
+ blockedUntilReset: false,
197884
+ seedPassPending: false,
197885
+ skipped: false,
197886
+ reseedAttempts: 0,
197887
+ lastReseedAttemptMs: null,
197888
+ reseedAwaitingSuccess: false,
197889
+ counters: emptyCounters()
197890
+ };
197891
+ }
197892
+ function cloneJson(value) {
197893
+ return JSON.parse(JSON.stringify(value));
197894
+ }
197895
+ function stableHash(value) {
197896
+ return createHmac("sha256", "magic-context-shadow-watermark").update(value).digest("hex");
197897
+ }
197898
+ function getMessageId(message) {
197899
+ return typeof message.info.id === "string" && message.info.id.length > 0 ? message.info.id : null;
197900
+ }
197901
+ function readStringField(part, field) {
197902
+ if (!isRecord(part))
197903
+ return null;
197904
+ if (field === "text")
197905
+ return typeof part.text === "string" ? part.text : null;
197906
+ if (field === "tool_state_output") {
197907
+ return isRecord(part.state) && typeof part.state.output === "string" ? part.state.output : null;
197908
+ }
197909
+ return typeof part.content === "string" ? part.content : null;
197910
+ }
197911
+ function writeStringField(part, field, value) {
197912
+ if (!isRecord(part))
197913
+ return;
197914
+ if (field === "text") {
197915
+ part.text = value;
197916
+ return;
197917
+ }
197918
+ if (field === "tool_state_output") {
197919
+ if (isRecord(part.state))
197920
+ part.state.output = value;
197921
+ return;
197922
+ }
197923
+ part.content = value;
197924
+ }
197925
+ function removeExactSuffix(value, suffix) {
197926
+ if (!suffix || !value.endsWith(suffix))
197927
+ return null;
197928
+ return value.slice(0, value.length - suffix.length);
197929
+ }
197930
+ function denormalizeShadowOutput(args) {
197931
+ const clonedOutput = cloneJson(args.outputMessages);
197932
+ const normalizations = [];
197933
+ const messageIndex = new Map;
197934
+ args.outputMessages.forEach((message, index) => {
197935
+ messageIndex.set(message, index);
197936
+ });
197937
+ for (const target of args.normalizationTargets) {
197938
+ const msgIndex = messageIndex.get(target.message);
197939
+ if (msgIndex === undefined)
197940
+ continue;
197941
+ let partIndex = -1;
197942
+ for (let index = 0;index < target.message.parts.length; index += 1) {
197943
+ if (target.message.parts[index] === target.part) {
197944
+ partIndex = index;
197945
+ break;
197946
+ }
197947
+ }
197948
+ if (partIndex < 0)
197949
+ continue;
197950
+ const cloneMessage = clonedOutput[msgIndex];
197951
+ const clonePart = cloneMessage?.parts?.[partIndex];
197952
+ const current = readStringField(clonePart, target.field);
197953
+ if (current === null)
197954
+ continue;
197955
+ const prefix = `§${target.tagNumber}§ `;
197956
+ if (!current.startsWith(prefix))
197957
+ continue;
197958
+ writeStringField(clonePart, target.field, current.slice(prefix.length));
197959
+ normalizations.push({
197960
+ kind: "tag_prefix",
197961
+ message_id: getMessageId(target.message),
197962
+ part_index: partIndex,
197963
+ field: target.field,
197964
+ tag_number: target.tagNumber,
197965
+ removed: prefix
197966
+ });
197967
+ }
197968
+ const hintDecisions = getAutoSearchHintDecisions(args.db, args.sessionId).filter((decision) => decision.decision === "hint" && typeof decision.text === "string");
197969
+ if (hintDecisions.length > 0) {
197970
+ const hintByMessage = new Map;
197971
+ for (const decision of hintDecisions) {
197972
+ if (decision.decision === "hint")
197973
+ hintByMessage.set(decision.messageId, decision.text);
197974
+ }
197975
+ args.outputMessages.forEach((message, msgIndex) => {
197976
+ const messageId = getMessageId(message);
197977
+ if (!messageId)
197978
+ return;
197979
+ const hint = hintByMessage.get(messageId);
197980
+ if (!hint)
197981
+ return;
197982
+ const cloneMessage = clonedOutput[msgIndex];
197983
+ for (let partIndex = 0;partIndex < message.parts.length; partIndex += 1) {
197984
+ const originalPart = message.parts[partIndex];
197985
+ if (!isRecord(originalPart) || originalPart.type !== "text")
197986
+ continue;
197987
+ const clonePart = cloneMessage?.parts?.[partIndex];
197988
+ const current = readStringField(clonePart, "text");
197989
+ if (current === null)
197990
+ continue;
197991
+ const stripped = removeExactSuffix(current, hint);
197992
+ if (stripped === null)
197993
+ continue;
197994
+ writeStringField(clonePart, "text", stripped);
197995
+ normalizations.push({
197996
+ kind: "ctx_search_hint",
197997
+ message_id: messageId,
197998
+ part_index: partIndex,
197999
+ field: "text",
198000
+ removed: hint
198001
+ });
198002
+ break;
198003
+ }
198004
+ });
198005
+ }
198006
+ const tsOutput = clonedOutput.filter((message, index) => {
198007
+ if (!isRawCompactionSummaryInfo(args.outputMessages[index]?.info))
198008
+ return true;
198009
+ normalizations.push({
198010
+ kind: "summary_message",
198011
+ message_id: getMessageId(args.outputMessages[index]),
198012
+ part_index: -1,
198013
+ field: "ts_output",
198014
+ removed: JSON.stringify(message)
198015
+ });
198016
+ return false;
198017
+ });
198018
+ return { ts_output: tsOutput, normalizations };
198019
+ }
198020
+ function resolveOrdinalsForShadow(args) {
198021
+ const memo = args.memo;
198022
+ if (args.memoGeneration !== args.generation) {
198023
+ memo.clear();
198024
+ }
198025
+ const ordinalById = readRawSessionMessageIdOrdinals(args.sessionId);
198026
+ const normalizations = [];
198027
+ const visibleMessages = args.messages.filter((message) => {
198028
+ if (!isRawCompactionSummaryInfo(message.info))
198029
+ return true;
198030
+ normalizations.push({
198031
+ kind: "summary_message",
198032
+ message_id: getMessageId(message),
198033
+ part_index: -1,
198034
+ field: "input",
198035
+ removed: JSON.stringify(message)
198036
+ });
198037
+ return false;
198038
+ });
198039
+ const annotated = visibleMessages;
198040
+ const resolved = new Array(annotated.length);
198041
+ let firstUnresolvedId;
198042
+ for (let index = 0;index < annotated.length; index += 1) {
198043
+ const messageId = getMessageId(visibleMessages[index]);
198044
+ if (!messageId)
198045
+ return { ok: false, reason: "unresolved" };
198046
+ const ordinal = ordinalById.get(messageId);
198047
+ if (ordinal === undefined && firstUnresolvedId === undefined) {
198048
+ firstUnresolvedId = messageId;
198049
+ }
198050
+ resolved[index] = ordinal;
198051
+ }
198052
+ let suffixStart = annotated.length;
198053
+ while (suffixStart > 0 && resolved[suffixStart - 1] === undefined) {
198054
+ suffixStart -= 1;
198055
+ }
198056
+ for (let index = 0;index < suffixStart; index += 1) {
198057
+ if (resolved[index] === undefined) {
198058
+ return { ok: false, reason: "unresolved", messageId: firstUnresolvedId };
198059
+ }
198060
+ }
198061
+ if (suffixStart < annotated.length) {
198062
+ const base = suffixStart > 0 ? resolved[suffixStart - 1] : -1;
198063
+ for (let index = suffixStart;index < annotated.length; index += 1) {
198064
+ resolved[index] = base + (index - suffixStart) + 1;
198065
+ }
198066
+ }
198067
+ for (let index = 0;index < annotated.length; index += 1) {
198068
+ const messageId = getMessageId(visibleMessages[index]);
198069
+ const ordinal = resolved[index];
198070
+ const prior = memo.get(messageId);
198071
+ if (prior !== undefined && prior !== ordinal) {
198072
+ return { ok: false, reason: "mismatch", messageId };
198073
+ }
198074
+ memo.set(messageId, ordinal);
198075
+ annotated[index].absolute_ordinal = ordinal;
198076
+ }
198077
+ return {
198078
+ ok: true,
198079
+ annotatedInput: annotated,
198080
+ memoGeneration: args.generation,
198081
+ normalizations
198082
+ };
198083
+ }
198084
+ function ordinalForMessageId(args) {
198085
+ if (args.state.idOrdinalMemoGeneration !== args.generation) {
198086
+ args.state.idOrdinalMemo.clear();
198087
+ args.state.idOrdinalMemoGeneration = args.generation;
198088
+ }
198089
+ if (!args.raw)
198090
+ return null;
198091
+ const prior = args.state.idOrdinalMemo.get(args.messageId);
198092
+ if (prior !== undefined && prior !== args.raw.ordinal)
198093
+ return "mismatch";
198094
+ args.state.idOrdinalMemo.set(args.messageId, args.raw.ordinal);
198095
+ return args.raw.ordinal;
198096
+ }
198097
+ function flatBlockCountForRawMessage(message) {
198098
+ if (!message)
198099
+ return 1;
198100
+ let count = 0;
198101
+ for (const part of message.parts) {
198102
+ if (!isRecord(part)) {
198103
+ count += 1;
198104
+ continue;
198105
+ }
198106
+ const type = typeof part.type === "string" ? part.type : "unknown";
198107
+ switch (type) {
198108
+ case "text":
198109
+ if (part.ignored !== true)
198110
+ count += 1;
198111
+ break;
198112
+ case "reasoning":
198113
+ case "file":
198114
+ case "image":
198115
+ case "step-start":
198116
+ case "subtask":
198117
+ count += 1;
198118
+ break;
198119
+ case "tool": {
198120
+ count += 1;
198121
+ const status = isRecord(part.state) ? part.state.status : undefined;
198122
+ const hasCompletedStatus = status === "completed" || status === "error";
198123
+ const hasOutput = isRecord(part.state) ? typeof part.state.output === "string" || typeof part.state.error === "string" : typeof part.output === "string" || typeof part.error === "string";
198124
+ if (hasCompletedStatus || hasOutput)
198125
+ count += 1;
198126
+ break;
198127
+ }
198128
+ case "compaction":
198129
+ case "step-finish":
198130
+ case "snapshot":
198131
+ case "patch":
198132
+ case "agent":
198133
+ case "retry":
198134
+ break;
198135
+ default:
198136
+ count += 1;
198137
+ break;
198138
+ }
198139
+ }
198140
+ return Math.max(1, count);
198141
+ }
198142
+ function flatBlockIdForRawMessage(messageId, raw, edge) {
198143
+ const blockIndex = edge === "start" ? 0 : flatBlockCountForRawMessage(raw) - 1;
198144
+ return `${messageId}#${blockIndex}`;
198145
+ }
198146
+ function resolveDeclaredTrimForShadow(args) {
198147
+ const marker = getPersistedCompactionMarkerState(args.db, args.sessionId);
198148
+ if (!marker || marker.boundaryOrdinal < 1)
198149
+ return null;
198150
+ const targetEndMessageId = marker.targetEndMessageId ?? marker.boundaryMessageId;
198151
+ if (!targetEndMessageId)
198152
+ return null;
198153
+ const markerKey = `${marker.boundaryOrdinal}:${targetEndMessageId}`;
198154
+ const cached2 = declaredTrimBySession.get(args.sessionId);
198155
+ if (cached2?.markerKey === markerKey)
198156
+ return cached2.trim;
198157
+ const boundaryRaw = readRawSessionMessageById(args.sessionId, targetEndMessageId);
198158
+ if (!boundaryRaw)
198159
+ return null;
198160
+ const compartments = getCompartmentsByEndMessageId(args.db, args.sessionId, targetEndMessageId);
198161
+ const boundaryCompartment = compartments.find((compartment) => compartment.endMessage === marker.boundaryOrdinal);
198162
+ if (!boundaryCompartment)
198163
+ return null;
198164
+ const trim = {
198165
+ flat_boundary_id: flatBlockIdForRawMessage(targetEndMessageId, boundaryRaw, "end"),
198166
+ boundary_bare_message_id: targetEndMessageId,
198167
+ boundary_absolute_ordinal: marker.boundaryOrdinal,
198168
+ next_absolute_ordinal: marker.boundaryOrdinal + 1
198169
+ };
198170
+ declaredTrimBySession.set(args.sessionId, { markerKey, trim });
198171
+ return trim;
198172
+ }
198173
+ function clearDeclaredTrimForSession(sessionId) {
198174
+ declaredTrimBySession.delete(sessionId);
198175
+ }
198176
+ function didDeclaredTrimAdvance(before, after) {
198177
+ if (!after)
198178
+ return false;
198179
+ if (!before)
198180
+ return true;
198181
+ return before.flat_boundary_id !== after.flat_boundary_id;
198182
+ }
198183
+ function resolveShadowWorkspaceContext(db, projectPath) {
198184
+ if (!projectPath) {
198185
+ return {
198186
+ workspace: null,
198187
+ expandedIdentities: [],
198188
+ ownIdentities: [],
198189
+ shareCategories: null
198190
+ };
198191
+ }
198192
+ const identitySet = resolveWorkspaceIdentitySet(db, projectPath);
198193
+ if (identitySet.identities.length <= 1) {
198194
+ return {
198195
+ workspace: null,
198196
+ expandedIdentities: [projectPath],
198197
+ ownIdentities: [projectPath],
198198
+ shareCategories: null
198199
+ };
198200
+ }
198201
+ const expanded = expandWorkspaceIdentitySetWithAliases(db, identitySet.identities);
198202
+ const ownIdentities = expanded.expandedIdentities.filter((identity) => expanded.canonicalIdentityByStoredPath.get(identity) === projectPath);
198203
+ if (ownIdentities.length === 0)
198204
+ ownIdentities.push(projectPath);
198205
+ const shareCategories = resolveWorkspaceShareCategories(db, projectPath) ?? [];
198206
+ const members = [
198207
+ projectPath,
198208
+ ...expanded.expandedIdentities.filter((identity) => identity !== projectPath).sort((left, right) => left.localeCompare(right))
198209
+ ];
198210
+ return {
198211
+ workspace: {
198212
+ fingerprint: computeWorkspaceEpochFingerprint(db, identitySet.identities),
198213
+ members: members.map((member) => ({
198214
+ project_path: member,
198215
+ share_categories: [...shareCategories]
198216
+ }))
198217
+ },
198218
+ expandedIdentities: members,
198219
+ ownIdentities,
198220
+ shareCategories
198221
+ };
198222
+ }
198223
+ function loadWatermarks(args) {
198224
+ const sessionMeta = getOrCreateSessionMeta(args.db, args.sessionId);
198225
+ const compartmentRow = args.db.prepare("SELECT COALESCE(MAX(sequence), -1) AS max_sequence FROM compartments WHERE session_id = ?").get(args.sessionId);
198226
+ const memoryId = args.projectPath ? getMaxMemoryIdForProjects(args.db, args.workspace.expandedIdentities, args.workspace.ownIdentities, args.workspace.shareCategories) : 0;
198227
+ const m0Row = args.db.prepare("SELECT COALESCE(MAX(id), 0) AS max_id FROM m0_mutation_log WHERE session_id = ?").get(args.sessionId);
198228
+ const memoryMutationId = args.projectPath ? getMaxMemoryMutationIdForProjects(args.db, args.workspace.expandedIdentities) ?? 0 : 0;
198229
+ return {
198230
+ compartment_sequence: compartmentRow?.max_sequence ?? -1,
198231
+ memory_id: memoryId,
198232
+ m0_mutation_id: m0Row?.max_id ?? 0,
198233
+ memory_mutation_id: memoryMutationId,
198234
+ last_todo_state_hash: stableHash(sessionMeta.lastTodoState ?? "")
198235
+ };
198236
+ }
198237
+ function watermarksEqual(left, right) {
198238
+ return left !== null && left.compartment_sequence === right.compartment_sequence && left.memory_id === right.memory_id && left.m0_mutation_id === right.m0_mutation_id && left.memory_mutation_id === right.memory_mutation_id && left.last_todo_state_hash === right.last_todo_state_hash;
198239
+ }
198240
+ function serializeCompartment(args) {
198241
+ const startRaw = args.readRawById(args.compartment.startMessageId);
198242
+ const endRaw = args.readRawById(args.compartment.endMessageId);
198243
+ const startOrdinal = ordinalForMessageId({
198244
+ raw: startRaw,
198245
+ messageId: args.compartment.startMessageId,
198246
+ generation: args.state.shadowGeneration,
198247
+ state: args.state
198248
+ });
198249
+ const endOrdinal = ordinalForMessageId({
198250
+ raw: endRaw,
198251
+ messageId: args.compartment.endMessageId,
198252
+ generation: args.state.shadowGeneration,
198253
+ state: args.state
198254
+ });
198255
+ if (startOrdinal === "mismatch" || endOrdinal === "mismatch")
198256
+ return "mismatch";
198257
+ if (startOrdinal === null || endOrdinal === null)
198258
+ return null;
198259
+ const startCreatedAt = startRaw?.createdAt;
198260
+ const endCreatedAt = endRaw?.createdAt;
198261
+ const dateRange = typeof startCreatedAt === "number" && typeof endCreatedAt === "number" ? {
198262
+ start_date: formatDate(startCreatedAt),
198263
+ end_date: formatDate(endCreatedAt)
198264
+ } : {};
198265
+ return {
198266
+ sequence: args.compartment.sequence,
198267
+ start_message: startOrdinal,
198268
+ end_message: endOrdinal,
198269
+ start_message_id: flatBlockIdForRawMessage(args.compartment.startMessageId, startRaw ?? undefined, "start"),
198270
+ end_message_id: flatBlockIdForRawMessage(args.compartment.endMessageId, endRaw ?? undefined, "end"),
198271
+ ...dateRange,
198272
+ title: args.compartment.title,
198273
+ content: args.compartment.content,
198274
+ p1: args.compartment.p1,
198275
+ p2: args.compartment.p2,
198276
+ p3: args.compartment.p3,
198277
+ p4: args.compartment.p4,
198278
+ importance: args.compartment.importance,
198279
+ episode_type: args.compartment.episodeType,
198280
+ legacy: args.compartment.legacy,
198281
+ created_at: args.compartment.createdAt
198282
+ };
198283
+ }
198284
+ function buildStateSyncPayload(args) {
198285
+ const workspace = resolveShadowWorkspaceContext(args.pass.db, args.pass.projectPath);
198286
+ const currentWatermarks = loadWatermarks({
198287
+ db: args.pass.db,
198288
+ sessionId: args.pass.sessionId,
198289
+ projectPath: args.pass.projectPath,
198290
+ workspace
198291
+ });
198292
+ if (!args.force && args.state.lastAckedWatermarks && currentWatermarks.m0_mutation_id > args.state.lastAckedWatermarks.m0_mutation_id) {
198293
+ return "m0_mutation";
198294
+ }
198295
+ if (!args.force && watermarksEqual(args.state.lastAckedWatermarks, currentWatermarks)) {
198296
+ return null;
198297
+ }
198298
+ const acked = args.force ? {
198299
+ compartment_sequence: -1,
198300
+ memory_id: 0,
198301
+ m0_mutation_id: 0,
198302
+ memory_mutation_id: 0,
198303
+ last_todo_state_hash: ""
198304
+ } : args.state.lastAckedWatermarks ?? {
198305
+ compartment_sequence: -1,
198306
+ memory_id: 0,
198307
+ m0_mutation_id: 0,
198308
+ memory_mutation_id: 0,
198309
+ last_todo_state_hash: ""
198310
+ };
198311
+ const rawById = new Map;
198312
+ const readRawById = (messageId) => {
198313
+ if (!rawById.has(messageId)) {
198314
+ rawById.set(messageId, readRawSessionMessageById(args.pass.sessionId, messageId));
198315
+ }
198316
+ return rawById.get(messageId) ?? null;
198317
+ };
198318
+ const compartments = [];
198319
+ for (const compartment of getCompartments(args.pass.db, args.pass.sessionId)) {
198320
+ if (compartment.sequence <= acked.compartment_sequence)
198321
+ continue;
198322
+ const serialized = serializeCompartment({
198323
+ compartment,
198324
+ readRawById,
198325
+ state: args.state
198326
+ });
198327
+ if (serialized === "mismatch")
198328
+ return "mismatch";
198329
+ if (serialized === null)
198330
+ return "unresolved";
198331
+ compartments.push(serialized);
198332
+ }
198333
+ const allMemories = args.pass.projectPath ? workspace.workspace ? getMemoriesByProjects(args.pass.db, workspace.expandedIdentities, ["active", "permanent"], args.pass.passInputs.now_ms, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(args.pass.db, args.pass.projectPath, ["active", "permanent"], args.pass.passInputs.now_ms) : [];
198334
+ const memories = allMemories.filter((memory) => memory.id > acked.memory_id).map((memory) => ({
198335
+ id: memory.id,
198336
+ project_path: memory.projectPath,
198337
+ category: memory.category,
198338
+ content: memory.content,
198339
+ normalized_hash: memory.normalizedHash,
198340
+ importance: memory.importance,
198341
+ scope: memory.scope,
198342
+ shareable: memory.shareable,
198343
+ source_session_id: memory.sourceSessionId,
198344
+ source_type: memory.sourceType,
198345
+ seen_count: memory.seenCount,
198346
+ retrieval_count: memory.retrievalCount,
198347
+ first_seen_at: memory.firstSeenAt,
198348
+ created_at: memory.createdAt,
198349
+ updated_at: memory.updatedAt,
198350
+ last_seen_at: memory.lastSeenAt,
198351
+ last_retrieved_at: memory.lastRetrievedAt,
198352
+ status: memory.status,
198353
+ expires_at: memory.expiresAt,
198354
+ verification_status: memory.verificationStatus,
198355
+ verified_at: memory.verifiedAt,
198356
+ superseded_by_memory_id: memory.supersededByMemoryId,
198357
+ merged_from: memory.mergedFrom,
198358
+ metadata_json: memory.metadataJson
198359
+ }));
198360
+ const renderedMemoryIds = allMemories.map((memory) => memory.id);
198361
+ const memoryMutations = args.pass.projectPath ? getMemoryMutationsForRenderByProjects(args.pass.db, workspace.expandedIdentities, acked.memory_mutation_id, renderedMemoryIds).map((row) => ({
198362
+ id: row.id,
198363
+ project_path: row.projectPath,
198364
+ mutation_type: row.mutationType,
198365
+ target_memory_id: row.targetMemoryId,
198366
+ superseded_by_id: row.supersededById,
198367
+ category: row.category,
198368
+ new_content: row.newContent,
198369
+ queued_at: row.queuedAt
198370
+ })) : [];
198371
+ const sessionMeta = getOrCreateSessionMeta(args.pass.db, args.pass.sessionId);
198372
+ return {
198373
+ method: "state_sync",
198374
+ params: {
198375
+ shadow_generation: args.state.shadowGeneration,
198376
+ expected_shadow_seq: args.state.lastAckedSeq,
198377
+ seed_boundary_id: args.state.seedPassPending && compartments.length > 0 ? args.pass.declaredTrim?.flat_boundary_id ?? null : null,
198378
+ compartments,
198379
+ memories,
198380
+ memory_mutations: memoryMutations,
198381
+ workspace: workspace.workspace,
198382
+ last_todo_state: sessionMeta.lastTodoState ?? ""
198383
+ },
198384
+ watermarks: currentWatermarks
198385
+ };
198386
+ }
198387
+ function extractAckValue(response) {
198388
+ if (isRecord(response) && isRecord(response.result))
198389
+ return response.result;
198390
+ return isRecord(response) ? response : {};
198391
+ }
198392
+ function numericAck(response, keys, fallback) {
198393
+ const value = extractAckValue(response);
198394
+ for (const key of keys) {
198395
+ const candidate = value[key];
198396
+ if (typeof candidate === "number" && Number.isFinite(candidate))
198397
+ return candidate;
198398
+ }
198399
+ return fallback;
198400
+ }
198401
+ function errorCode(error51) {
198402
+ if (isRecord(error51) && typeof error51.code === "string")
198403
+ return error51.code;
198404
+ return null;
198405
+ }
198406
+ function isPeerReject(error51) {
198407
+ const code = errorCode(error51);
198408
+ if (code === "stale_generation" || code === "shadow_generation_mismatch" || code === "shadow_seq_mismatch" || code === "seq_mismatch" || code === "cas_mismatch" || code === "state_cas_reject") {
198409
+ return true;
198410
+ }
198411
+ const text = error51 instanceof Error ? error51.message : String(error51);
198412
+ return text.includes("generation") || text.includes("shadow_seq") || text.includes("seq mismatch") || text.includes("CAS");
198413
+ }
198414
+ function isSeedBoundaryReject(error51) {
198415
+ return errorCode(error51) === "shadow_seed_boundary_mismatch";
198416
+ }
198417
+ function isConnectionFailure(error51) {
198418
+ const code = errorCode(error51);
198419
+ if (code === "ENOENT" || code === "ECONNREFUSED" || code === "ECONNRESET")
198420
+ return true;
198421
+ const text = error51 instanceof Error ? error51.message : String(error51);
198422
+ return text.includes("backoff") || text.includes("connection") || text.includes("ECONN");
198423
+ }
198424
+ function toFlatWireBody(payload) {
198425
+ return { method: payload.method, ...payload.params };
198426
+ }
198427
+ function buildShadowResetBody(args) {
198428
+ return {
198429
+ method: "shadow_reset",
198430
+ params: {
198431
+ shadow_generation: args.state.shadowGeneration,
198432
+ reason: args.reason
198433
+ }
198434
+ };
198435
+ }
198436
+ function buildShadowTransformBody(args) {
198437
+ const denormalized = args.pass.shadowTsOutput !== undefined && args.pass.shadowNormalizations !== undefined ? {
198438
+ ts_output: args.pass.shadowTsOutput,
198439
+ normalizations: args.pass.shadowNormalizations
198440
+ } : denormalizeShadowOutput({
198441
+ db: args.pass.db,
198442
+ sessionId: args.pass.sessionId,
198443
+ outputMessages: args.pass.outputMessages,
198444
+ normalizationTargets: args.pass.normalizationTargets
198445
+ });
198446
+ return {
198447
+ method: "shadow_transform",
198448
+ params: {
198449
+ shadow_generation: args.state.shadowGeneration,
198450
+ seed_pass: args.state.seedPassPending,
198451
+ input: args.pass.annotatedInput,
198452
+ ts_output: denormalized.ts_output,
198453
+ normalizations: denormalized.normalizations,
198454
+ pass_inputs: args.pass.passInputs,
198455
+ ts_decision: args.pass.tsDecision,
198456
+ declared_trim: args.pass.declaredTrim
198457
+ }
198458
+ };
198459
+ }
198460
+ function createShadowSender(options = {}) {
198461
+ const transport = options.transport ?? new SubcShadowTransport;
198462
+ const now = options.now ?? Date.now;
198463
+ const reseedCooldownMs = options.reseedCooldownMs ?? RESEED_COOLDOWN_MS;
198464
+ const reseedAttemptCap = options.reseedAttemptCap ?? RESEED_ATTEMPT_CAP;
198465
+ const sessions = new Map;
198466
+ const subagentSessions = new Set;
198467
+ const getState = (sessionId) => {
198468
+ let state = sessions.get(sessionId);
198469
+ if (!state) {
198470
+ state = createSessionQueueState();
198471
+ sessions.set(sessionId, state);
198472
+ }
198473
+ return state;
198474
+ };
198475
+ const schedule = (sessionId) => {
198476
+ const state = getState(sessionId);
198477
+ if (state.running)
198478
+ return;
198479
+ state.running = true;
198480
+ runQueue(sessionId, state).finally(() => {
198481
+ state.running = false;
198482
+ if (state.queue.length > 0)
198483
+ schedule(sessionId);
198484
+ });
198485
+ };
198486
+ const pushWork = (sessionId, work) => {
198487
+ const state = getState(sessionId);
198488
+ if (work.kind === "pass") {
198489
+ state.counters.enqueued += 1;
198490
+ let dropped = 0;
198491
+ for (let index = state.queue.length - 1;index >= 0; index -= 1) {
198492
+ if (state.queue[index]?.kind !== "pass")
198493
+ continue;
198494
+ state.queue.splice(index, 1);
198495
+ dropped += 1;
198496
+ }
198497
+ if (dropped > 0) {
198498
+ state.counters.dropped_oldest += dropped;
198499
+ sessionLog(sessionId, `shadow: dropped ${dropped} stale queued pass${dropped === 1 ? "" : "es"}`);
198500
+ }
198501
+ }
198502
+ state.queue.push(work);
198503
+ schedule(sessionId);
198504
+ };
198505
+ const runQueue = async (sessionId, state) => {
198506
+ while (!state.skipped && state.queue.length > 0) {
198507
+ const item = state.queue.shift();
198508
+ if (!item)
198509
+ continue;
198510
+ if (item.kind === "reset") {
198511
+ try {
198512
+ await performReset({
198513
+ sessionId,
198514
+ state,
198515
+ reason: item.reason,
198516
+ projectRoot: item.projectRoot
198517
+ });
198518
+ } catch (error51) {
198519
+ state.counters.send_failures += 1;
198520
+ state.initialized = false;
198521
+ state.blockedUntilReset = true;
198522
+ state.requireResetReason ??= item.reason || "reset_retry";
198523
+ sessionLog(sessionId, "shadow: reset failed (ignored):", error51);
198524
+ }
198525
+ continue;
198526
+ }
198527
+ try {
198528
+ await processPass(state, item.pass);
198529
+ } catch (error51) {
198530
+ state.counters.send_failures += 1;
198531
+ if (isPeerReject(error51)) {
198532
+ const code = errorCode(error51);
198533
+ if (code?.includes("generation"))
198534
+ state.counters.generation_rejects += 1;
198535
+ else
198536
+ state.counters.state_sync_rejects += 1;
198537
+ state.requireResetReason = code ?? "peer_reject";
198538
+ state.blockedUntilReset = true;
198539
+ state.lastAckedWatermarks = null;
198540
+ } else if (isConnectionFailure(error51)) {
198541
+ state.counters.connection_skips += 1;
198542
+ state.initialized = false;
198543
+ state.requireResetReason = "route_reopen";
198544
+ }
198545
+ sessionLog(sessionId, "shadow: send failed (ignored):", error51);
198546
+ }
198547
+ }
198548
+ };
198549
+ const performReset = async (args) => {
198550
+ const projectRoot = args.projectRoot ?? process.cwd();
198551
+ const body = toFlatWireBody(buildShadowResetBody(args));
198552
+ const response = await transport.call({
198553
+ sessionId: args.sessionId,
198554
+ projectRoot,
198555
+ method: "shadow_reset",
198556
+ body
198557
+ });
198558
+ args.state.shadowGeneration = numericAck(response, ["shadow_generation", "generation"], args.state.shadowGeneration + 1);
198559
+ args.state.lastAckedSeq = numericAck(response, ["shadow_seq", "seq"], 0);
198560
+ args.state.lastAckedWatermarks = null;
198561
+ args.state.idOrdinalMemo.clear();
198562
+ args.state.idOrdinalMemoGeneration = args.state.shadowGeneration;
198563
+ clearDeclaredTrimForSession(args.sessionId);
198564
+ args.state.initialized = true;
198565
+ args.state.blockedUntilReset = false;
198566
+ args.state.requireResetReason = null;
198567
+ args.state.seedPassPending = true;
198568
+ args.state.counters.resets_sent += 1;
198569
+ sessionLog(args.sessionId, `shadow: reset acknowledged (generation=${args.state.shadowGeneration})`);
198570
+ };
198571
+ const beginReseed = (state) => {
198572
+ const attemptAt = now();
198573
+ if (state.reseedAttempts >= reseedAttemptCap)
198574
+ return false;
198575
+ if (state.lastReseedAttemptMs !== null && attemptAt - state.lastReseedAttemptMs < reseedCooldownMs) {
198576
+ return false;
198577
+ }
198578
+ state.reseedAttempts += 1;
198579
+ state.lastReseedAttemptMs = attemptAt;
198580
+ state.reseedAwaitingSuccess = true;
198581
+ return true;
198582
+ };
198583
+ const processPass = async (state, pass) => {
198584
+ if (state.skipped)
198585
+ return;
198586
+ if (state.blockedUntilReset && !state.requireResetReason) {
198587
+ state.requireResetReason = "unknown_block";
198588
+ }
198589
+ if (!state.initialized || state.requireResetReason) {
198590
+ await performReset({
198591
+ sessionId: pass.sessionId,
198592
+ state,
198593
+ reason: state.requireResetReason ?? "cold_start",
198594
+ projectRoot: pass.projectRoot
198595
+ });
198596
+ }
198597
+ if (state.skipped || state.blockedUntilReset)
198598
+ return;
198599
+ let resolved;
198600
+ try {
198601
+ resolved = resolveOrdinalsForShadow({
198602
+ sessionId: pass.sessionId,
198603
+ messages: pass.inputMessages,
198604
+ generation: state.shadowGeneration,
198605
+ memoGeneration: state.idOrdinalMemoGeneration,
198606
+ memo: state.idOrdinalMemo
198607
+ });
198608
+ } catch (error51) {
198609
+ sessionLog(pass.sessionId, "shadow: capture failed (ignored):", error51);
198610
+ return;
198611
+ }
198612
+ if (!resolved.ok) {
198613
+ if (resolved.reason === "mismatch") {
198614
+ state.counters.ordinal_mismatch += 1;
198615
+ state.requireResetReason = "ordinal_mismatch";
198616
+ state.blockedUntilReset = true;
198617
+ state.idOrdinalMemo.clear();
198618
+ await performReset({
198619
+ sessionId: pass.sessionId,
198620
+ state,
198621
+ reason: "ordinal_mismatch",
198622
+ projectRoot: pass.projectRoot
198623
+ });
198624
+ } else {
198625
+ state.counters.ordinal_unresolved += 1;
198626
+ sessionLog(pass.sessionId, `shadow: pass skipped; unresolved ordinal for ${resolved.messageId ?? "unknown"}`);
198627
+ }
198628
+ return;
198629
+ }
198630
+ state.idOrdinalMemoGeneration = resolved.memoGeneration;
198631
+ let preparedPass;
198632
+ let syncPayload;
198633
+ try {
198634
+ const declaredTrim = resolveDeclaredTrimForShadow({
198635
+ db: pass.db,
198636
+ sessionId: pass.sessionId
198637
+ });
198638
+ const denormalized = denormalizeShadowOutput({
198639
+ db: pass.db,
198640
+ sessionId: pass.sessionId,
198641
+ outputMessages: pass.outputMessages,
198642
+ normalizationTargets: pass.normalizationTargets
198643
+ });
198644
+ preparedPass = {
198645
+ ...pass,
198646
+ annotatedInput: resolved.annotatedInput,
198647
+ shadowTsOutput: denormalized.ts_output,
198648
+ shadowNormalizations: [...resolved.normalizations, ...denormalized.normalizations],
198649
+ declaredTrim,
198650
+ tsDecision: {
198651
+ ...pass.tsDecision,
198652
+ marker_state: {
198653
+ marker_message_id: declaredTrim?.boundary_bare_message_id,
198654
+ advanced_this_pass: didDeclaredTrimAdvance(pass.declaredTrimBefore, declaredTrim)
198655
+ }
198656
+ }
198657
+ };
198658
+ syncPayload = buildStateSyncPayload({
198659
+ state,
198660
+ pass: preparedPass,
198661
+ force: state.lastAckedWatermarks === null
198662
+ });
198663
+ } catch (error51) {
198664
+ sessionLog(pass.sessionId, "shadow: capture failed (ignored):", error51);
198665
+ return;
198666
+ }
198667
+ if (syncPayload === "m0_mutation") {
198668
+ await performReset({
198669
+ sessionId: pass.sessionId,
198670
+ state,
198671
+ reason: "m0_mutation",
198672
+ projectRoot: pass.projectRoot
198673
+ });
198674
+ try {
198675
+ const fullSync = buildStateSyncPayload({ state, pass: preparedPass, force: true });
198676
+ if (fullSync === "m0_mutation") {
198677
+ throw new Error("forced state sync unexpectedly requested another m0 reset");
198678
+ }
198679
+ syncPayload = fullSync;
198680
+ } catch (error51) {
198681
+ sessionLog(pass.sessionId, "shadow: capture failed (ignored):", error51);
198682
+ return;
198683
+ }
198684
+ }
198685
+ if (syncPayload === "mismatch") {
198686
+ state.counters.ordinal_mismatch += 1;
198687
+ state.requireResetReason = "ordinal_mismatch";
198688
+ await performReset({
198689
+ sessionId: pass.sessionId,
198690
+ state,
198691
+ reason: "ordinal_mismatch",
198692
+ projectRoot: pass.projectRoot
198693
+ });
198694
+ return;
198695
+ }
198696
+ if (syncPayload === "unresolved") {
198697
+ state.counters.ordinal_unresolved += 1;
198698
+ sessionLog(pass.sessionId, "shadow: state sync skipped; compartment ordinal unresolved");
198699
+ return;
198700
+ }
198701
+ if (syncPayload !== null) {
198702
+ let response2;
198703
+ try {
198704
+ response2 = await transport.call({
198705
+ sessionId: pass.sessionId,
198706
+ projectRoot: pass.projectRoot,
198707
+ method: "state_sync",
198708
+ body: toFlatWireBody(syncPayload)
198709
+ });
198710
+ } catch (error51) {
198711
+ if (isSeedBoundaryReject(error51) && beginReseed(state)) {
198712
+ await performReset({
198713
+ sessionId: pass.sessionId,
198714
+ state,
198715
+ reason: "seed_boundary_reseed",
198716
+ projectRoot: pass.projectRoot
198717
+ });
198718
+ if (!state.skipped)
198719
+ await processPass(state, pass);
198720
+ return;
198721
+ }
198722
+ throw error51;
198723
+ }
198724
+ if (state.skipped)
198725
+ return;
198726
+ state.lastAckedSeq = numericAck(response2, ["shadow_seq", "seq"], syncPayload.params.expected_shadow_seq + 1);
198727
+ state.lastAckedWatermarks = syncPayload.watermarks;
198728
+ }
198729
+ const transformBody = toFlatWireBody(buildShadowTransformBody({ pass: preparedPass, state }));
198730
+ const response = await transport.call({
198731
+ sessionId: pass.sessionId,
198732
+ projectRoot: pass.projectRoot,
198733
+ method: "shadow_transform",
198734
+ body: transformBody
198735
+ });
198736
+ if (state.skipped)
198737
+ return;
198738
+ const ack = extractAckValue(response);
198739
+ state.seedPassPending = false;
198740
+ state.counters.transforms_sent += 1;
198741
+ if (ack.divergence || ack.divergence_class || ack.hard_divergence) {
198742
+ sessionLog(pass.sessionId, "shadow: divergence report", ack);
198743
+ }
198744
+ if (ack.quarantined === true && beginReseed(state)) {
198745
+ await performReset({
198746
+ sessionId: pass.sessionId,
198747
+ state,
198748
+ reason: "quarantine_reseed",
198749
+ projectRoot: pass.projectRoot
198750
+ });
198751
+ if (!state.skipped)
198752
+ await processPass(state, pass);
198753
+ } else if (ack.quarantined !== true && state.reseedAwaitingSuccess) {
198754
+ state.reseedAttempts = 0;
198755
+ state.lastReseedAttemptMs = null;
198756
+ state.reseedAwaitingSuccess = false;
198757
+ }
198758
+ };
198759
+ return {
198760
+ enqueue(pass) {
198761
+ if (pass.isSubagent) {
198762
+ const state = sessions.get(pass.sessionId);
198763
+ if (state) {
198764
+ state.skipped = true;
198765
+ state.queue.length = 0;
198766
+ sessions.delete(pass.sessionId);
198767
+ }
198768
+ clearDeclaredTrimForSession(pass.sessionId);
198769
+ transport.closeSession?.(pass.sessionId);
198770
+ if (!subagentSessions.has(pass.sessionId)) {
198771
+ subagentSessions.add(pass.sessionId);
198772
+ sessionLog(pass.sessionId, "shadow: skipped (subagent session)");
198773
+ }
198774
+ return;
198775
+ }
198776
+ pushWork(pass.sessionId, { kind: "pass", pass });
198777
+ },
198778
+ resetSession(sessionId, reason) {
198779
+ if (subagentSessions.has(sessionId))
198780
+ return;
198781
+ const state = getState(sessionId);
198782
+ state.queue.length = 0;
198783
+ state.requireResetReason = reason;
198784
+ state.blockedUntilReset = true;
198785
+ transport.closeSession?.(sessionId);
198786
+ pushWork(sessionId, { kind: "reset", sessionId, reason });
198787
+ },
198788
+ clearSession(sessionId) {
198789
+ const state = sessions.get(sessionId);
198790
+ if (state)
198791
+ state.skipped = true;
198792
+ sessions.delete(sessionId);
198793
+ subagentSessions.delete(sessionId);
198794
+ clearDeclaredTrimForSession(sessionId);
198795
+ transport.closeSession?.(sessionId);
198796
+ },
198797
+ getStats(sessionId) {
198798
+ return { ...getState(sessionId).counters };
198799
+ }
198800
+ };
198801
+ }
198802
+
198803
+ class SubcShadowTransport {
198804
+ connectionFile;
198805
+ moduleId;
198806
+ socket = null;
198807
+ reader = null;
198808
+ nextCorr = 1;
198809
+ routes = new Map;
198810
+ pending = Promise.resolve();
198811
+ nextProbeMs = 0;
198812
+ backoffMs = CONNECT_BACKOFF_INITIAL_MS;
198813
+ requestTimeoutMs;
198814
+ constructor(connectionFile, moduleId = DEFAULT_MODULE_ID, requestTimeoutMs = REQUEST_TIMEOUT_MS) {
198815
+ this.connectionFile = connectionFile ?? getDefaultConnectionFile();
198816
+ this.moduleId = moduleId;
198817
+ this.requestTimeoutMs = requestTimeoutMs;
198818
+ }
198819
+ async call(args) {
198820
+ const run = async () => {
198821
+ const route = await this.ensureRoute(args.sessionId, args.projectRoot);
198822
+ return await this.unaryJson(route, args.body);
198823
+ };
198824
+ const next = this.pending.then(run, run);
198825
+ this.pending = next.then(() => {
198826
+ return;
198827
+ }, () => {
198828
+ return;
198829
+ });
198830
+ return next;
198831
+ }
198832
+ closeSession(sessionId) {
198833
+ this.routes.delete(sessionId);
198834
+ }
198835
+ async ensureRoute(sessionId, projectRoot) {
198836
+ const existing = this.routes.get(sessionId);
198837
+ if (existing !== undefined)
198838
+ return existing;
198839
+ await this.ensureConnected();
198840
+ const body = {
198841
+ op: "route.open",
198842
+ target: { kind: "tool_provider", module_id: this.moduleId },
198843
+ identity: {
198844
+ project_root: projectRoot,
198845
+ harness: getHarness(),
198846
+ session: `shadow:${sessionId}`
198847
+ }
198848
+ };
198849
+ const moduleId = process.env.SUBC_MODULE_ID;
198850
+ const launchNonce = process.env.SUBC_LAUNCH_NONCE;
198851
+ if (moduleId && launchNonce) {
198852
+ body.consumer_identity = { module_id: moduleId, launch_nonce: launchNonce };
198853
+ }
198854
+ const response = await this.unaryJson(0, body);
198855
+ const ack = extractAckValue(response);
198856
+ const route = ack.route_channel;
198857
+ if (typeof route !== "number")
198858
+ throw new Error("connection route.open missing route_channel");
198859
+ this.routes.set(sessionId, route);
198860
+ return route;
198861
+ }
198862
+ async ensureConnected() {
198863
+ if (this.socket && !this.socket.destroyed && this.reader)
198864
+ return;
198865
+ const now = Date.now();
198866
+ if (now < this.nextProbeMs) {
198867
+ throw new Error(`connection backoff active until ${this.nextProbeMs}`);
198868
+ }
198869
+ let candidate = null;
198870
+ try {
198871
+ const conn = readConnectionInfo(this.connectionFile);
198872
+ const endpoint = conn.endpoints[0];
198873
+ if (!endpoint)
198874
+ throw new Error("connection file has no endpoint");
198875
+ candidate = await connectTcp(endpoint.host, endpoint.port, HANDSHAKE_TIMEOUT_MS);
198876
+ const reader = new SocketReader(candidate);
198877
+ await authenticateSubcClient(candidate, reader, conn, HANDSHAKE_TIMEOUT_MS);
198878
+ const socket = candidate;
198879
+ this.socket = socket;
198880
+ this.reader = reader;
198881
+ this.routes.clear();
198882
+ this.backoffMs = CONNECT_BACKOFF_INITIAL_MS;
198883
+ this.nextProbeMs = 0;
198884
+ socket.once("close", () => {
198885
+ if (this.socket === socket)
198886
+ this.invalidateConnection(socket);
198887
+ });
198888
+ } catch (error51) {
198889
+ candidate?.destroy();
198890
+ this.invalidateConnection();
198891
+ this.nextProbeMs = Date.now() + this.backoffMs;
198892
+ this.backoffMs = Math.min(this.backoffMs * 2, CONNECT_BACKOFF_MAX_MS);
198893
+ throw error51;
198894
+ }
198895
+ }
198896
+ invalidateConnection(socket = this.socket) {
198897
+ if (socket && this.socket && socket !== this.socket)
198898
+ return;
198899
+ this.socket = null;
198900
+ this.reader = null;
198901
+ this.routes.clear();
198902
+ socket?.destroy();
198903
+ }
198904
+ async unaryJson(channel, body) {
198905
+ await this.ensureConnected();
198906
+ const socket = this.socket;
198907
+ const reader = this.reader;
198908
+ if (!socket || !reader)
198909
+ throw new Error("connection unavailable");
198910
+ const corr = this.nextCorr++;
198911
+ try {
198912
+ await writeFrame(socket, {
198913
+ type: FRAME_REQUEST,
198914
+ channel,
198915
+ corr,
198916
+ body: Buffer.from(JSON.stringify(body))
198917
+ });
198918
+ const frame = await readTerminalFor(reader, channel, corr, this.requestTimeoutMs);
198919
+ if (frame.type === FRAME_ERROR) {
198920
+ throw parseErrorBody(frame.body);
198921
+ }
198922
+ if (frame.type !== FRAME_RESPONSE) {
198923
+ throw new Error(`unexpected subc frame type ${frame.type}`);
198924
+ }
198925
+ if (frame.body.length === 0)
198926
+ return null;
198927
+ return JSON.parse(frame.body.toString("utf8"));
198928
+ } catch (error51) {
198929
+ this.invalidateConnection(socket);
198930
+ throw error51;
198931
+ }
198932
+ }
198933
+ }
198934
+ function readConnectionInfo(path10) {
198935
+ if (!existsSync21(path10))
198936
+ throw new Error(`connection file not found: ${path10}`);
198937
+ try {
198938
+ const mode = statSync8(path10).mode & 511;
198939
+ if ((mode & 63) !== 0)
198940
+ throw new Error(`connection file has insecure mode ${mode.toString(8)}`);
198941
+ } catch (error51) {
198942
+ if (error51 instanceof Error && error51.message.includes("insecure"))
198943
+ throw error51;
198944
+ }
198945
+ const parsed = JSON.parse(readFileSync13(path10, "utf8"));
198946
+ if (parsed.schema !== 1)
198947
+ throw new Error(`unsupported connection schema ${parsed.schema}`);
198948
+ if (!Array.isArray(parsed.endpoints) || parsed.endpoints.length === 0) {
198949
+ throw new Error("connection file has no endpoints");
198950
+ }
198951
+ if (!Array.isArray(parsed.key) || parsed.key.length < 32)
198952
+ throw new Error("connection key too short");
198953
+ return parsed;
198954
+ }
198955
+ function connectTcp(host, port, timeoutMs) {
198956
+ return new Promise((resolve4, reject) => {
198957
+ const socket = net.createConnection({ host, port });
198958
+ const timer = setTimeout(() => {
198959
+ socket.destroy();
198960
+ reject(new Error("connection timeout"));
198961
+ }, timeoutMs);
198962
+ socket.once("connect", () => {
198963
+ clearTimeout(timer);
198964
+ resolve4(socket);
198965
+ });
198966
+ socket.once("error", (error51) => {
198967
+ clearTimeout(timer);
198968
+ reject(error51);
198969
+ });
198970
+ });
198971
+ }
198972
+
198973
+ class SocketReader {
198974
+ chunks = [];
198975
+ buffered = 0;
198976
+ waiters = [];
198977
+ closed = false;
198978
+ constructor(socket) {
198979
+ socket.on("data", (chunk) => {
198980
+ this.chunks.push(Buffer.from(chunk));
198981
+ this.buffered += chunk.length;
198982
+ this.flushWaiters();
198983
+ });
198984
+ socket.on("close", () => {
198985
+ this.closed = true;
198986
+ this.flushWaiters();
198987
+ });
198988
+ socket.on("error", () => {
198989
+ this.closed = true;
198990
+ this.flushWaiters();
198991
+ });
198992
+ }
198993
+ async readExact(length, timeoutMs) {
198994
+ const deadline = Date.now() + timeoutMs;
198995
+ while (this.buffered < length) {
198996
+ if (this.closed)
198997
+ throw new Error("connection closed");
198998
+ const remaining = deadline - Date.now();
198999
+ if (remaining <= 0)
199000
+ throw new Error("read timeout");
199001
+ await new Promise((resolve4, reject) => {
199002
+ const timer = setTimeout(() => {
199003
+ this.waiters = this.waiters.filter((waiter) => waiter !== onReady);
199004
+ reject(new Error("read timeout"));
199005
+ }, remaining);
199006
+ const onReady = () => {
199007
+ clearTimeout(timer);
199008
+ resolve4();
199009
+ };
199010
+ this.waiters.push(onReady);
199011
+ });
199012
+ }
199013
+ const out = Buffer.allocUnsafe(length);
199014
+ let offset = 0;
199015
+ while (offset < length) {
199016
+ const first = this.chunks[0];
199017
+ const take = Math.min(first.length, length - offset);
199018
+ first.copy(out, offset, 0, take);
199019
+ offset += take;
199020
+ if (take === first.length)
199021
+ this.chunks.shift();
199022
+ else
199023
+ this.chunks[0] = first.subarray(take);
199024
+ this.buffered -= take;
199025
+ }
199026
+ return out;
199027
+ }
199028
+ flushWaiters() {
199029
+ const waiters = this.waiters.splice(0);
199030
+ for (const waiter of waiters)
199031
+ waiter();
199032
+ }
199033
+ }
199034
+ async function writeAuthMessage(socket, value) {
199035
+ const json2 = Buffer.from(JSON.stringify(value));
199036
+ const len = Buffer.allocUnsafe(4);
199037
+ len.writeUInt32LE(json2.length, 0);
199038
+ socket.write(Buffer.concat([len, json2]));
199039
+ }
199040
+ async function readAuthMessage(reader, timeoutMs) {
199041
+ const lenBytes = await reader.readExact(4, timeoutMs);
199042
+ const len = lenBytes.readUInt32LE(0);
199043
+ const body = await reader.readExact(len, timeoutMs);
199044
+ return JSON.parse(body.toString("utf8"));
199045
+ }
199046
+ function proof(key, domain2, clientNonce, serverNonce, daemonId) {
199047
+ return createHmac("sha256", key).update(domain2).update(clientNonce).update(serverNonce).update(daemonId).digest();
199048
+ }
199049
+ async function authenticateSubcClient(socket, reader, conn, timeoutMs) {
199050
+ const key = Buffer.from(conn.key);
199051
+ const clientNonce = randomBytes(NONCE_LEN);
199052
+ await writeAuthMessage(socket, { client_nonce: [...clientNonce], role: "client" });
199053
+ const serverProof = await readAuthMessage(reader, timeoutMs);
199054
+ const serverNonce = Buffer.from(serverProof.server_nonce ?? []);
199055
+ const daemonId = Buffer.from(serverProof.daemon_id ?? []);
199056
+ const serverProofBytes = Buffer.from(serverProof.server_proof ?? []);
199057
+ const expected = proof(key, AUTH_SERVER_DOMAIN, clientNonce, serverNonce, daemonId);
199058
+ if (daemonId.length !== conn.daemon_id.length || !timingSafeEqual(daemonId, Buffer.from(conn.daemon_id)) || serverProofBytes.length !== PROOF_LEN || !timingSafeEqual(serverProofBytes, expected)) {
199059
+ throw new Error("invalid subc server proof");
199060
+ }
199061
+ const clientAuth = proof(key, AUTH_CLIENT_DOMAIN, clientNonce, serverNonce, daemonId);
199062
+ await writeAuthMessage(socket, { client_auth: [...clientAuth] });
199063
+ }
199064
+ async function writeFrame(socket, frame) {
199065
+ const header = Buffer.allocUnsafe(SUBC_HEADER_LEN);
199066
+ header.writeUInt32LE(frame.body.length, 0);
199067
+ header.writeUInt8(SUBC_PROTOCOL_VERSION, 4);
199068
+ header.writeUInt8(frame.type, 5);
199069
+ header.writeUInt8(PRIORITY_BACKGROUND_FLAGS, 6);
199070
+ header.writeUInt16LE(frame.channel, 7);
199071
+ header.writeBigUInt64LE(BigInt(frame.corr), 9);
199072
+ socket.write(Buffer.concat([header, frame.body]));
199073
+ }
199074
+ async function readFrame(reader, timeoutMs) {
199075
+ const header = await reader.readExact(SUBC_HEADER_LEN, timeoutMs);
199076
+ const len = header.readUInt32LE(0);
199077
+ const version2 = header.readUInt8(4);
199078
+ if (version2 !== SUBC_PROTOCOL_VERSION)
199079
+ throw new Error(`unsupported subc frame version ${version2}`);
199080
+ const type = header.readUInt8(5);
199081
+ const channel = header.readUInt16LE(7);
199082
+ const corr = Number(header.readBigUInt64LE(9));
199083
+ const body = len > 0 ? await reader.readExact(len, timeoutMs) : Buffer.alloc(0);
199084
+ return { type, channel, corr, body };
199085
+ }
199086
+ async function readTerminalFor(reader, channel, corr, timeoutMs) {
199087
+ const deadline = Date.now() + timeoutMs;
199088
+ for (;; ) {
199089
+ const remaining = deadline - Date.now();
199090
+ if (remaining <= 0)
199091
+ throw new Error("subc request timeout");
199092
+ const frame = await readFrame(reader, remaining);
199093
+ if (frame.channel === channel && frame.corr === corr)
199094
+ return frame;
199095
+ }
199096
+ }
199097
+ function parseErrorBody(body) {
199098
+ try {
199099
+ const parsed = JSON.parse(body.toString("utf8"));
199100
+ const error51 = new Error(`${parsed.code ?? "subc_error"}: ${parsed.message ?? "unknown"}`);
199101
+ if (typeof parsed.code === "string")
199102
+ error51.code = parsed.code;
199103
+ return error51;
199104
+ } catch {
199105
+ return new Error(body.toString("utf8") || "subc_error");
199106
+ }
199107
+ }
197660
199108
  // src/hooks/magic-context/strip-content.ts
197661
199109
  var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped §\d+§\]$/;
197662
199110
  var TAG_PREFIX_PATTERN = /^§\d+§\s*/;
@@ -198817,6 +200265,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
198817
200265
  const skipPrefixInjection = options.skipPrefixInjection === true;
198818
200266
  const onToolOwnerFallbackLookup = options.onToolOwnerFallbackLookup;
198819
200267
  const targets = new Map;
200268
+ const normalizationTargets = [];
198820
200269
  const reasoningByMessage = new Map;
198821
200270
  const messageTagNumbers = new Map;
198822
200271
  const toolTagByCallId = new Map;
@@ -198937,6 +200386,12 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
198937
200386
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
198938
200387
  if (!skipPrefixInjection) {
198939
200388
  textPart.text = prependTag(tagId, textPart.text);
200389
+ normalizationTargets.push({
200390
+ tagNumber: tagId,
200391
+ message,
200392
+ part: textPart,
200393
+ field: "text"
200394
+ });
198940
200395
  }
198941
200396
  targets.set(tagId, {
198942
200397
  message,
@@ -198977,6 +200432,12 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
198977
200432
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
198978
200433
  if (!skipPrefixInjection) {
198979
200434
  toolPart.state.output = prependTag(tagId, toolPart.state.output);
200435
+ normalizationTargets.push({
200436
+ tagNumber: tagId,
200437
+ message,
200438
+ part: toolPart,
200439
+ field: "tool_state_output"
200440
+ });
198980
200441
  }
198981
200442
  toolTagByCallId.set(compositeKey, tagId);
198982
200443
  if (thinkingParts.length > 0 && !toolThinkingByCallId.has(compositeKey)) {
@@ -199054,7 +200515,8 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
199054
200515
  toolCallIndex,
199055
200516
  batch,
199056
200517
  hasRecentReduceCall,
199057
- hasRecentCommit: commitDetected
200518
+ hasRecentCommit: commitDetected,
200519
+ normalizationTargets
199058
200520
  };
199059
200521
  }
199060
200522
  // src/hooks/magic-context/transform-postprocess-phase.ts
@@ -200973,6 +202435,11 @@ function clearPendingCompactionMarkerAfterSuccessfulDrain(args) {
200973
202435
  sessionLog(args.sessionId, "compaction-marker drain: CAS-clear failed but no pending blob remains; another drain already cleared it");
200974
202436
  return "cas-lost-already-cleared";
200975
202437
  }
202438
+ function finalizeMessageRepresentation(messages, resolvedProviderID) {
202439
+ const clearedParts = modelAcceptsEmptyContent(resolvedProviderID) ? stripClearedReasoning(messages) : 0;
202440
+ const mergedReasoningParts = stripReasoningFromMergedAssistants(messages, resolvedProviderID);
202441
+ return { clearedParts, mergedReasoningParts };
202442
+ }
200976
202443
  async function runPostTransformPhase(args) {
200977
202444
  const pendingMaterializationAtPassStart = args.pendingMaterializationSessions.has(args.sessionId);
200978
202445
  const deferredMaterializationAtPassStart = args.deferredMaterializationSessions.has(args.sessionId);
@@ -201221,6 +202688,7 @@ async function runPostTransformPhase(args) {
201221
202688
  injectDocs: args.m0M1.injectDocs,
201222
202689
  memoryInjectionBudgetTokens: args.m0M1.memoryInjectionBudgetTokens,
201223
202690
  historyBudgetTokens: args.m0M1.historyBudgetTokens,
202691
+ temporalAwareness: args.m0M1.temporalAwareness,
201224
202692
  isCacheBustingPass,
201225
202693
  hardSignals: args.m0M1.hardSignals
201226
202694
  });
@@ -201451,6 +202919,10 @@ async function runPostTransformPhase(args) {
201451
202919
  const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? "explicit_flush" : null);
201452
202920
  const materialized = m0RematerializedThisPass || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
201453
202921
  const bustedThisPass = args.didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass;
202922
+ const tFinalRepresentation = performance.now();
202923
+ const finalRepresentation = finalizeMessageRepresentation(args.messages, args.resolvedProviderID);
202924
+ sessionLog(args.sessionId, `final representation: clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
202925
+ logTransformTiming(args.sessionId, "finalizeMessageRepresentation", tFinalRepresentation, `clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
201454
202926
  return {
201455
202927
  explicitMaterializedSuccessfully,
201456
202928
  deferredMaterializedSuccessfully,
@@ -201505,6 +202977,16 @@ function clearMessageTokensCache(sessionId, messageId) {
201505
202977
  cache.delete(messageId);
201506
202978
  }
201507
202979
  var recordedSessionProjectIdentity = new BoundedSessionMap(MESSAGE_TOKENS_CACHE_MAX);
202980
+ function cloneForShadow(value) {
202981
+ return JSON.parse(JSON.stringify(value));
202982
+ }
202983
+ function classifyShadowDecision(args) {
202984
+ if (args.materialized)
202985
+ return "hard";
202986
+ if (args.schedulerDecision === "execute" || args.bustedThisPass)
202987
+ return "soft";
202988
+ return "defer";
202989
+ }
201508
202990
  function deriveTaggerLoadFloor(messages, sessionId, db) {
201509
202991
  return deriveTagLoadFloor(db, sessionId, function* () {
201510
202992
  for (const message of messages)
@@ -201548,6 +203030,19 @@ function createTransform(deps) {
201548
203030
  clearOpenCodePendingTransformDecision(sessionId);
201549
203031
  logTransformTiming(sessionId, "findSessionId", startTime, `messages=${messages.length}`);
201550
203032
  const db = deps.db;
203033
+ const shadowSender = deps.shadowSender;
203034
+ let shadowCapture;
203035
+ if (shadowSender) {
203036
+ try {
203037
+ shadowCapture = {
203038
+ nowMs: Date.now(),
203039
+ inputMessages: cloneForShadow(messages),
203040
+ declaredTrimBefore: resolveDeclaredTrimForShadow({ db, sessionId })
203041
+ };
203042
+ } catch (error51) {
203043
+ sessionLog(sessionId, "shadow: capture failed (ignored):", error51);
203044
+ }
203045
+ }
201551
203046
  if (deps.client !== undefined) {
201552
203047
  scheduleReconciliation(db, sessionId, readRawSessionMessages);
201553
203048
  }
@@ -201902,6 +203397,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
201902
203397
  let targets = new Map;
201903
203398
  let reasoningByMessage = new Map;
201904
203399
  let messageTagNumbers = new Map;
203400
+ let tagNormalizationTargets = [];
201905
203401
  let batch = null;
201906
203402
  let hasRecentReduceCall = false;
201907
203403
  if (deps.experimentalTemporalAwareness) {
@@ -201925,6 +203421,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
201925
203421
  targets = result.targets;
201926
203422
  reasoningByMessage = result.reasoningByMessage;
201927
203423
  messageTagNumbers = result.messageTagNumbers;
203424
+ tagNormalizationTargets = result.normalizationTargets;
201928
203425
  batch = result.batch;
201929
203426
  hasRecentReduceCall = result.hasRecentReduceCall;
201930
203427
  const hadPriorCommitState = deps.commitSeenLastPass?.has(sessionId) ?? false;
@@ -201986,12 +203483,6 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
201986
203483
  const t4 = performance.now();
201987
203484
  const strippedClearedReasoning = canUseEmptySentinels ? stripClearedReasoning(messages) : 0;
201988
203485
  logTransformTiming(sessionId, "stripClearedReasoning", t4, `strippedParts=${strippedClearedReasoning}`);
201989
- const tMergeStrip = performance.now();
201990
- const strippedMergedReasoning = stripReasoningFromMergedAssistants(messages, resolvedProviderID);
201991
- if (strippedMergedReasoning > 0) {
201992
- sessionLog(sessionId, `stripped ${strippedMergedReasoning} reasoning parts from merged assistants (anthropic groupIntoBlocks workaround)`);
201993
- }
201994
- logTransformTiming(sessionId, "stripReasoningFromMergedAssistants", tMergeStrip, `strippedParts=${strippedMergedReasoning}`);
201995
203486
  const watermark = getMaxDroppedTagNumber(db, sessionId);
201996
203487
  const contextUsage = contextUsageEarly;
201997
203488
  const schedulerDecision = midTurnAdjustedSchedulerDecision;
@@ -202115,6 +203606,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
202115
203606
  injectDocs: deps.injectDocs,
202116
203607
  memoryInjectionBudgetTokens: deps.memoryConfig?.injectionBudgetTokens,
202117
203608
  historyBudgetTokens,
203609
+ temporalAwareness: deps.experimentalTemporalAwareness,
202118
203610
  hardSignals: m0HardSignals
202119
203611
  }
202120
203612
  });
@@ -202317,6 +203809,45 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
202317
203809
  }
202318
203810
  const elapsed = (performance.now() - startTime).toFixed(1);
202319
203811
  sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
203812
+ if (shadowSender && shadowCapture) {
203813
+ try {
203814
+ const shadowDecision = {
203815
+ class: classifyShadowDecision({
203816
+ schedulerDecision,
203817
+ materialized: postTransformResult.materialized,
203818
+ bustedThisPass: postTransformResult.bustedThisPass
203819
+ }),
203820
+ marker_state: { advanced_this_pass: false },
203821
+ materialize_reason: postTransformResult.materializeReason,
203822
+ emergency: postTransformResult.emergency
203823
+ };
203824
+ shadowSender.enqueue({
203825
+ sessionId,
203826
+ isSubagent: sessionMeta.isSubagent,
203827
+ db,
203828
+ inputMessages: shadowCapture.inputMessages,
203829
+ outputMessages: messages,
203830
+ normalizationTargets: tagNormalizationTargets,
203831
+ projectRoot: sessionDirectory ?? deps.directory ?? process.cwd(),
203832
+ projectPath: sessionProjectIdentity,
203833
+ passInputs: {
203834
+ now_ms: shadowCapture.nowMs,
203835
+ model_key: deps.getModelKey?.(sessionId) ?? hardModelKey ?? null,
203836
+ usage: {
203837
+ input_tokens: contextUsage.inputTokens,
203838
+ limit: resolvedContextLimit ?? boundaryContextLimit
203839
+ },
203840
+ effective_execute_threshold: boundaryExecuteThreshold,
203841
+ history_budget_tokens: historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS,
203842
+ cache_ttl: sessionMeta.cacheTtl
203843
+ },
203844
+ tsDecision: shadowDecision,
203845
+ declaredTrimBefore: shadowCapture.declaredTrimBefore
203846
+ });
203847
+ } catch (error51) {
203848
+ sessionLog(sessionId, "shadow: capture failed (ignored):", error51);
203849
+ }
203850
+ }
202320
203851
  deps.maybeAutoEmbedSession?.(sessionId);
202321
203852
  };
202322
203853
  }
@@ -203390,6 +204921,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
203390
204921
  var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
203391
204922
  var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
203392
204923
  var MEMORY_GUIDANCE = `Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
204924
+ Memories are grouped by category as \`#id: fact\` lines; pass the numeric id to \`ctx_memory\` actions.
203393
204925
  **Save to memory proactively**: If you spent multiple turns finding something (a file path, a DB location, a config pattern, a workaround), save it with \`ctx_memory\` so future sessions don't repeat the search. Examples:
203394
204926
  - Found a project's source code path after searching → \`ctx_memory(action="write", category="CONFIG_VALUES", content="OpenCode source is at ~/Work/OSS/opencode")\`
203395
204927
  - Discovered a non-obvious build/test command → \`ctx_memory(action="write", category="PROJECT_RULES", content="Always use scripts/release.sh for releases")\`
@@ -203403,7 +204935,7 @@ Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space
203403
204935
  Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
203404
204936
  ${CTX_NOTE_GUIDANCE}
203405
204937
  ${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
203406
- Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` summary in \`<session-history>\` — pass its \`start\`/\`end\` attributes when the summary is not enough (exact wording, values, error text).
204938
+ Use \`ctx_expand\` to recover the raw conversation behind a summary under a \`## start-end · date · title\` heading inside \`<session-history>\` — pass the heading's start/end range when the summary is not enough (exact wording, values, error text).
203407
204939
  **Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
203408
204940
  - Can't remember where a related codebase or dependency lives → \`ctx_search(query="opencode source code path")\`
203409
204941
  - Forgot a prior architectural decision or constraint → \`ctx_search(query="why did we choose SQLite over postgres")\`
@@ -203418,7 +204950,7 @@ NEVER drop assistant text messages unless they are exceptionally large. Your con
203418
204950
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
203419
204951
  var BASE_INTRO_NO_REDUCE = (memoryEnabled) => `${CTX_NOTE_GUIDANCE}
203420
204952
  ${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
203421
- Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` summary in \`<session-history>\` — pass its \`start\`/\`end\` attributes when the summary is not enough (exact wording, values, error text).
204953
+ Use \`ctx_expand\` to recover the raw conversation behind a summary under a \`## start-end · date · title\` heading inside \`<session-history>\` — pass the heading's start/end range when the summary is not enough (exact wording, values, error text).
203422
204954
  **Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
203423
204955
  - Can't remember where a related codebase or dependency lives → \`ctx_search(query="opencode source code path")\`
203424
204956
  - Forgot a prior architectural decision or constraint → \`ctx_search(query="why did we choose SQLite over postgres")\`
@@ -204004,6 +205536,7 @@ function createMagicContextHook(deps) {
204004
205536
  };
204005
205537
  const sidekickRunnable = isSidekickRunnable(deps.config);
204006
205538
  const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
205539
+ const shadowSender = deps.config.shadow_transform?.enabled === true ? createShadowSender() : undefined;
204007
205540
  const transform2 = createTransform({
204008
205541
  tagger: deps.tagger,
204009
205542
  scheduler: deps.scheduler,
@@ -204063,7 +205596,8 @@ function createMagicContextHook(deps) {
204063
205596
  enabled: true,
204064
205597
  minChars: deps.config.caveman_text_compression.min_chars ?? 500
204065
205598
  } : undefined,
204066
- maybeAutoEmbedSession
205599
+ maybeAutoEmbedSession,
205600
+ shadowSender
204067
205601
  });
204068
205602
  const eventHandler = createEventHandler2({
204069
205603
  contextUsageMap,
@@ -204090,6 +205624,7 @@ function createMagicContextHook(deps) {
204090
205624
  recompProgressBySession.delete(sessionId);
204091
205625
  internalChildSessions.delete(sessionId);
204092
205626
  channel1StateBySession.delete(sessionId);
205627
+ shadowSender?.clearSession(sessionId);
204093
205628
  clearEmbedSessionState(sessionId);
204094
205629
  }
204095
205630
  });
@@ -204269,22 +205804,9 @@ function createMagicContextHook(deps) {
204269
205804
  // src/plugin/hooks/create-session-hooks.ts
204270
205805
  function buildMagicContextHookConfig(pluginConfig) {
204271
205806
  return {
205807
+ ...pluginConfig,
204272
205808
  protected_tags: pluginConfig.protected_tags ?? DEFAULT_PROTECTED_TAGS,
204273
- cache_ttl: pluginConfig.cache_ttl,
204274
- clear_reasoning_age: pluginConfig.clear_reasoning_age,
204275
- toast_duration_ms: pluginConfig.toast_duration_ms,
204276
- execute_threshold_percentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
204277
- execute_threshold_tokens: pluginConfig.execute_threshold_tokens,
204278
- historian: pluginConfig.historian,
204279
- history_budget_percentage: pluginConfig.history_budget_percentage,
204280
- historian_timeout_ms: pluginConfig.historian_timeout_ms,
204281
- memory: pluginConfig.memory,
204282
- sidekick: pluginConfig.sidekick,
204283
- dreamer: pluginConfig.dreamer,
204284
- commit_cluster_trigger: pluginConfig.commit_cluster_trigger,
204285
- system_prompt_injection: pluginConfig.system_prompt_injection,
204286
- temporal_awareness: pluginConfig.temporal_awareness,
204287
- caveman_text_compression: pluginConfig.caveman_text_compression
205809
+ execute_threshold_percentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE
204288
205810
  };
204289
205811
  }
204290
205812
  function createSessionHooks(args) {
@@ -204507,9 +206029,8 @@ function computeM0BlockTokens(db, sessionId, args) {
204507
206029
  try {
204508
206030
  const compRows = db.prepare("SELECT content, title, start_message, end_message FROM compartments WHERE session_id = ?").all(sessionId);
204509
206031
  for (const c of compRows) {
204510
- compartmentTokens += estimateTokens(`<compartment start="${c.start_message}" end="${c.end_message}" title="${c.title}">
206032
+ compartmentTokens += estimateTokens(`## ${c.start_message}-${c.end_message} · ${c.title}
204511
206033
  ${c.content}
204512
- </compartment>
204513
206034
  `);
204514
206035
  }
204515
206036
  } catch {}
@@ -205260,9 +206781,9 @@ import { tool } from "@opencode-ai/plugin";
205260
206781
  // src/tools/ctx-expand/constants.ts
205261
206782
  var CTX_EXPAND_DESCRIPTION = `Recover the original conversation from your compacted history.
205262
206783
 
205263
- Older parts of this session are summarized into <compartment> blocks inside <session-history> — e.g. <compartment start="120" end="245" title="Fixed tagger collision">. Each one replaces the raw messages in that ordinal range with a summary. When the summary isn't enough — you need exact wording, a specific value, an error message, or the reasoning behind a decision — expand the range:
206784
+ Older parts of this session are summarized under \`## start-end · date · title\` headings inside <session-history> — e.g. \`## 120-245 · … · Fixed tagger collision\`. Each heading replaces the raw messages in that ordinal range with a summary. When the summary isn't enough — you need exact wording, a specific value, an error message, or the reasoning behind a decision — expand the range:
205264
206785
 
205265
- ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
206786
+ ctx_expand(start=120, end=245) ← the heading's start/end range
205266
206787
 
205267
206788
  Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
205268
206789
 
@@ -206652,12 +208173,12 @@ init_models_dev_cache();
206652
208173
  // src/shared/rpc-server.ts
206653
208174
  init_logger();
206654
208175
  init_rpc_notifications();
206655
- import { randomBytes, timingSafeEqual } from "node:crypto";
208176
+ import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
206656
208177
  import {
206657
208178
  chmodSync as chmodSync3,
206658
208179
  mkdirSync as mkdirSync11,
206659
208180
  readdirSync,
206660
- readFileSync as readFileSync14,
208181
+ readFileSync as readFileSync15,
206661
208182
  renameSync as renameSync5,
206662
208183
  rmSync as rmSync3,
206663
208184
  unlinkSync as unlinkSync4,
@@ -206667,17 +208188,17 @@ import { dirname as dirname9 } from "node:path";
206667
208188
 
206668
208189
  // src/shared/rpc-utils.ts
206669
208190
  import { createHash as createHash14 } from "node:crypto";
206670
- import { join as join20 } from "node:path";
208191
+ import { join as join21 } from "node:path";
206671
208192
  function projectHash(directory) {
206672
208193
  const normalized = directory.replace(/\/+$/, "");
206673
208194
  return createHash14("sha256").update(normalized).digest("hex").slice(0, 16);
206674
208195
  }
206675
208196
  function rpcPortDir(storageDir, directory) {
206676
- return join20(storageDir, "rpc", projectHash(directory));
208197
+ return join21(storageDir, "rpc", projectHash(directory));
206677
208198
  }
206678
208199
  function rpcPortFilePath(storageDir, directory, pid = process.pid, instanceId) {
206679
208200
  const suffix = instanceId ? `-${instanceId}` : "";
206680
- return join20(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
208201
+ return join21(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
206681
208202
  }
206682
208203
  function isPidAlive(pid) {
206683
208204
  if (!Number.isInteger(pid) || pid <= 0)
@@ -206730,7 +208251,7 @@ function tokensMatch(presented, expected) {
206730
208251
  const b = Buffer.from(expected, "utf8");
206731
208252
  if (a.length !== b.length)
206732
208253
  return false;
206733
- return timingSafeEqual(a, b);
208254
+ return timingSafeEqual2(a, b);
206734
208255
  }
206735
208256
  function bearerToken(req) {
206736
208257
  const auth = req.headers.get("authorization");
@@ -206747,9 +208268,9 @@ class MagicContextRpcServer {
206747
208268
  portFilePath;
206748
208269
  portDir;
206749
208270
  startedAt = Date.now();
206750
- instanceId = randomBytes(8).toString("hex");
208271
+ instanceId = randomBytes2(8).toString("hex");
206751
208272
  sockets = new Set;
206752
- token = randomBytes(32).toString("hex");
208273
+ token = randomBytes2(32).toString("hex");
206753
208274
  constructor(storageDir, directory) {
206754
208275
  this.portFilePath = rpcPortFilePath(storageDir, directory, process.pid, this.instanceId);
206755
208276
  this.portDir = rpcPortDir(storageDir, directory);
@@ -206758,6 +208279,10 @@ class MagicContextRpcServer {
206758
208279
  this.handlers.set(method, handler);
206759
208280
  }
206760
208281
  async start() {
208282
+ if (typeof Bun === "undefined") {
208283
+ log("rpc server skipped: Bun runtime not available (no TUI consumer)");
208284
+ return 0;
208285
+ }
206761
208286
  this.startedAt = Date.now();
206762
208287
  const self2 = this;
206763
208288
  const server = Bun.serve({
@@ -206837,7 +208362,7 @@ class MagicContextRpcServer {
206837
208362
  for (const entry of readdirSync(this.portDir)) {
206838
208363
  if (!entry.startsWith("port-") || !entry.endsWith(".json"))
206839
208364
  continue;
206840
- const record2 = parseRpcPortFile(readFileSync14(`${this.portDir}/${entry}`, "utf-8"));
208365
+ const record2 = parseRpcPortFile(readFileSync15(`${this.portDir}/${entry}`, "utf-8"));
206841
208366
  if (!record2 || record2.pid === process.pid || !isPidAlive(record2.pid))
206842
208367
  continue;
206843
208368
  log(`[rpc] another Magic Context RPC server is active for this project (pid ${record2.pid}, port ${record2.port}); starting separate instance on a new port`);