@cortexkit/pi-magic-context 0.21.7 → 0.21.8

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.
package/dist/index.js CHANGED
@@ -142834,6 +142834,49 @@ var MIGRATIONS = [
142834
142834
  ON compartment_state_lease(expires_at);
142835
142835
  `);
142836
142836
  }
142837
+ },
142838
+ {
142839
+ version: 20,
142840
+ description: "Add subagent invocation token accounting",
142841
+ up: (db) => {
142842
+ db.exec(`
142843
+ CREATE TABLE IF NOT EXISTS subagent_invocations (
142844
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
142845
+ session_id TEXT NOT NULL,
142846
+ harness TEXT NOT NULL,
142847
+ subagent TEXT NOT NULL,
142848
+ task TEXT,
142849
+ provider_id TEXT,
142850
+ model_id TEXT,
142851
+ started_at INTEGER NOT NULL,
142852
+ ended_at INTEGER,
142853
+ status TEXT NOT NULL,
142854
+ input_tokens INTEGER NOT NULL DEFAULT 0,
142855
+ output_tokens INTEGER NOT NULL DEFAULT 0,
142856
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
142857
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
142858
+ error TEXT,
142859
+ parent_invocation_id INTEGER
142860
+ );
142861
+ CREATE INDEX IF NOT EXISTS idx_sai_session_started
142862
+ ON subagent_invocations(session_id, started_at DESC);
142863
+ CREATE INDEX IF NOT EXISTS idx_sai_subagent
142864
+ ON subagent_invocations(subagent, started_at DESC);
142865
+ `);
142866
+ }
142867
+ },
142868
+ {
142869
+ version: 21,
142870
+ description: "Add session lifetime work metrics",
142871
+ up: (db) => {
142872
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
142873
+ if (!cols.some((c) => c.name === "new_work_tokens")) {
142874
+ db.exec("ALTER TABLE session_meta ADD COLUMN new_work_tokens INTEGER NOT NULL DEFAULT 0");
142875
+ }
142876
+ if (!cols.some((c) => c.name === "total_input_tokens")) {
142877
+ db.exec("ALTER TABLE session_meta ADD COLUMN total_input_tokens INTEGER NOT NULL DEFAULT 0");
142878
+ }
142879
+ }
142837
142880
  }
142838
142881
  ];
142839
142882
  function ensureMigrationsTable(db) {
@@ -143494,12 +143537,37 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143494
143537
  -- is a valid JSON blob written via setPendingPiCompactionMarkerState.
143495
143538
  -- Excluded from healNullTextColumns.
143496
143539
  pending_pi_compaction_marker_state TEXT,
143540
+ new_work_tokens INTEGER NOT NULL DEFAULT 0,
143541
+ total_input_tokens INTEGER NOT NULL DEFAULT 0,
143497
143542
  -- deferred_execute_state: intentionally NULLABLE without a default.
143498
143543
  -- Absence is SQL NULL; presence is a JSON blob written via
143499
143544
  -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
143500
143545
  deferred_execute_state TEXT
143501
143546
  );
143502
143547
 
143548
+ CREATE TABLE IF NOT EXISTS subagent_invocations (
143549
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
143550
+ session_id TEXT NOT NULL,
143551
+ harness TEXT NOT NULL,
143552
+ subagent TEXT NOT NULL,
143553
+ task TEXT,
143554
+ provider_id TEXT,
143555
+ model_id TEXT,
143556
+ started_at INTEGER NOT NULL,
143557
+ ended_at INTEGER,
143558
+ status TEXT NOT NULL,
143559
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143560
+ output_tokens INTEGER NOT NULL DEFAULT 0,
143561
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
143562
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
143563
+ error TEXT,
143564
+ parent_invocation_id INTEGER
143565
+ );
143566
+ CREATE INDEX IF NOT EXISTS idx_sai_session_started
143567
+ ON subagent_invocations(session_id, started_at DESC);
143568
+ CREATE INDEX IF NOT EXISTS idx_sai_subagent
143569
+ ON subagent_invocations(subagent, started_at DESC);
143570
+
143503
143571
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
143504
143572
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
143505
143573
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -143591,6 +143659,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143591
143659
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
143592
143660
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
143593
143661
  ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
143662
+ ensureColumn(db, "session_meta", "new_work_tokens", "INTEGER NOT NULL DEFAULT 0");
143663
+ ensureColumn(db, "session_meta", "total_input_tokens", "INTEGER NOT NULL DEFAULT 0");
143594
143664
  ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
143595
143665
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143596
143666
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -143654,7 +143724,9 @@ function healNullIntegerColumns(db) {
143654
143724
  ["tool_call_tokens", 0],
143655
143725
  ["note_nudge_trigger_pending", 0],
143656
143726
  ["observed_safe_input_tokens", 0],
143657
- ["cache_alert_sent", 0]
143727
+ ["cache_alert_sent", 0],
143728
+ ["new_work_tokens", 0],
143729
+ ["total_input_tokens", 0]
143658
143730
  ];
143659
143731
  for (const [column, fallback] of columns) {
143660
143732
  try {
@@ -144264,6 +144336,12 @@ function clearDeferredExecutePendingIfMatches(db, sessionId, expected) {
144264
144336
  WHERE session_id = ? AND deferred_execute_state = ?`).run(sessionId, expectedBlob);
144265
144337
  return result.changes > 0;
144266
144338
  }
