@ponythewhite/base-context 1.0.10 → 1.0.11

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 (67) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/base-context-runtime/pyproject.toml +1 -1
  3. package/dist/base-context-runtime/uv.lock +1 -1
  4. package/dist/build-info.json +1 -1
  5. package/dist/bundle/{bundled-modules-MX6YNAM2.js → bundled-modules-PEFT72QN.js} +2 -2
  6. package/dist/bundle/{chunk-UHLGPYMI.js → chunk-JZHGOOFM.js} +68 -18
  7. package/dist/bundle/{chunk-CDXQNS5P.js → chunk-TOOVREMP.js} +498 -82
  8. package/dist/bundle/{cli-main-D7W3WWCV.js → cli-main-BRXGYTGK.js} +2 -2
  9. package/dist/bundle/cli.js +1 -1
  10. package/dist/bundle/{main-5J3QBZ5Y.js → main-POUXGEL6.js} +2 -2
  11. package/dist/core/agent-session.d.ts +0 -1
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +85 -45
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/canonical-context.d.ts +19 -3
  16. package/dist/core/canonical-context.d.ts.map +1 -1
  17. package/dist/core/canonical-context.js +130 -9
  18. package/dist/core/canonical-context.js.map +1 -1
  19. package/dist/core/context-epoch-copy.d.ts +1 -1
  20. package/dist/core/context-epoch-copy.d.ts.map +1 -1
  21. package/dist/core/context-epoch-copy.js +18 -7
  22. package/dist/core/context-epoch-copy.js.map +1 -1
  23. package/dist/core/context-epoch.d.ts +4 -0
  24. package/dist/core/context-epoch.d.ts.map +1 -1
  25. package/dist/core/context-epoch.js +8 -1
  26. package/dist/core/context-epoch.js.map +1 -1
  27. package/dist/core/context-tree.d.ts +1 -1
  28. package/dist/core/context-tree.d.ts.map +1 -1
  29. package/dist/core/context-tree.js +98 -8
  30. package/dist/core/context-tree.js.map +1 -1
  31. package/dist/core/history-index-worker.d.ts.map +1 -1
  32. package/dist/core/history-index-worker.js +59 -6
  33. package/dist/core/history-index-worker.js.map +1 -1
  34. package/dist/core/history-index.d.ts +17 -0
  35. package/dist/core/history-index.d.ts.map +1 -1
  36. package/dist/core/history-index.js +22 -0
  37. package/dist/core/history-index.js.map +1 -1
  38. package/dist/core/inference-coordinator.d.ts.map +1 -1
  39. package/dist/core/inference-coordinator.js +5 -1
  40. package/dist/core/inference-coordinator.js.map +1 -1
  41. package/dist/core/request-view-selection.d.ts +2 -1
  42. package/dist/core/request-view-selection.d.ts.map +1 -1
  43. package/dist/core/request-view-selection.js +29 -2
  44. package/dist/core/request-view-selection.js.map +1 -1
  45. package/dist/core/session-fork-messages.d.ts +5 -0
  46. package/dist/core/session-fork-messages.d.ts.map +1 -1
  47. package/dist/core/session-fork-messages.js +44 -0
  48. package/dist/core/session-fork-messages.js.map +1 -1
  49. package/dist/core/session-history-index.d.ts +5 -0
  50. package/dist/core/session-history-index.d.ts.map +1 -1
  51. package/dist/core/session-history-index.js +7 -0
  52. package/dist/core/session-history-index.js.map +1 -1
  53. package/dist/core/session-manager.d.ts +2 -0
  54. package/dist/core/session-manager.d.ts.map +1 -1
  55. package/dist/core/session-manager.js +155 -13
  56. package/dist/core/session-manager.js.map +1 -1
  57. package/dist/core/session-tree.d.ts +9 -0
  58. package/dist/core/session-tree.d.ts.map +1 -1
  59. package/dist/core/session-tree.js +71 -11
  60. package/dist/core/session-tree.js.map +1 -1
  61. package/dist/modes/agent-connection/in-process-agent-connection.d.ts.map +1 -1
  62. package/dist/modes/agent-connection/in-process-agent-connection.js +4 -4
  63. package/dist/modes/agent-connection/in-process-agent-connection.js.map +1 -1
  64. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  65. package/dist/modes/daemon/daemon-mode.js +4 -4
  66. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  67. package/package.json +4 -4
