@cortexkit/opencode-magic-context 0.16.3 → 0.17.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 (64) hide show
  1. package/dist/features/magic-context/message-index-async.d.ts +12 -0
  2. package/dist/features/magic-context/message-index-async.d.ts.map +1 -0
  3. package/dist/features/magic-context/message-index.d.ts +4 -0
  4. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  5. package/dist/features/magic-context/migrations.d.ts +7 -0
  6. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  7. package/dist/features/magic-context/search.d.ts +2 -2
  8. package/dist/features/magic-context/search.d.ts.map +1 -1
  9. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  10. package/dist/features/magic-context/storage-meta-persisted.d.ts +3 -6
  11. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  12. package/dist/features/magic-context/storage-tags.d.ts +163 -1
  13. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  14. package/dist/features/magic-context/storage.d.ts +1 -1
  15. package/dist/features/magic-context/storage.d.ts.map +1 -1
  16. package/dist/features/magic-context/tagger.d.ts +52 -2
  17. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  18. package/dist/features/magic-context/tool-definition-tokens.d.ts +26 -3
  19. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  20. package/dist/features/magic-context/tool-owner-backfill.d.ts +90 -0
  21. package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -0
  22. package/dist/features/magic-context/types.d.ts +17 -0
  23. package/dist/features/magic-context/types.d.ts.map +1 -1
  24. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +23 -0
  26. package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/event-payloads.d.ts +8 -0
  29. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/inject-compartments.d.ts +16 -0
  34. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
  36. package/dist/hooks/magic-context/read-session-chunk.d.ts +24 -1
  37. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/read-session-db.d.ts +1 -0
  39. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/read-session-raw.d.ts +1 -0
  41. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/strip-content.d.ts +9 -6
  43. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/tag-messages.d.ts +1 -1
  45. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/tool-drop-target.d.ts +16 -1
  47. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +0 -11
  49. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1731 -758
  53. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  54. package/dist/plugin/rpc-handlers.d.ts +3 -0
  55. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  56. package/dist/shared/models-dev-cache.d.ts +3 -10
  57. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  58. package/dist/shared/tag-transcript.d.ts.map +1 -1
  59. package/package.json +1 -1
  60. package/src/shared/models-dev-cache.test.ts +64 -57
  61. package/src/shared/models-dev-cache.ts +49 -68
  62. package/src/shared/tag-transcript.ts +137 -126
  63. package/dist/hooks/magic-context/reasoning-capability.d.ts +0 -23
  64. package/dist/hooks/magic-context/reasoning-capability.d.ts.map +0 -1