144339
+ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
144340
+ ensureSessionMetaRow(db, sessionId);
144341
+ db.prepare(`UPDATE session_meta
144342
+ SET new_work_tokens = ?, total_input_tokens = ?
144343
+ WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
144344
+ }
144267
144345
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
144268
144346
  init_logger();
144269
144347
  await init_read_session_db();
@@ -144579,6 +144657,19 @@ function getSourceContents(db, sessionId, tagIds) {
144579
144657
  }
144580
144658
  return sources;
144581
144659
  }
144660
+ // ../plugin/src/features/magic-context/storage-subagent-invocations.ts
144661
+ function clampToken(value) {
144662
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
144663
+ }
144664
+ function recordSubagentInvocation(db, input) {
144665
+ const result = db.prepare(`INSERT INTO subagent_invocations (
144666
+ session_id, harness, subagent, task, provider_id, model_id,
144667
+ started_at, ended_at, status,
144668
+ input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
144669
+ error, parent_invocation_id
144670
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.sessionId, input.harness, input.subagent, input.task ?? null, input.providerId ?? null, input.modelId ?? null, input.startedAt, input.endedAt, input.status, clampToken(input.inputTokens), clampToken(input.outputTokens), clampToken(input.cacheReadTokens), clampToken(input.cacheWriteTokens), input.error ?? null, input.parentInvocationId ?? null);
144671
+ return Number(result.lastInsertRowid);
144672
+ }
144582
144673
  // ../plugin/src/features/magic-context/storage-tags.ts
144583
144674
  var insertTagStatements = new WeakMap;
144584
144675
  var updateTagStatusStatements = new WeakMap;
@@ -145563,6 +145654,97 @@ import { createRequire as createRequire3 } from "node:module";
145563
145654
  import { dirname as dirname3, join as join9, resolve as resolvePath } from "node:path";
145564
145655
  import { createInterface } from "node:readline";
145565
145656
  import { fileURLToPath } from "node:url";