@@ -927,6 +927,8 @@ const DEFAULT_MANAGER_HISTORY_LIMITS = {
927
927
  maxEntries: 16_384,
928
928
  maxSourceBytes: 64 * 1024 * 1024,
929
929
  };
930
+ // Selected forks bound compact original-reference metadata separately from selected payload/output limits.
931
+ const FORK_SOURCE_METADATA_MAX_BYTES = 64 * 1024 * 1024;
930
932
  async function readOwnedSessionHeader(snapshot) {
931
933
  const handle = await openFile(snapshot.journalPath, "r");
932
934
  let capturedHeader;
@@ -1476,6 +1478,20 @@ export class SessionManager {
1476
1478
  readSourceHistory(read) {
1477
1479
  return this._runHistoryRead(this._bindHistorySource((index, source, query) => createSessionHistoryReadScope(index, source, query, "source")), read);
1478
1480
  }
1481
+ /** Read the owner-maintained own-spend projection without hydrating the source. */
1482
+ async readOwnUsageSummary() {
1483
+ this._assertMutable();
1484
+ if (!this.supportsCapturedHistoryReads())
1485
+ throw new Error("Own usage projection requires an owned native source");
1486
+ const state = this.writeState;
1487
+ return this._enqueue(state, 0, async () => {
1488
+ await state.owner.flush();
1489
+ const snapshot = state.owner.getSnapshot();
1490
+ await this._acknowledgedHistory(state, snapshot);
1491
+ // Keep later appends queued: the catalog holds current totals, not historical versions.
1492
+ return state.history.currentSourceUsage(snapshot);
1493
+ });
1494
+ }
1479
1495
  /** Complete bounded structured task reduction on one captured native branch. */
1480
1496
  async readTaskState(limits = {}) {
1481
1497
  const captured = taskStateReadLimits(limits);
@@ -3330,6 +3346,11 @@ export class SessionManager {
3330
3346
  }
3331
3347
  async _captureForkInput(target, options) {
3332
3348
  const limits = { ...(options.limits ?? DEFAULT_MANAGER_HISTORY_LIMITS) };
3349
+ if (!Number.isSafeInteger(limits.maxEntries) ||
3350
+ limits.maxEntries <= 0 ||
3351
+ !Number.isSafeInteger(limits.maxSourceBytes) ||
3352
+ limits.maxSourceBytes <= 0)
3353
+ throw new Error("Invalid fork source limits");
3333
3354
  const sourceFile = this.sessionFile;
3334
3355
  const header = JSON.parse(stringifyBoundedJson(this.getHeader(), limits.maxSourceBytes));
3335
3356
  const settings = {
@@ -3448,34 +3469,155 @@ export class SessionManager {
3448
3469
  if (headerBytes >= limits.maxSourceBytes)
3449
3470
  throw new Error("Fork source byte budget exceeded");
3450
3471
  return this.readSourceHistory(async (history) => {
3451
- const materialized = await history.materialize({
3452
- ...limits,
3453
- maxSourceBytes: limits.maxSourceBytes - headerBytes,
3454
- });
3455
- const entries = materialized.entries.map(({ entry, source }) => withEntryRetention(entry, source.retention, source.qualification));
3456
- const sourceEntries = materialized.entries.map(({ entry, source }) => captureEpochCopyEntry(entry, source));
3457
- const hasEpoch = sourceEntries.some((entry) => entry.kind === "compaction" && entry.qualification === "native-context-epoch");
3472
+ const metadata = new Map();
3473
+ let metadataBytes = 0;
3474
+ let after = 0;
3475
+ for (;;) {
3476
+ const page = await history.page(after, 128);
3477
+ if (page.indexedThrough < history.source.sourceSequence)
3478
+ throw new Error("Captured fork history has incomplete index coverage");
3479
+ for (const reference of page.events) {
3480
+ // Prefix/revision remapping needs original refs, not text previews or unselected bodies.
3481
+ const source = {
3482
+ id: reference.id,
3483
+ parentId: reference.parentId,
3484
+ kind: reference.kind,
3485
+ sequence: reference.sequence,
3486
+ revision: reference.revision,
3487
+ locator: { ...reference.locator },
3488
+ retention: reference.retention,
3489
+ qualification: reference.qualification,
3490
+ };
3491
+ metadataBytes += Buffer.byteLength(JSON.stringify(source)) + 1;
3492
+ if (metadataBytes > FORK_SOURCE_METADATA_MAX_BYTES)
3493
+ throw new Error("Fork source metadata byte budget exceeded");
3494
+ metadata.set(source.id, source);
3495
+ }
3496
+ if (page.nextAfter === null)
3497
+ break;
3498
+ after = page.nextAfter;
3499
+ }
3500
+ const hasEpoch = [...metadata.values()].some((entry) => entry.kind === "compaction" && entry.qualification === "native-context-epoch");
3501
+ const loaded = new Map();
3502
+ let sourceBytes = headerBytes;
3503
+ const load = async (id) => {
3504
+ const existing = loaded.get(id);
3505
+ if (existing)
3506
+ return existing;
3507
+ const reference = metadata.get(id);
3508
+ if (!reference)
3509
+ throw new Error("Fork selected source is unavailable");
3510
+ if (loaded.size >= limits.maxEntries)
3511
+ throw new Error("Fork entry budget exceeded");
3512
+ const remaining = limits.maxSourceBytes - sourceBytes;
3513
+ if (reference.locator.length > remaining)
3514
+ throw new Error("Fork source byte budget exceeded");
3515
+ const hydrated = await history.hydrateEntry(id, remaining);
3516
+ if (!hydrated)
3517
+ throw new Error("Fork selected source is unavailable");
3518
+ const entry = withEntryRetention(hydrated.entry, hydrated.source.retention, hydrated.source.qualification);
3519
+ loaded.set(id, entry);
3520
+ metadata.set(id, captureEpochCopyEntry(entry, hydrated.source));
3521
+ sourceBytes += reference.locator.length;
3522
+ return entry;
3523
+ };
3524
+ if ("entryId" in target && !metadata.has(target.entryId))
3525
+ throw new Error("Invalid entry ID for forking");
3526
+ const selected = "entryId" in target ? await load(target.entryId) : undefined;
3527
+ if ("entryId" in target &&
3528
+ target.position === "before" &&
3529
+ (selected?.type !== "message" || selected.message.role !== "user"))
3530
+ throw new Error("Invalid entry ID for forking");
3531
+ const leafId = "entryId" in target ? (target.position === "at" ? selected.id : selected.parentId) : target.leafId;
3532
+ const path = [];
3533
+ let page = await history.parentPathFrom(leafId);
3534
+ for (;;) {
3535
+ if (page.totalEntries > limits.maxEntries)
3536
+ throw new Error("Fork entry budget exceeded");
3537
+ for (const reference of page.events)
3538
+ path.push(await load(reference.id));
3539
+ if (!page.nextCursor)
3540
+ break;
3541
+ page = await history.parentPathFrom(leafId, { cursor: page.nextCursor });
3542
+ }
3543
+ const copied = path.filter((entry) => entry.type !== "label");
3544
+ const ids = new Set(copied.map((entry) => entry.id));
3545
+ if (hasEpoch) {
3546
+ for (const entry of copied) {
3547
+ if (entry.type !== "message")
3548
+ continue;
3549
+ if (entry.execution && "invocationId" in entry.execution)
3550
+ ids.add(entry.execution.invocationId);
3551
+ const updateTarget = entry.message.role === "assistant"
3552
+ ? { kind: "assistant-usage", targetId: entry.id }
3553
+ : entry.message.role === "toolResult"
3554
+ ? { kind: "ipython-sent-message", toolCallId: entry.message.toolCallId }
3555
+ : undefined;
3556
+ if (!updateTarget)
3557
+ continue;
3558
+ let after = 0;
3559
+ for (;;) {
3560
+ const updates = await history.sourceContextUpdates(updateTarget, after);
3561
+ if (updates.indexedThrough < history.source.sourceSequence)
3562
+ throw new Error("Captured fork updates have incomplete index coverage");
3563
+ for (const reference of updates.events)
3564
+ ids.add(reference.id);
3565
+ if (updates.nextAfter === null)
3566
+ break;
3567
+ after = updates.nextAfter;
3568
+ }
3569
+ }
3570
+ // Keep every actual related ancestor, including private intent/request rows when necessary.
3571
+ for (const id of ids) {
3572
+ const reference = metadata.get(id);
3573
+ if (!reference)
3574
+ throw new Error("Fork related source is unavailable");
3575
+ if (reference.parentId !== null)
3576
+ ids.add(reference.parentId);
3577
+ }
3578
+ for (const id of ids)
3579
+ await load(id);
3580
+ }
3581
+ else {
3582
+ // Preserve the existing non-epoch copy's resolved whole-source assistant usage overlay.
3583
+ for (const entry of copied) {
3584
+ if (entry.type !== "message" || entry.message.role !== "assistant")
3585
+ continue;
3586
+ const latest = await history.sourceAssistantUsage(entry.id);
3587
+ if (latest)
3588
+ await load(latest.id);
3589
+ }
3590
+ }
3591
+ for (const id of ids) {
3592
+ const label = await history.sourceLabel(id);
3593
+ if (label)
3594
+ await load(label.id);
3595
+ }
3596
+ const entries = [...loaded.values()].sort((left, right) => metadata.get(left.id).sequence - metadata.get(right.id).sequence);
3458
3597
  if (!hasEpoch)
3459
3598
  applyChildUsageAttributions(entries);
3460
- return finish(entries, history, hasEpoch
3599
+ const epochCopy = hasEpoch
3461
3600
  ? {
3462
3601
  sessionId: history.source.sessionId,
3463
3602
  sessionFile: history.source.sessionFile,
3464
3603
  through: history.source.sourceSequence,
3465
- entries: sourceEntries,
3604
+ entries: [...metadata.values()],
3466
3605
  retained: false,
3467
3606
  }
3468
- : undefined);
3607
+ : undefined;
3608
+ if (epochCopy)
3609
+ stringifyBoundedJson(epochCopy, FORK_SOURCE_METADATA_MAX_BYTES);
3610
+ return finish(entries, history, epochCopy);
3469
3611
  });
3470
3612
  }
3471
- async _rebuildCopiedEpoch(copied, limits) {
3613
+ async _rebuildCopiedEpoch(copied, limits, metadataMaxBytes = limits.maxSourceBytes) {
3472
3614
  if (!copied.entries.some((entry) => entry.kind === "compaction" && entry.qualification === "native-context-epoch"))
3473
3615
  return;
3474
3616
  if (!this.supportsCapturedHistoryReads())
3475
3617
  throw new Error("Copied context epochs require an owned persistent source");
3476
3618
  const sink = this.bindCompactionSink(limits);
3477
3619
  await this._runHistoryRead({ ...sink, readHistory: (read) => sink[readCopiedEpochSource](read) }, async (history) => {
3478
- const rebuilt = await rebuildCopiedContextEpoch(history, copied, limits);
3620
+ const rebuilt = await rebuildCopiedContextEpoch(history, copied, limits, metadataMaxBytes);
3479
3621
  if (rebuilt)
3480
3622
  await sink[appendContextEpoch](rebuilt.checkpoint, rebuilt.tokensBefore, rebuilt.summary);
3481
3623
  });
@@ -3512,7 +3654,7 @@ export class SessionManager {
3512
3654
  if (input.epochCopy) {
3513
3655
  try {
3514
3656
  await next.branchTo(input.leafId ?? null);
3515
- await next._rebuildCopiedEpoch(input.epochCopy, input.limits);
3657
+ await next._rebuildCopiedEpoch(input.epochCopy, input.limits, FORK_SOURCE_METADATA_MAX_BYTES);
3516
3658
  }
3517
3659
  catch (error) {
3518
3660
  try {