@@ -146,151 +146,156 @@ export function tagTranscript(
146
146
  // across both invocation and result.
147
147
  const toolAggregates = new Map<string, ToolAggregate & { tagId: number }>();
148
148
 
149
- db.transaction(() => {
150
- for (let msgIndex = 0; msgIndex < transcript.messages.length; msgIndex += 1) {
151
- const message = transcript.messages[msgIndex];
152
- if (message === undefined) continue;
153
- const messageId = message.info.id;
149
+ // v3.3.1 Layer C (plan v3.3.1 Finding #16): the previous outer
150
+ // db.transaction() wrapper rolled back EVERY tag insert + savedSource
151
+ // when a single UNIQUE collision fired late in the walk. Per-tag
152
+ // SAVEPOINTs inside `assignToolTag` / `assignTag` already give us the
153
+ // atomicity we need. Removing the wrapper matches OpenCode's
154
+ // tag-messages.ts design — see the long comment there for the
155
+ // rationale (cache-bust amplifier story).
156
+ for (let msgIndex = 0; msgIndex < transcript.messages.length; msgIndex += 1) {
157
+ const message = transcript.messages[msgIndex];
158
+ if (message === undefined) continue;
159
+ const messageId = message.info.id;
154
160
 
155
- let textOrdinal = 0;
161
+ let textOrdinal = 0;
156
162
 
157
- for (let partIndex = 0; partIndex < message.parts.length; partIndex += 1) {
158
- const part = message.parts[partIndex];
159
- if (part === undefined) continue;
163
+ for (let partIndex = 0; partIndex < message.parts.length; partIndex += 1) {
164
+ const part = message.parts[partIndex];
165
+ if (part === undefined) continue;
160
166
 
161
- if (part.kind === "text") {
162
- // Synthetic message ids (Pi tail synthetic user with
163
- // no id) cannot be tagged — there's no stable handle
164
- // to bind a tag to across passes. Pass through
165
- // untagged; this is rare (only happens for the
166
- // dangling tool-result tail case in Pi).
167
- if (messageId === undefined) {
168
- textOrdinal += 1;
169
- continue;
170
- }
171
- tagTextPart({
167
+ if (part.kind === "text") {
168
+ // Synthetic message ids (Pi tail synthetic user with
169
+ // no id) cannot be tagged — there's no stable handle
170
+ // to bind a tag to across passes. Pass through
171
+ // untagged; this is rare (only happens for the
172
+ // dangling tool-result tail case in Pi).
173
+ if (messageId === undefined) {
174
+ textOrdinal += 1;
175
+ continue;
176
+ }
177
+ tagTextPart({
178
+ sessionId,
179
+ message,
180
+ messageId,
181
+ msgIndex,
182
+ textOrdinal,
183
+ part,
184
+ tagger,
185
+ db,
186
+ targets,
187
+ skipPrefixInjection,
188
+ });
189
+ textOrdinal += 1;
190
+ continue;
191
+ }
192
+
193
+ if (part.kind === "tool_use" || part.kind === "tool_result") {
194
+ if (messageId === undefined) continue;
195
+
196
+ const callId = part.id;
197
+ const text = part.getText() ?? "";
198
+ const meta = part.getToolMetadata();
199
+
200
+ if (typeof callId !== "string" || callId.length === 0) {
201
+ // No stable callId to aggregate on. Tag independently.
202
+ tagToolPart({
172
203
  sessionId,
173
204
  message,
174
205
  messageId,
175
206
  msgIndex,
176
- textOrdinal,
207
+ partIndex,
177
208
  part,
178
209
  tagger,
179
210
  db,
180
211
  targets,
181
212
  skipPrefixInjection,
182
213
  });
183
- textOrdinal += 1;
184
214
  continue;
185
215
  }
186
216
 
187
- if (part.kind === "tool_use" || part.kind === "tool_result") {
188
- if (messageId === undefined) continue;
189
-
190
- const callId = part.id;
191
- const text = part.getText() ?? "";
192
- const meta = part.getToolMetadata();
193
-
194
- if (typeof callId !== "string" || callId.length === 0) {
195
- // No stable callId to aggregate on. Tag independently.
196
- tagToolPart({
197
- sessionId,
198
- message,
199
- messageId,
200
- msgIndex,
201
- partIndex,
202
- part,
203
- tagger,
204
- db,
205
- targets,
206
- skipPrefixInjection,
207
- });
208
- continue;
217
+ const existing = toolAggregates.get(callId);
218
+ if (existing) {
219
+ // Second (or later) occurrence for this call_id.
220
+ // Merge into the existing aggregate, update byte_size
221
+ // in DB if larger, and rebuild the TagTarget so the
222
+ // closures over `occurrences` see all parts.
223
+ existing.occurrences.push({
224
+ message,
225
+ part,
226
+ kind: part.kind,
227
+ });
228
+ const newByteSize = byteSize(text);
229
+ if (newByteSize > existing.maxByteSize) {
230
+ existing.maxByteSize = newByteSize;
231
+ updateTagByteSize(db, sessionId, existing.tagId, newByteSize);
209
232
  }
210
-
211
- const existing = toolAggregates.get(callId);
212
- if (existing) {
213
- // Second (or later) occurrence for this call_id.
214
- // Merge into the existing aggregate, update byte_size
215
- // in DB if larger, and rebuild the TagTarget so the
216
- // closures over `occurrences` see all parts.
217
- existing.occurrences.push({
218
- message,
219
- part,
220
- kind: part.kind,
221
- });
222
- const newByteSize = byteSize(text);
223
- if (newByteSize > existing.maxByteSize) {
224
- existing.maxByteSize = newByteSize;
225
- updateTagByteSize(db, sessionId, existing.tagId, newByteSize);
226
- }
227
- if (existing.toolName === null && meta.toolName) {
228
- existing.toolName = meta.toolName;
229
- }
230
- if (
231
- existing.inputByteSize === 0 &&
232
- part.kind === "tool_use" &&
233
- meta.inputByteSize > 0
234
- ) {
235
- existing.inputByteSize = meta.inputByteSize;
236
- updateTagInputByteSize(
237
- db,
238
- sessionId,
239
- existing.tagId,
240
- meta.inputByteSize,
241
- );
242
- }
243
- // Inject §N§ prefix into this tool_result occurrence
244
- // (matches OpenCode behavior — only result gets the prefix).
245
- if (!skipPrefixInjection && part.kind === "tool_result") {
246
- part.setText(prependTag(existing.tagId, text));
247
- }
248
- // Rebuild the aggregate target so it walks the now-
249
- // longer occurrences list.
250
- targets.set(
251
- existing.tagId,
252
- buildAggregateTarget(existing.tagId, existing.occurrences),
253
- );
254
- } else {
255
- // First occurrence — reserve the tag number.
256
- const tagId = tagger.assignTag(
257
- sessionId,
258
- callId,
259
- "tool",
260
- byteSize(text),
261
- db,
262
- 0,
263
- meta.toolName ?? null,
264
- meta.inputByteSize,
265
- );
266
- const aggregate = {
267
- callId,
268
- tagId,
269
- occurrences: [
270
- {
271
- message,
272
- part,
273
- kind: part.kind,
274
- },
275
- ],
276
- maxByteSize: byteSize(text),
277
- toolName: meta.toolName ?? null,
278
- inputByteSize: part.kind === "tool_use" ? meta.inputByteSize : 0,
279
- };
280
- toolAggregates.set(callId, aggregate);
281
- // Inject §N§ prefix into this occurrence's visible text
282
- // when it's a tool_result. (OpenCode parity: prefix
283
- // only goes on the result, not the invocation.)
284
- if (!skipPrefixInjection && part.kind === "tool_result") {
285
- part.setText(prependTag(tagId, text));
286
- }
287
- targets.set(tagId, buildAggregateTarget(tagId, aggregate.occurrences));
233
+ if (existing.toolName === null && meta.toolName) {
234
+ existing.toolName = meta.toolName;
235
+ }
236
+ if (
237
+ existing.inputByteSize === 0 &&
238
+ part.kind === "tool_use" &&
239
+ meta.inputByteSize > 0
240
+ ) {
241
+ existing.inputByteSize = meta.inputByteSize;
242
+ updateTagInputByteSize(db, sessionId, existing.tagId, meta.inputByteSize);
243
+ }
244
+ // Inject §N§ prefix into this tool_result occurrence
245
+ // (matches OpenCode behavior — only result gets the prefix).
246
+ if (!skipPrefixInjection && part.kind === "tool_result") {
247
+ part.setText(prependTag(existing.tagId, text));
248
+ }
249
+ // Rebuild the aggregate target so it walks the now-
250
+ // longer occurrences list.
251
+ targets.set(
252
+ existing.tagId,
253
+ buildAggregateTarget(existing.tagId, existing.occurrences),
254
+ );
255
+ } else {
256
+ // First occurrence — reserve the tag number.
257
+ // v3.3.1 Layer C: Pi main aggregation path. Owner
258
+ // is the Pi message hosting the tool aggregate.
259
+ // Owner stays stable across passes because Pi
260
+ // re-emits the full transcript each time and
261
+ // message ids are durable.
262
+ const tagId = tagger.assignToolTag(
263
+ sessionId,
264
+ callId,
265
+ messageId,
266
+ byteSize(text),
267
+ db,
268
+ 0,
269
+ meta.toolName ?? null,
270
+ meta.inputByteSize,
271
+ );
272
+ const aggregate = {
273
+ callId,
274
+ tagId,
275
+ occurrences: [
276
+ {
277
+ message,
278
+ part,
279
+ kind: part.kind,
280
+ },
281
+ ],
282
+ maxByteSize: byteSize(text),
283
+ toolName: meta.toolName ?? null,
284
+ inputByteSize: part.kind === "tool_use" ? meta.inputByteSize : 0,
285
+ };
286
+ toolAggregates.set(callId, aggregate);
287
+ // Inject §N§ prefix into this occurrence's visible text
288
+ // when it's a tool_result. (OpenCode parity: prefix
289
+ // only goes on the result, not the invocation.)
290
+ if (!skipPrefixInjection && part.kind === "tool_result") {
291
+ part.setText(prependTag(tagId, text));
288
292
  }
293
+ targets.set(tagId, buildAggregateTarget(tagId, aggregate.occurrences));
289
294
  }
290
- // thinking, image, file, structural, unknown → skip.
291
295
  }
296
+ // thinking, image, file, structural, unknown → skip.
292
297
  }
293
- })();
298
+ }
294
299
 
295
300
  return { targets };
296
301
  }
@@ -366,10 +371,16 @@ function tagToolPart(args: TagToolPartArgs): void {
366
371
  const contentId = stableId ?? `${args.messageId}:t${args.partIndex}`;
367
372
  const text = args.part.getText() ?? "";
368
373
  const meta = args.part.getToolMetadata();
369
- const tagId = args.tagger.assignTag(
374
+ // v3.3.1 Layer C: synthetic ownership for the no-callId Pi
375
+ // fallback. Owner == callId == contentId. The composite key
376
+ // collapses to a unique synthetic identifier per part, preserving
377
+ // the legacy "each part gets its own tag" behavior while
378
+ // satisfying the composite-identity contract (TagEntry.tool_owner_message_id
379
+ // is non-null, lazy-adoption path is correctly bypassed).
380
+ const tagId = args.tagger.assignToolTag(
370
381
  args.sessionId,
371
382
  contentId,
372
- "tool",
383
+ contentId,
373
384
  byteSize(text),
374
385
  args.db,
375
386
  0,
@@ -1,23 +0,0 @@
1
- export interface LiveSessionModel {
2
- providerID: string;
3
- modelID: string;
4
- }
5
- /**
6
- * Return true when the session's current model exposes an interleaved
7
- * reasoning field (for example `reasoning_content`). In that mode OpenCode's
8
- * provider transform concatenates typed `reasoning` parts into a top-level
9
- * wire field and strips those parts from `content` afterwards.
10
- *
11
- * Magic-context's older reasoning cleanup rewrites aged reasoning to the
12
- * literal string `[cleared]` and then removes those reasoning parts entirely.
13
- * That is safe for providers that treat reasoning as optional local context,
14
- * but it breaks providers like Moonshot/Kimi: once we remove all reasoning
15
- * parts from an assistant tool-call message, OpenCode emits no
16
- * `reasoning_content` field and the provider rejects the request.
17
- *
18
- * Unknown models return false on purpose. We only disable stripping when the
19
- * cache explicitly says the provider requires interleaved reasoning; guessing
20
- * true would preserve extra context for models that do not need it.
21
- */
22
- export declare function modelRequiresInterleavedReasoning(model: LiveSessionModel | undefined): boolean;
23
- //# sourceMappingURL=reasoning-capability.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reasoning-capability.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/reasoning-capability.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAO9F"}