145657
+
145658
+ // ../plugin/src/features/magic-context/subagent-token-capture.ts
145659
+ init_logger();
145660
+ function asNumber(value) {
145661
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
145662
+ }
145663
+ function tokenObjectFromMessage(message) {
145664
+ const info = message.info;
145665
+ if (info && typeof info === "object") {
145666
+ const tokens2 = info.tokens;
145667
+ if (tokens2 && typeof tokens2 === "object")
145668
+ return tokens2;
145669
+ }
145670
+ const tokens = message.tokens;
145671
+ if (tokens && typeof tokens === "object")
145672
+ return tokens;
145673
+ return null;
145674
+ }
145675
+ function isAssistantMessage(message) {
145676
+ if (!message || typeof message !== "object")
145677
+ return false;
145678
+ const record = message;
145679
+ const info = record.info;
145680
+ if (info && typeof info === "object") {
145681
+ return info.role === "assistant";
145682
+ }
145683
+ return record.role === "assistant";
145684
+ }
145685
+ function modelFromMessage(message) {
145686
+ const info = message.info;
145687
+ const source = info && typeof info === "object" ? info : message;
145688
+ return {
145689
+ providerId: typeof source.providerID === "string" ? source.providerID : typeof source.providerId === "string" ? source.providerId : null,
145690
+ modelId: typeof source.modelID === "string" ? source.modelID : typeof source.modelId === "string" ? source.modelId : null
145691
+ };
145692
+ }
145693
+ function emptyTokenTotals() {
145694
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
145695
+ }
145696
+ function sumTokensFromChildMessages(messages) {
145697
+ const totals = emptyTokenTotals();
145698
+ for (const message of messages) {
145699
+ if (!isAssistantMessage(message))
145700
+ continue;
145701
+ const tokens = tokenObjectFromMessage(message);
145702
+ if (!tokens)
145703
+ continue;
145704
+ const cache = tokens.cache && typeof tokens.cache === "object" ? tokens.cache : {};
145705
+ totals.input += asNumber(tokens.input);
145706
+ totals.output += asNumber(tokens.output);
145707
+ totals.cacheRead += asNumber(cache.read ?? tokens.cacheRead ?? tokens.cache_read);
145708
+ totals.cacheWrite += asNumber(cache.write ?? tokens.cacheWrite ?? tokens.cache_write);
145709
+ }
145710
+ return totals;
145711
+ }
145712
+ function findLastAssistantModel(messages) {
145713
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
145714
+ const message = messages[index];
145715
+ if (isAssistantMessage(message))
145716
+ return modelFromMessage(message);
145717
+ }
145718
+ return { providerId: null, modelId: null };
145719
+ }
145720
+ function recordChildInvocation(input) {
145721
+ const tokens = input.tokens ?? sumTokensFromChildMessages(input.messages ?? []);
145722
+ const model = input.providerId !== undefined || input.modelId !== undefined ? { providerId: input.providerId ?? null, modelId: input.modelId ?? null } : findLastAssistantModel(input.messages ?? []);
145723
+ try {
145724
+ return recordSubagentInvocation(input.db, {
145725
+ sessionId: input.parentSessionId,
145726
+ harness: input.harness,
145727
+ subagent: input.subagent,
145728
+ task: input.task ?? null,
145729
+ providerId: model.providerId,
145730
+ modelId: model.modelId,
145731
+ startedAt: input.startedAt,
145732
+ endedAt: input.endedAt ?? Date.now(),
145733
+ status: input.status,
145734
+ inputTokens: tokens.input,
145735
+ outputTokens: tokens.output,
145736
+ cacheReadTokens: tokens.cacheRead,
145737
+ cacheWriteTokens: tokens.cacheWrite,
145738
+ error: input.error ? describeError(input.error).brief : null,
145739
+ parentInvocationId: input.parentInvocationId ?? null
145740
+ });
145741
+ } catch (error) {
145742
+ sessionLog(input.parentSessionId, "subagent token accounting failed:", describeError(error).brief);
145743
+ return null;
145744
+ }
145745
+ }
145746
+
145747
+ // src/subagent-runner.ts
145566
145748
  function resolveBundledPiCli() {
145567
145749
  try {
145568
145750
  const require_ = createRequire3(import.meta.url);
@@ -145592,6 +145774,17 @@ var DREAMER_ACTION_AGENTS = new Set([
145592
145774
  "dreamer",
145593
145775
  "magic-context-dreamer"
145594
145776
  ]);
145777
+ function inferAccountingSubagent(agent) {
145778
+ if (agent.includes("sidekick"))
145779
+ return "sidekick";
145780
+ if (agent.includes("dreamer"))
145781
+ return "dreamer";
145782
+ if (agent.includes("compressor"))
145783
+ return "compressor";
145784
+ if (agent.includes("recomp"))
145785
+ return "recomp";
145786
+ return "historian";
145787
+ }
145595
145788
 
145596
145789
  class PiSubagentRunner {
145597
145790
  harness = "pi";
@@ -145633,21 +145826,45 @@ class PiSubagentRunner {
145633
145826
  }
145634
145827
  async runOnce(options) {
145635
145828
  const startTime = Date.now();
145829
+ let recordedAccounting = false;
145830
+ const recordAccounting = (result, messages = []) => {
145831
+ if (!options.accountingSessionId || recordedAccounting)
145832
+ return;
145833
+ recordedAccounting = true;
145834
+ recordChildInvocation({
145835
+ db: openDatabase(),
145836
+ parentSessionId: options.accountingSessionId,
145837
+ harness: "pi",
145838
+ subagent: options.accountingSubagent ?? inferAccountingSubagent(options.agent),
145839
+ task: options.accountingTask ?? null,
145840
+ startedAt: startTime,
145841
+ status: result.ok ? "completed" : result.reason === "abort" ? "aborted" : "failed",
145842
+ messages,
145843
+ providerId: typeof options.model === "string" ? options.model.split("/")[0] : null,
145844
+ modelId: typeof options.model === "string" ? options.model.split("/").slice(1).join("/") : null,
145845
+ error: result.ok ? null : result.error,
145846
+ parentInvocationId: options.accountingParentInvocationId ?? null
145847
+ });
145848
+ };
145636
145849
  if (options.signal?.aborted) {
145637
- return {
145850
+ const result = {
145638
145851
  ok: false,
145639
145852
  reason: "abort",
145640
145853
  error: "pi subagent aborted by caller",
145641
145854
  durationMs: Date.now() - startTime
145642
145855
  };
145856
+ recordAccounting(result);
145857
+ return result;
145643
145858
  }
145644
145859
  const args = buildArgs(options);
145645
145860
  return new Promise((resolve2) => {
145861
+ let accountingMessages = [];
145646
145862
  let settled = false;
145647
145863
  const settle = (result) => {
145648
145864
  if (settled)
145649
145865
  return;
145650
145866
  settled = true;
145867
+ recordAccounting(result, accountingMessages);
145651
145868
  resolve2(result);
145652
145869
  };
145653
145870
  const emitProgress = (event) => {
@@ -145717,6 +145934,7 @@ class PiSubagentRunner {
145717
145934
  let lastEventType = null;
145718
145935
  let lastEventTimestamp = 0;
145719
145936
  const accumulatedMessages = [];
145937
+ accountingMessages = accumulatedMessages;
145720
145938
  rl.on("line", (line) => {
145721
145939
  if (line.length === 0)
145722
145940
  return;
@@ -146069,7 +146287,9 @@ function registerCtxAugCommand(pi, config) {
146069
146287
  timeoutMs: config.timeoutMs ?? 30000,
146070
146288
  cwd: ctx.cwd,
146071
146289
  signal: ctx.signal,
146072
- thinkingLevel: config.thinking_level
146290
+ thinkingLevel: config.thinking_level,
146291
+ accountingSessionId: sessionLabel,
146292
+ accountingSubagent: "sidekick"
146073
146293
  });
146074
146294
  if (!result.ok) {
146075
146295
  log(`[magic-context][pi] /ctx-aug: sidekick failed (${result.reason}): ${result.error}`);
@@ -148067,6 +148287,23 @@ Return valid JSON (no markdown fencing):
148067
148287
 
148068
148288
  If no promotions are warranted, return empty arrays. Always consume reviewed candidates so they don't accumulate indefinitely.`;
148069
148289
  let agentSessionId = null;
148290
+ const startedAt = Date.now();
148291
+ let invocationRecorded = false;
148292
+ const recordInvocation = (params) => {
148293
+ if (!args.parentSessionId || invocationRecorded)
148294
+ return;
148295
+ invocationRecorded = true;
148296
+ recordChildInvocation({
148297
+ db: args.db,
148298
+ parentSessionId: args.parentSessionId,
148299
+ harness: "opencode",
148300
+ subagent: "user_memory_review",
148301
+ startedAt,
148302
+ status: params.status,
148303
+ messages: params.messages,
148304
+ error: params.error
148305
+ });
148306
+ };
148070
148307
  const abortController = new AbortController;
148071
148308
  const leaseInterval = setInterval(() => {
148072
148309
  try {
@@ -148088,8 +148325,11 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
148088
148325
  });
148089
148326
  const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
148090
148327
  agentSessionId = typeof created?.id === "string" ? created.id : null;
148091
- if (!agentSessionId)
148092
- throw new Error("Could not create user memory review session.");
148328
+ if (!agentSessionId) {
148329
+ const error = new Error("Could not create user memory review session.");
148330
+ recordInvocation({ status: "failed", error });
148331
+ throw error;
148332
+ }
148093
148333
  log(`[dreamer] user-memories: child session created ${agentSessionId}`);
148094
148334
  const remainingMs = Math.max(0, args.deadline - Date.now());
148095
148335
  await promptSyncWithModelSuggestionRetry(args.client, {
@@ -148113,6 +148353,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
148113
148353
  const messages = normalizeSDKResponse(messagesResponse, [], {
148114
148354
  preferResponseOnMissingData: true
148115
148355
  });
148356
+ recordInvocation({ status: "completed", messages });
148116
148357
  const responseText = extractLatestAssistantText(messages);
148117
148358
  if (!responseText) {
148118
148359
  log("[dreamer] user-memories: no response from review agent");
@@ -148352,6 +148593,24 @@ async function runDream(args) {
148352
148593
  log(`[dreamer] starting task: ${taskName}`);
148353
148594
  const taskStartedAt = Date.now();
148354
148595
  let agentSessionId = null;
148596
+ const invocationStartedAt = Date.now();
148597
+ let invocationRecorded = false;
148598
+ const recordInvocation = (params) => {
148599
+ if (!parentSessionId || invocationRecorded)
148600
+ return;
148601
+ invocationRecorded = true;
148602
+ recordChildInvocation({
148603
+ db: args.db,
148604
+ parentSessionId,
148605
+ harness: "opencode",
148606
+ subagent: "dreamer",
148607
+ task: taskName,
148608
+ startedAt: invocationStartedAt,
148609
+ status: params.status,
148610
+ messages: params.messages,
148611
+ error: params.error
148612
+ });
148613
+ };
148355
148614
  const taskAbortController = new AbortController;
148356
148615
  const leaseRenewalInterval = setInterval(() => {
148357
148616
  try {
@@ -148392,7 +148651,9 @@ async function runDream(args) {
148392
148651
  const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
148393
148652
  agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
148394
148653
  if (!agentSessionId) {
148395
- throw new Error("Dreamer could not create its child session.");
148654
+ const error = new Error("Dreamer could not create its child session.");
148655
+ recordInvocation({ status: "failed", error });
148656
+ throw error;
148396
148657
  }
148397
148658
  log(`[dreamer] task ${taskName}: child session created ${agentSessionId}`);
148398
148659
  await promptSyncWithModelSuggestionRetry(args.client, {
@@ -148419,6 +148680,7 @@ async function runDream(args) {
148419
148680
  const messages = normalizeSDKResponse(messagesResponse, [], {
148420
148681
  preferResponseOnMissingData: true
148421
148682
  });
148683
+ recordInvocation({ status: "completed", messages });
148422
148684
  const taskResult = extractLatestAssistantText(messages);
148423
148685
  if (!taskResult) {
148424
148686
  throw new Error("Dreamer returned no assistant output.");
@@ -148433,6 +148695,7 @@ async function runDream(args) {
148433
148695
  lastErrorSignature = null;
148434
148696
  consecutiveSameErrorFailures = 0;
148435
148697
  } catch (error) {
148698
+ recordInvocation({ status: lostLease ? "aborted" : "failed", error });
148436
148699
  const durationMs = Date.now() - taskStartedAt;
148437
148700
  const errorDescription = describeError(error);
148438
148701
  logWithStackHead(`[dreamer] task ${taskName}: failed after ${(durationMs / 1000).toFixed(1)}s — ${errorDescription.brief}`, errorDescription.stackHead);
@@ -148702,6 +148965,24 @@ ${noteDescriptions}
148702
148965
  Only include notes whose conditions you could definitively evaluate against external signals. Skip notes where you cannot determine the status (they will be re-evaluated next run, or eventually archived as stale).`;
148703
148966
  const taskStartedAt = Date.now();
148704
148967
  let agentSessionId = null;
148968
+ const startedAt = Date.now();
148969
+ let invocationRecorded = false;
148970
+ const recordInvocation = (params) => {
148971
+ if (!args.parentSessionId || invocationRecorded)
148972
+ return;
148973
+ invocationRecorded = true;
148974
+ recordChildInvocation({
148975
+ db: args.db,
148976
+ parentSessionId: args.parentSessionId,
148977
+ harness: "opencode",
148978
+ subagent: "dreamer",
148979
+ task: "smart-notes",
148980
+ startedAt,
148981
+ status: params.status,
148982
+ messages: params.messages,
148983
+ error: params.error
148984
+ });
148985
+ };
148705
148986
  const abortController = new AbortController;
148706
148987
  const leaseInterval = setInterval(() => {
148707
148988
  try {
@@ -148725,8 +149006,11 @@ Only include notes whose conditions you could definitively evaluate against exte
148725
149006
  });
148726
149007
  const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
148727
149008
  agentSessionId = typeof created?.id === "string" ? created.id : null;
148728
- if (!agentSessionId)
148729
- throw new Error("Could not create smart note evaluation session.");
149009
+ if (!agentSessionId) {
149010
+ const error = new Error("Could not create smart note evaluation session.");
149011
+ recordInvocation({ status: "failed", error });
149012
+ throw error;
149013
+ }
148730
149014
  log(`[dreamer] smart notes: child session created ${agentSessionId}`);
148731
149015
  const remainingMs = Math.max(0, args.deadline - Date.now());
148732
149016
  await promptSyncWithModelSuggestionRetry(args.client, {
@@ -148750,6 +149034,7 @@ Only include notes whose conditions you could definitively evaluate against exte
148750
149034
  const messages = normalizeSDKResponse(messagesResponse, [], {
148751
149035
  preferResponseOnMissingData: true
148752
149036
  });
149037
+ recordInvocation({ status: "completed", messages });
148753
149038
  const output = extractLatestAssistantText(messages);
148754
149039
  if (!output)
148755
149040
  throw new Error("Smart note evaluation returned no output.");
@@ -166118,6 +166403,57 @@ function createTagger() {
166118
166403
  };
166119
166404
  }
166120
166405
 
166406
+ // ../plugin/src/features/magic-context/work-metrics.ts
166407
+ function asNumber2(value) {
166408
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
166409
+ }
166410
+ function getPiUsage(entry) {
166411
+ if (!entry || typeof entry !== "object")
166412
+ return null;
166413
+ const record2 = entry;
166414
+ const message = record2.message && typeof record2.message === "object" ? record2.message : record2;
166415
+ if (message.role !== "assistant")
166416
+ return null;
166417
+ if (!message.usage || typeof message.usage !== "object")
166418
+ return null;
166419
+ const usage = message.usage;
166420
+ return {
166421
+ input: asNumber2(usage.input),
166422
+ output: asNumber2(usage.output),
166423
+ cacheRead: asNumber2(usage.cacheRead ?? usage.cache_read),
166424
+ cacheWrite: asNumber2(usage.cacheWrite ?? usage.cache_write)
166425
+ };
166426
+ }
166427
+ function computePiWorkMetrics(sessionEntries) {
166428
+ let previousPrompt = 0;
166429
+ let phasePeak = 0;
166430
+ let newWorkTokens = 0;
166431
+ let totalInputTokens = 0;
166432
+ let lastOutput = 0;
166433
+ let sawAssistant = false;
166434
+ for (const entry of sessionEntries) {
166435
+ const usage = getPiUsage(entry);
166436
+ if (!usage)
166437
+ continue;
166438
+ const prompt = usage.input + usage.cacheRead + usage.cacheWrite;
166439
+ if (sawAssistant && prompt < previousPrompt) {
166440
+ totalInputTokens += phasePeak;
166441
+ phasePeak = prompt;
166442
+ } else {
166443
+ phasePeak = Math.max(phasePeak, prompt);
166444
+ }
166445
+ newWorkTokens += Math.max(0, prompt - previousPrompt);
166446
+ previousPrompt = prompt;
166447
+ lastOutput = usage.output;
166448
+ sawAssistant = true;
166449
+ }
166450
+ if (sawAssistant) {
166451
+ totalInputTokens += phasePeak;
166452
+ newWorkTokens += lastOutput;
166453
+ }
166454
+ return { newWorkTokens, totalInputTokens };
166455
+ }
166456
+
166121
166457
  // ../plugin/src/hooks/magic-context/apply-operations.ts
166122
166458
  var USER_DROP_PREVIEW_CHARS = 250;
166123
166459
  function buildReplacementContent(tagId, target) {
@@ -168879,12 +169215,12 @@ function createAssistantNudgeMessage(text, messages) {
168879
169215
  function findLatestAssistantMessage(messages) {
168880
169216
  for (let i = messages.length - 1;i >= 0; i--) {
168881
169217
  const message = messages[i];
168882
- if (isAssistantMessage(message))
169218
+ if (isAssistantMessage2(message))
168883
169219
  return message;
168884
169220
  }
168885
169221
  return;
168886
169222
  }
168887
- function isAssistantMessage(message) {
169223
+ function isAssistantMessage2(message) {
168888
169224
  return getRole(message) === "assistant";
168889
169225
  }
168890
169226
  function createZeroUsage() {
@@ -169495,7 +169831,9 @@ async function runCompressorPass(args) {
169495
169831
  fallbackModels: args.fallbackModels,
169496
169832
  timeoutMs: args.historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
169497
169833
  cwd: args.directory,
169498
- thinkingLevel: args.thinkingLevel
169834
+ thinkingLevel: args.thinkingLevel,
169835
+ accountingSessionId: args.sessionId,
169836
+ accountingSubagent: "compressor"
169499
169837
  });
169500
169838
  if (!result.ok) {
169501
169839
  sessionLog(args.sessionId, `compressor: subagent failed (${result.reason}): ${result.error}`);
@@ -170239,7 +170577,9 @@ ${chunk.text}`, { stateFilePath });
170239
170577
  timeoutMs: historianTimeoutMs,
170240
170578
  cwd: directory,
170241
170579
  thinkingLevel,
170242
- onProgress: buildProgressLogger("first")
170580
+ onProgress: buildProgressLogger("first"),
170581
+ accountingSessionId: sessionId,
170582
+ accountingSubagent: "historian"
170243
170583
  });
170244
170584
  let validatedPass = await validateHistorianResult(firstResult, sessionId, chunk, priorCompartments, sequenceOffset);
170245
170585
  let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
@@ -170255,7 +170595,9 @@ ${chunk.text}`, { stateFilePath });
170255
170595
  timeoutMs: historianTimeoutMs,
170256
170596
  cwd: directory,
170257
170597
  thinkingLevel,
170258
- onProgress: buildProgressLogger("repair")
170598
+ onProgress: buildProgressLogger("repair"),
170599
+ accountingSessionId: sessionId,
170600
+ accountingSubagent: "historian"
170259
170601
  });
170260
170602
  validatedPass = await validateHistorianResult(repairResult, sessionId, chunk, priorCompartments, sequenceOffset);
170261
170603
  if (validatedPass.kind === "ok" && repairResult.ok) {
@@ -170281,7 +170623,9 @@ ${chunk.text}`, { stateFilePath });
170281
170623
  timeoutMs: historianTimeoutMs,
170282
170624
  cwd: directory,
170283
170625
  thinkingLevel,
170284
- onProgress: buildProgressLogger("editor")
170626
+ onProgress: buildProgressLogger("editor"),
170627
+ accountingSessionId: sessionId,
170628
+ accountingSubagent: "historian_editor"
170285
170629
  });
170286
170630
  const editorPass = await validateHistorianResult(editorResult, sessionId, chunk, priorCompartments, sequenceOffset);
170287
170631
  if (editorPass.kind === "ok") {
@@ -172403,6 +172747,12 @@ function registerPiContextHandler(pi, options) {
172403
172747
  } catch (err) {
172404
172748
  sessionLog(sessionId, `synthetic todowrite injection failed: ${err instanceof Error ? err.message : String(err)}`);
172405
172749
  }
172750
+ try {
172751
+ const metrics = computePiWorkMetrics(outputMessages);
172752
+ setSessionWorkMetrics(options.db, sessionId, metrics.newWorkTokens, metrics.totalInputTokens);
172753
+ } catch (err) {
172754
+ sessionLog(sessionId, `work-metrics update failed: ${err instanceof Error ? err.message : String(err)}`);
172755
+ }
172406
172756
  logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
172407
172757
  sessionLog(sessionId, `transform completed in ${(performance.now() - transformStartTime).toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
172408
172758
  clearLastTransformErrorIfSet(options.db, sessionId);
@@ -173828,6 +174178,27 @@ async function runCompressorPass2(args) {
173828
174178
  } = args;
173829
174179
  const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount);
173830
174180
  let agentSessionId = null;
174181
+ const startedAt = Date.now();
174182
+ let invocationRecorded = false;
174183
+ const recordInvocation = (params) => {
174184
+ if (invocationRecorded)
174185
+ return;
174186
+ invocationRecorded = true;
174187
+ try {
174188
+ recordChildInvocation({
174189
+ db: openDatabase(),
174190
+ parentSessionId: sessionId,
174191
+ harness: "opencode",
174192
+ subagent: "compressor",
174193
+ startedAt,
174194
+ status: params.status,
174195
+ messages: params.messages,
174196
+ error: params.error
174197
+ });
174198
+ } catch (error51) {
174199
+ sessionLog(sessionId, "subagent token accounting unavailable", getErrorMessage(error51));
174200
+ }
174201
+ };
173831
174202
  try {
173832
174203
  const createResponse = await client.session.create({
173833
174204
  body: { parentID: sessionId, title: "magic-context-compressor" },
@@ -173837,6 +174208,7 @@ async function runCompressorPass2(args) {
173837
174208
  agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
173838
174209
  if (!agentSessionId) {
173839
174210
  sessionLog(sessionId, "compressor: could not create child session");
174211
+ recordInvocation({ status: "failed", error: "could not create child session" });
173840
174212
  return null;
173841
174213
  }
173842
174214
  await promptSyncWithModelSuggestionRetry(client, {
@@ -173858,6 +174230,7 @@ async function runCompressorPass2(args) {
173858
174230
  const messages = normalizeSDKResponse(messagesResponse, [], {
173859
174231
  preferResponseOnMissingData: true
173860
174232
  });
174233
+ recordInvocation({ status: "completed", messages });
173861
174234
  const result = extractLatestAssistantText(messages);
173862
174235
  if (!result) {
173863
174236
  sessionLog(sessionId, "compressor: historian returned no output");
@@ -173878,6 +174251,7 @@ async function runCompressorPass2(args) {
173878
174251
  }
173879
174252
  return snapped.result;
173880
174253
  } catch (error51) {
174254
+ recordInvocation({ status: "failed", error: error51 });
173881
174255
  sessionLog(sessionId, "compressor: historian call failed:", getErrorMessage(error51));
173882
174256
  return null;
173883
174257
  } finally {
@@ -173916,7 +174290,8 @@ async function runValidatedHistorianPass(args) {
173916
174290
  ...args,
173917
174291
  draftXml: firstRun.result,
173918
174292
  draftValidation: firstValidation,
173919
- draftDumpPath: firstRun.dumpPath
174293
+ draftDumpPath: firstRun.dumpPath,
174294
+ draftInvocationId: firstRun.invocationId ?? null
173920
174295
  }) : firstValidation;
173921
174296
  cleanupHistorianDump(args.parentSessionId, firstRun.dumpPath);
173922
174297
  return finalResult;
@@ -173942,7 +174317,8 @@ async function runValidatedHistorianPass(args) {
173942
174317
  ...args,
173943
174318
  draftXml: repairRun.result,
173944
174319
  draftValidation: repairValidation,
173945
- draftDumpPath: repairRun.dumpPath
174320
+ draftDumpPath: repairRun.dumpPath,
174321
+ draftInvocationId: repairRun.invocationId ?? null
173946
174322
  }) : repairValidation;
173947
174323
  cleanupHistorianDump(args.parentSessionId, repairRun.dumpPath);
173948
174324
  return finalResult;
@@ -173963,7 +174339,8 @@ async function runEditorPassOrFallback(args) {
173963
174339
  prompt: buildHistorianEditorPrompt(args.draftXml),
173964
174340
  timeoutMs: args.timeoutMs,
173965
174341
  dumpLabel: `${args.dumpLabelBase}-editor`,
173966
- agentId: HISTORIAN_EDITOR_AGENT
174342
+ agentId: HISTORIAN_EDITOR_AGENT,
174343
+ parentInvocationId: args.draftInvocationId ?? null
173967
174344
  });
173968
174345
  if (!editorRun.ok || !editorRun.result) {
173969
174346
  sessionLog(args.parentSessionId, "historian two-pass: editor call failed", {
@@ -173990,9 +174367,29 @@ async function runHistorianPrompt(args) {
173990
174367
  dumpLabel,
173991
174368
  modelOverride,
173992
174369
  agentId = HISTORIAN_AGENT,
173993
- fallbackModels
174370
+ fallbackModels,
174371
+ subagentKind,
174372
+ parentInvocationId
173994
174373
  } = args;
173995
174374
  let agentSessionId = null;
174375
+ const startedAt = Date.now();
174376
+ let invocationRecorded = false;
174377
+ const recordInvocation = (params) => {
174378
+ if (invocationRecorded)
174379
+ return null;
174380
+ invocationRecorded = true;
174381
+ return recordChildInvocation({
174382
+ db: openDatabase(),
174383
+ parentSessionId,
174384
+ harness: "opencode",
174385
+ subagent: agentId === HISTORIAN_EDITOR_AGENT ? "historian_editor" : subagentKind ?? "historian",
174386
+ startedAt,
174387
+ status: params.status,
174388
+ messages: params.messages,
174389
+ error: params.error,
174390
+ parentInvocationId: agentId === HISTORIAN_EDITOR_AGENT ? parentInvocationId ?? null : null
174391
+ });
174392
+ };
173996
174393
  try {
173997
174394
  sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
173998
174395
  const createResponse = await client.session.create({
@@ -174005,6 +174402,10 @@ async function runHistorianPrompt(args) {
174005
174402
  const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
174006
174403
  agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
174007
174404
  if (!agentSessionId) {
174405
+ recordInvocation({
174406
+ status: "failed",
174407
+ error: "Historian could not create its child session."
174408
+ });
174008
174409
  return { ok: false, error: "Historian could not create its child session." };
174009
174410
  }
174010
174411
  for (let retryIndex = 0;retryIndex <= MAX_HISTORIAN_RETRIES; retryIndex += 1) {
@@ -174043,15 +174444,21 @@ async function runHistorianPrompt(args) {
174043
174444
  const messages = normalizeSDKResponse(messagesResponse, [], {
174044
174445
  preferResponseOnMissingData: true
174045
174446
  });
174447
+ const invocationId = recordInvocation({ status: "completed", messages });
174046
174448
  const result = extractLatestAssistantText(messages);
174047
174449
  if (!result) {
174048
- return { ok: false, error: "Historian returned no assistant output." };
174450
+ return {
174451
+ ok: false,
174452
+ error: "Historian returned no assistant output.",
174453
+ invocationId: invocationId ?? undefined
174454
+ };
174049
174455
  }
174050
174456
  const dumpPath = dumpHistorianResponse(parentSessionId, sessionDirectory, dumpLabel ?? "historian-response", result);
174051
- return { ok: true, result, dumpPath };
174457
+ return { ok: true, result, dumpPath, invocationId: invocationId ?? undefined };
174052
174458
  } catch (modelError) {
174053
174459
  const desc = describeError(modelError);
174054
174460
  sessionLog(parentSessionId, `historian prompt failed: ${desc.brief} promptLength=${prompt.length}${desc.stackHead ? ` stackHead="${desc.stackHead}"` : ""}`);
174461
+ recordInvocation({ status: "failed", error: modelError });
174055
174462
  return {
174056
174463
  ok: false,
174057
174464
  error: `Historian failed while processing this session: ${desc.brief}`
@@ -174712,6 +175119,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
174712
175119
  fallbackModelId: deps.fallbackModelId,
174713
175120
  fallbackModels: deps.fallbackModels,
174714
175121
  twoPass: deps.historianTwoPass,
175122
+ subagentKind: "recomp",
174715
175123
  callbacks: {
174716
175124
  onRepairRetry: async (error51) => {
174717
175125
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp
@@ -175078,6 +175486,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
175078
175486
  timeoutMs: deps.historianTimeoutMs,
175079
175487
  thinkingLevel: deps.historianThinkingLevel,
175080
175488
  directory: ctx.cwd,
175489
+ accountingSessionId: sessionId,
175081
175490
  notify: (text) => {
175082
175491
  sendCtxStatusMessage(pi, {
175083
175492
  title: "/ctx-recomp",
@@ -175264,7 +175673,9 @@ function createPiRecompClient(args) {
175264
175673
  fallbackModels: args.fallbackModels,
175265
175674
  timeoutMs: args.timeoutMs,
175266
175675
  cwd: args.directory,
175267
- thinkingLevel: args.thinkingLevel
175676
+ thinkingLevel: args.thinkingLevel,
175677
+ accountingSessionId: args.accountingSessionId,
175678
+ accountingSubagent: "recomp"
175268
175679
  });
175269
175680
  if (!result.ok) {
175270
175681
  throw new Error(`Pi recomp historian failed (${result.reason}): ${result.error}`);
@@ -175460,7 +175871,7 @@ function formatThresholdPercent(value) {
175460
175871
  // package.json
175461
175872
  var package_default = {
175462
175873
  name: "@cortexkit/pi-magic-context",
175463
- version: "0.21.7",
175874
+ version: "0.21.8",
175464
175875
  type: "module",
175465
175876
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
175466
175877
  main: "dist/index.js",
@@ -8925,6 +8925,49 @@ var MIGRATIONS = [
8925
8925
  ON compartment_state_lease(expires_at);
8926
8926
  `);
8927
8927
  }
8928
+ },
8929
+ {
8930
+ version: 20,
8931
+ description: "Add subagent invocation token accounting",
8932
+ up: (db) => {
8933
+ db.exec(`
8934
+ CREATE TABLE IF NOT EXISTS subagent_invocations (
8935
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8936
+ session_id TEXT NOT NULL,
8937
+ harness TEXT NOT NULL,
8938
+ subagent TEXT NOT NULL,
8939
+ task TEXT,
8940
+ provider_id TEXT,
8941
+ model_id TEXT,
8942
+ started_at INTEGER NOT NULL,
8943
+ ended_at INTEGER,
8944
+ status TEXT NOT NULL,
8945
+ input_tokens INTEGER NOT NULL DEFAULT 0,
8946
+ output_tokens INTEGER NOT NULL DEFAULT 0,
8947
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
8948
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
8949
+ error TEXT,
8950
+ parent_invocation_id INTEGER
8951
+ );
8952
+ CREATE INDEX IF NOT EXISTS idx_sai_session_started
8953
+ ON subagent_invocations(session_id, started_at DESC);
8954
+ CREATE INDEX IF NOT EXISTS idx_sai_subagent
8955
+ ON subagent_invocations(subagent, started_at DESC);
8956
+ `);
8957
+ }
8958
+ },
8959
+ {
8960
+ version: 21,
8961
+ description: "Add session lifetime work metrics",
8962
+ up: (db) => {
8963
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
8964
+ if (!cols.some((c) => c.name === "new_work_tokens")) {
8965
+ db.exec("ALTER TABLE session_meta ADD COLUMN new_work_tokens INTEGER NOT NULL DEFAULT 0");
8966
+ }
8967
+ if (!cols.some((c) => c.name === "total_input_tokens")) {
8968
+ db.exec("ALTER TABLE session_meta ADD COLUMN total_input_tokens INTEGER NOT NULL DEFAULT 0");
8969
+ }
8970
+ }
8928
8971
  }
8929
8972
  ];
8930
8973
  function ensureMigrationsTable(db) {
@@ -142177,12 +142220,37 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
142177
142220
  -- is a valid JSON blob written via setPendingPiCompactionMarkerState.
142178
142221
  -- Excluded from healNullTextColumns.
142179
142222
  pending_pi_compaction_marker_state TEXT,
142223
+ new_work_tokens INTEGER NOT NULL DEFAULT 0,
142224
+ total_input_tokens INTEGER NOT NULL DEFAULT 0,
142180
142225
  -- deferred_execute_state: intentionally NULLABLE without a default.
142181
142226
  -- Absence is SQL NULL; presence is a JSON blob written via
142182
142227
  -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
142183
142228
  deferred_execute_state TEXT
142184
142229
  );
142185
142230
 
142231
+ CREATE TABLE IF NOT EXISTS subagent_invocations (
142232
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
142233
+ session_id TEXT NOT NULL,
142234
+ harness TEXT NOT NULL,
142235
+ subagent TEXT NOT NULL,
142236
+ task TEXT,
142237
+ provider_id TEXT,
142238
+ model_id TEXT,
142239
+ started_at INTEGER NOT NULL,
142240
+ ended_at INTEGER,
142241
+ status TEXT NOT NULL,
142242
+ input_tokens INTEGER NOT NULL DEFAULT 0,
142243
+ output_tokens INTEGER NOT NULL DEFAULT 0,
142244
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
142245
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
142246
+ error TEXT,
142247
+ parent_invocation_id INTEGER
142248
+ );
142249
+ CREATE INDEX IF NOT EXISTS idx_sai_session_started
142250
+ ON subagent_invocations(session_id, started_at DESC);
142251
+ CREATE INDEX IF NOT EXISTS idx_sai_subagent
142252
+ ON subagent_invocations(subagent, started_at DESC);
142253
+
142186
142254
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
142187
142255
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
142188
142256
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -142274,6 +142342,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
142274
142342
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
142275
142343
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
142276
142344
  ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
142345
+ ensureColumn(db, "session_meta", "new_work_tokens", "INTEGER NOT NULL DEFAULT 0");
142346
+ ensureColumn(db, "session_meta", "total_input_tokens", "INTEGER NOT NULL DEFAULT 0");
142277
142347
  ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
142278
142348
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
142279
142349
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -142337,7 +142407,9 @@ function healNullIntegerColumns(db) {
142337
142407
  ["tool_call_tokens", 0],
142338
142408
  ["note_nudge_trigger_pending", 0],
142339
142409
  ["observed_safe_input_tokens", 0],
142340
- ["cache_alert_sent", 0]
142410
+ ["cache_alert_sent", 0],
142411
+ ["new_work_tokens", 0],
142412
+ ["total_input_tokens", 0]
142341
142413
  ];
142342
142414
  for (const [column, fallback] of columns) {
142343
142415
  try {
@@ -165296,6 +165368,12 @@ function clearDeferredExecutePendingIfMatches(db, sessionId, expected) {
165296
165368
  WHERE session_id = ? AND deferred_execute_state = ?`).run(sessionId, expectedBlob);
165297
165369
  return result.changes > 0;
165298
165370
  }
165371
+ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
165372
+ ensureSessionMetaRow(db, sessionId);
165373
+ db.prepare(`UPDATE session_meta
165374
+ SET new_work_tokens = ?, total_input_tokens = ?
165375
+ WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
165376
+ }
165299
165377
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
165300
165378
  init_logger();
165301
165379
  await init_read_session_db();
@@ -165611,6 +165689,19 @@ function getSourceContents(db, sessionId, tagIds) {
165611
165689
  }
165612
165690
  return sources;
165613
165691
  }
165692
+ // ../plugin/src/features/magic-context/storage-subagent-invocations.ts
165693
+ function clampToken(value) {
165694
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
165695
+ }
165696
+ function recordSubagentInvocation(db, input) {
165697
+ const result = db.prepare(`INSERT INTO subagent_invocations (
165698
+ session_id, harness, subagent, task, provider_id, model_id,
165699
+ started_at, ended_at, status,
165700
+ input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
165701
+ error, parent_invocation_id
165702
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.sessionId, input.harness, input.subagent, input.task ?? null, input.providerId ?? null, input.modelId ?? null, input.startedAt, input.endedAt, input.status, clampToken(input.inputTokens), clampToken(input.outputTokens), clampToken(input.cacheReadTokens), clampToken(input.cacheWriteTokens), input.error ?? null, input.parentInvocationId ?? null);
165703
+ return Number(result.lastInsertRowid);
165704
+ }
165614
165705
  // ../plugin/src/features/magic-context/storage-tags.ts
165615
165706
  var insertTagStatements = new WeakMap;
165616
165707
  var updateTagStatusStatements = new WeakMap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/pi-magic-context",
3
- "version": "0.21.7",
3
+ "version": "0.21.8",
4
4
  "type": "module",
5
5
  "description": "Pi coding agent extension for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",