@theokit/sdk 4.0.2 → 4.1.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.
@@ -626,6 +626,84 @@ declare class Skill {
626
626
  static create(spec: CreateSkillSpec): InlineSkill;
627
627
  }
628
628
 
629
+ /** One transcript record (one JSONL line). `message` absent on `system` (compact_boundary) records. */
630
+ interface SessionRecord {
631
+ type: "user" | "assistant" | "system";
632
+ uuid: string;
633
+ parentUuid: string | null;
634
+ sessionId: string;
635
+ timestamp: string;
636
+ isSidechain?: boolean;
637
+ userType?: string;
638
+ cwd?: string;
639
+ version?: string;
640
+ subtype?: string;
641
+ compactMetadata?: {
642
+ preTokens: number;
643
+ trigger: string;
644
+ };
645
+ message?: Record<string, unknown>;
646
+ }
647
+
648
+ /**
649
+ * SE41 — the pluggable `SessionStore` seam over the NATIVE session transcript.
650
+ *
651
+ * A minimal, two-method port so an external store (Postgres / Redis / KV /
652
+ * durable object) can be the **primary store AND resume source** — the
653
+ * serverless (ephemeral FS) and multi-host / multi-pod use case that SE40
654
+ * dropped when it removed the `ConversationStorageAdapter`. This is deliberately
655
+ * NOT that removed ~10-method adapter: the seam is JUST record read/append over
656
+ * the native {@link SessionRecord} shape (no getMessages / getSessionMeta /
657
+ * delete / objective methods).
658
+ *
659
+ * The SDK ships a real default implementation, `FsSessionStore`, that reads and
660
+ * append-writes the native Claude-shaped `.jsonl` transcript — omitting
661
+ * `local.sessionStore` yields byte-identical current behavior (back-compat, zero
662
+ * consumer change). Injected via `local.sessionStore` for external stores.
663
+ *
664
+ * Consistency contract: `appendRecords` is append-only and ordering-preserving.
665
+ * The FS default serializes appends per agent with a cross-process file lock;
666
+ * external implementations own (and MUST document) their own concurrency
667
+ * guarantees for two hosts appending to the same `agentId`.
668
+ *
669
+ * @public
670
+ */
671
+
672
+ /**
673
+ * The pluggable session-store seam. Exactly two methods over the native
674
+ * {@link SessionRecord} shape.
675
+ *
676
+ * @public
677
+ */
678
+ interface SessionStore {
679
+ /**
680
+ * Return every persisted record for `agentId`, in append order. A session
681
+ * that was never written MUST resolve to `[]` (not throw) — a fresh agent has
682
+ * no history. The SDK reconstructs the resumable `LlmMessage[]` from these
683
+ * records via the native DAG reader, so the shape MUST be the exact
684
+ * {@link SessionRecord} the SDK writes.
685
+ *
686
+ * A store that cannot READ (e.g. the backing DB is unreachable on resume)
687
+ * MUST throw a typed error rather than silently returning `[]` — a silent
688
+ * empty read would masquerade as "no history" and drop the conversation.
689
+ */
690
+ readRecords(agentId: string): Promise<SessionRecord[]>;
691
+ /**
692
+ * Append `records` (the new-turn delta) to `agentId`'s session, append-only.
693
+ * MUST preserve order and MUST NOT drop or rewrite prior records — the native
694
+ * format is an append-only `parentUuid` DAG (compaction is a new-root
695
+ * `compact_boundary` record, still an append).
696
+ *
697
+ * Note on the write path: per-turn persistence is fire-and-forget so `send()`
698
+ * is never blocked by store I/O — an `appendRecords` rejection is logged to
699
+ * stderr, NOT thrown to the caller (best-effort write). An external store that
700
+ * must guarantee durability should make `appendRecords` resilient (retry /
701
+ * durable write) internally. This differs from {@link SessionStore.readRecords},
702
+ * which MUST throw on failure (a resume cannot proceed on a silent partial history).
703
+ */
704
+ appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void>;
705
+ }
706
+
629
707
  /**
630
708
  * Context manager backend.
631
709
  *
@@ -989,6 +1067,16 @@ interface LocalOptions {
989
1067
  * Set to `~/.claude` to write sessions the Claude Code CLI can `--continue`.
990
1068
  */
991
1069
  baseDir?: string;
1070
+ /**
1071
+ * SE41 — inject an external {@link import("./session-store.js").SessionStore}
1072
+ * (Postgres / Redis / KV / durable object) as the PRIMARY session store and
1073
+ * resume source. Omit for the default FS transcript store (`baseDir` above) —
1074
+ * byte-identical to SE40. Use this for serverless (ephemeral FS) or multi-host /
1075
+ * multi-pod deployments where a resumed agent must read its history from a shared
1076
+ * store instead of local disk. The records stay the native Claude-shaped shape,
1077
+ * so `--continue` interop is preserved (a store may also mirror to `~/.claude`).
1078
+ */
1079
+ sessionStore?: SessionStore;
992
1080
  }
993
1081
  /**
994
1082
  * Repo to clone into a cloud agent's VM.
@@ -2442,4 +2530,4 @@ declare class Cron {
2442
2530
  static status(_options?: CronStartOptions): Promise<CronSchedulerStatus>;
2443
2531
  }
2444
2532
 
2445
- export { type GoalOptions as $, type AgentOptions as A, type BudgetTracker as B, type CloudOptions as C, type ContextBudget as D, type ContextManagerKind as E, type ContextSnapshot as F, type GetAgentOptions as G, type ContextSource as H, type InlineSkill as I, type ContextSourceStatus as J, type CreateSkillSpec as K, type LocalOptions as L, type MemorySettings as M, Cron as N, type CronCreateOptions as O, type ProviderRoutingSettings as P, type CronGetOptions as Q, type CronJob as R, type SystemPromptResolver as S, type CronJobStatus as T, type CronListOptions as U, type CronOperationOptions as V, type CronRunOptions as W, type CronRuntime as X, type CronSchedulerStatus as Y, type CronStartOptions as Z, type GoalEvent as _, type AgentDefinition as a, type GoalResult as a0, type HookName as a1, type InvalidateCacheOptions as a2, type MemoryAdapter as a3, type MemoryAdapterCapabilities as a4, type MemoryContext as a5, type MemoryFact as a6, type MemoryProviderHandle as a7, type MemoryProviderInitOptions as a8, type MemoryRevision as a9, type SystemPromptSkillRef as aA, type TelemetrySettings as aB, type MemoryToolSchema as aa, type MemoryTurnMessage as ab, type PersonalityPreset as ac, type PluginContext as ad, type PostAssistantReplyContext as ae, type PreToolCallContext as af, type PreUserSendContext as ag, type PreUserSendResult as ah, type ProviderCapability as ai, type ProviderRoute as aj, type RecordSessionSummaryArgs as ak, type ResolvedProviderRoute as al, type RunUntilIterator as am, type SDKAgentPlugins as an, type SDKAgentSkillDetail as ao, type SDKAgentSkills as ap, type SDKArtifact as aq, type SDKContextManager as ar, type SDKPluginMetadata as as, type SDKProvidersManager as at, type SettingSource as au, Skill as av, type SkillsResolver as aw, type SkillsResolverContext as ax, type SystemPromptContext as ay, type SystemPromptMemoryFact as az, type ContextSettings as b, type PluginsSettings as c, type SkillsSettings as d, type SDKAgent as e, type ListAgentsOptions as f, type ListResult as g, type SDKAgentInfo as h, type ListRunsOptions as i, type GetRunOptions as j, type AgentOperationOptions as k, type ProviderProfile as l, Plugin as m, type MemoryProvider as n, type MemoryId as o, type PreToolCallDecision as p, type StepResult as q, type SDKProvider as r, type ActiveMemoryPassArgs as s, type ActiveMemoryPassResult as t, type AgentMemory as u, type BudgetCheck as v, type BudgetTotal as w, type BudgetUsageEvent as x, type CloudEnv as y, type CloudRepo as z };
2533
+ export { type GoalOptions as $, type AgentOptions as A, type BudgetTracker as B, type CloudOptions as C, type ContextBudget as D, type ContextManagerKind as E, type ContextSnapshot as F, type GetAgentOptions as G, type ContextSource as H, type InlineSkill as I, type ContextSourceStatus as J, type CreateSkillSpec as K, type LocalOptions as L, type MemorySettings as M, Cron as N, type CronCreateOptions as O, type ProviderRoutingSettings as P, type CronGetOptions as Q, type CronJob as R, type SystemPromptResolver as S, type CronJobStatus as T, type CronListOptions as U, type CronOperationOptions as V, type CronRunOptions as W, type CronRuntime as X, type CronSchedulerStatus as Y, type CronStartOptions as Z, type GoalEvent as _, type AgentDefinition as a, type GoalResult as a0, type HookName as a1, type InvalidateCacheOptions as a2, type MemoryAdapter as a3, type MemoryAdapterCapabilities as a4, type MemoryContext as a5, type MemoryFact as a6, type MemoryProviderHandle as a7, type MemoryProviderInitOptions as a8, type MemoryRevision as a9, type SystemPromptContext as aA, type SystemPromptMemoryFact as aB, type SystemPromptSkillRef as aC, type TelemetrySettings as aD, type MemoryToolSchema as aa, type MemoryTurnMessage as ab, type PersonalityPreset as ac, type PluginContext as ad, type PostAssistantReplyContext as ae, type PreToolCallContext as af, type PreUserSendContext as ag, type PreUserSendResult as ah, type ProviderCapability as ai, type ProviderRoute as aj, type RecordSessionSummaryArgs as ak, type ResolvedProviderRoute as al, type RunUntilIterator as am, type SDKAgentPlugins as an, type SDKAgentSkillDetail as ao, type SDKAgentSkills as ap, type SDKArtifact as aq, type SDKContextManager as ar, type SDKPluginMetadata as as, type SDKProvidersManager as at, type SessionRecord as au, type SessionStore as av, type SettingSource as aw, Skill as ax, type SkillsResolver as ay, type SkillsResolverContext as az, type ContextSettings as b, type PluginsSettings as c, type SkillsSettings as d, type SDKAgent as e, type ListAgentsOptions as f, type ListResult as g, type SDKAgentInfo as h, type ListRunsOptions as i, type GetRunOptions as j, type AgentOperationOptions as k, type ProviderProfile as l, Plugin as m, type MemoryProvider as n, type MemoryId as o, type PreToolCallDecision as p, type StepResult as q, type SDKProvider as r, type ActiveMemoryPassArgs as s, type ActiveMemoryPassResult as t, type AgentMemory as u, type BudgetCheck as v, type BudgetTotal as w, type BudgetUsageEvent as x, type CloudEnv as y, type CloudRepo as z };
@@ -626,6 +626,84 @@ declare class Skill {
626
626
  static create(spec: CreateSkillSpec): InlineSkill;
627
627
  }
628
628
 
629
+ /** One transcript record (one JSONL line). `message` absent on `system` (compact_boundary) records. */
630
+ interface SessionRecord {
631
+ type: "user" | "assistant" | "system";
632
+ uuid: string;
633
+ parentUuid: string | null;
634
+ sessionId: string;
635
+ timestamp: string;
636
+ isSidechain?: boolean;
637
+ userType?: string;
638
+ cwd?: string;
639
+ version?: string;
640
+ subtype?: string;
641
+ compactMetadata?: {
642
+ preTokens: number;
643
+ trigger: string;
644
+ };
645
+ message?: Record<string, unknown>;
646
+ }
647
+
648
+ /**
649
+ * SE41 — the pluggable `SessionStore` seam over the NATIVE session transcript.
650
+ *
651
+ * A minimal, two-method port so an external store (Postgres / Redis / KV /
652
+ * durable object) can be the **primary store AND resume source** — the
653
+ * serverless (ephemeral FS) and multi-host / multi-pod use case that SE40
654
+ * dropped when it removed the `ConversationStorageAdapter`. This is deliberately
655
+ * NOT that removed ~10-method adapter: the seam is JUST record read/append over
656
+ * the native {@link SessionRecord} shape (no getMessages / getSessionMeta /
657
+ * delete / objective methods).
658
+ *
659
+ * The SDK ships a real default implementation, `FsSessionStore`, that reads and
660
+ * append-writes the native Claude-shaped `.jsonl` transcript — omitting
661
+ * `local.sessionStore` yields byte-identical current behavior (back-compat, zero
662
+ * consumer change). Injected via `local.sessionStore` for external stores.
663
+ *
664
+ * Consistency contract: `appendRecords` is append-only and ordering-preserving.
665
+ * The FS default serializes appends per agent with a cross-process file lock;
666
+ * external implementations own (and MUST document) their own concurrency
667
+ * guarantees for two hosts appending to the same `agentId`.
668
+ *
669
+ * @public
670
+ */
671
+
672
+ /**
673
+ * The pluggable session-store seam. Exactly two methods over the native
674
+ * {@link SessionRecord} shape.
675
+ *
676
+ * @public
677
+ */
678
+ interface SessionStore {
679
+ /**
680
+ * Return every persisted record for `agentId`, in append order. A session
681
+ * that was never written MUST resolve to `[]` (not throw) — a fresh agent has
682
+ * no history. The SDK reconstructs the resumable `LlmMessage[]` from these
683
+ * records via the native DAG reader, so the shape MUST be the exact
684
+ * {@link SessionRecord} the SDK writes.
685
+ *
686
+ * A store that cannot READ (e.g. the backing DB is unreachable on resume)
687
+ * MUST throw a typed error rather than silently returning `[]` — a silent
688
+ * empty read would masquerade as "no history" and drop the conversation.
689
+ */
690
+ readRecords(agentId: string): Promise<SessionRecord[]>;
691
+ /**
692
+ * Append `records` (the new-turn delta) to `agentId`'s session, append-only.
693
+ * MUST preserve order and MUST NOT drop or rewrite prior records — the native
694
+ * format is an append-only `parentUuid` DAG (compaction is a new-root
695
+ * `compact_boundary` record, still an append).
696
+ *
697
+ * Note on the write path: per-turn persistence is fire-and-forget so `send()`
698
+ * is never blocked by store I/O — an `appendRecords` rejection is logged to
699
+ * stderr, NOT thrown to the caller (best-effort write). An external store that
700
+ * must guarantee durability should make `appendRecords` resilient (retry /
701
+ * durable write) internally. This differs from {@link SessionStore.readRecords},
702
+ * which MUST throw on failure (a resume cannot proceed on a silent partial history).
703
+ */
704
+ appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void>;
705
+ }
706
+
629
707
  /**
630
708
  * Context manager backend.
631
709
  *
@@ -989,6 +1067,16 @@ interface LocalOptions {
989
1067
  * Set to `~/.claude` to write sessions the Claude Code CLI can `--continue`.
990
1068
  */
991
1069
  baseDir?: string;
1070
+ /**
1071
+ * SE41 — inject an external {@link import("./session-store.js").SessionStore}
1072
+ * (Postgres / Redis / KV / durable object) as the PRIMARY session store and
1073
+ * resume source. Omit for the default FS transcript store (`baseDir` above) —
1074
+ * byte-identical to SE40. Use this for serverless (ephemeral FS) or multi-host /
1075
+ * multi-pod deployments where a resumed agent must read its history from a shared
1076
+ * store instead of local disk. The records stay the native Claude-shaped shape,
1077
+ * so `--continue` interop is preserved (a store may also mirror to `~/.claude`).
1078
+ */
1079
+ sessionStore?: SessionStore;
992
1080
  }
993
1081
  /**
994
1082
  * Repo to clone into a cloud agent's VM.
@@ -2442,4 +2530,4 @@ declare class Cron {
2442
2530
  static status(_options?: CronStartOptions): Promise<CronSchedulerStatus>;
2443
2531
  }
2444
2532
 
2445
- export { type GoalOptions as $, type AgentOptions as A, type BudgetTracker as B, type CloudOptions as C, type ContextBudget as D, type ContextManagerKind as E, type ContextSnapshot as F, type GetAgentOptions as G, type ContextSource as H, type InlineSkill as I, type ContextSourceStatus as J, type CreateSkillSpec as K, type LocalOptions as L, type MemorySettings as M, Cron as N, type CronCreateOptions as O, type ProviderRoutingSettings as P, type CronGetOptions as Q, type CronJob as R, type SystemPromptResolver as S, type CronJobStatus as T, type CronListOptions as U, type CronOperationOptions as V, type CronRunOptions as W, type CronRuntime as X, type CronSchedulerStatus as Y, type CronStartOptions as Z, type GoalEvent as _, type AgentDefinition as a, type GoalResult as a0, type HookName as a1, type InvalidateCacheOptions as a2, type MemoryAdapter as a3, type MemoryAdapterCapabilities as a4, type MemoryContext as a5, type MemoryFact as a6, type MemoryProviderHandle as a7, type MemoryProviderInitOptions as a8, type MemoryRevision as a9, type SystemPromptSkillRef as aA, type TelemetrySettings as aB, type MemoryToolSchema as aa, type MemoryTurnMessage as ab, type PersonalityPreset as ac, type PluginContext as ad, type PostAssistantReplyContext as ae, type PreToolCallContext as af, type PreUserSendContext as ag, type PreUserSendResult as ah, type ProviderCapability as ai, type ProviderRoute as aj, type RecordSessionSummaryArgs as ak, type ResolvedProviderRoute as al, type RunUntilIterator as am, type SDKAgentPlugins as an, type SDKAgentSkillDetail as ao, type SDKAgentSkills as ap, type SDKArtifact as aq, type SDKContextManager as ar, type SDKPluginMetadata as as, type SDKProvidersManager as at, type SettingSource as au, Skill as av, type SkillsResolver as aw, type SkillsResolverContext as ax, type SystemPromptContext as ay, type SystemPromptMemoryFact as az, type ContextSettings as b, type PluginsSettings as c, type SkillsSettings as d, type SDKAgent as e, type ListAgentsOptions as f, type ListResult as g, type SDKAgentInfo as h, type ListRunsOptions as i, type GetRunOptions as j, type AgentOperationOptions as k, type ProviderProfile as l, Plugin as m, type MemoryProvider as n, type MemoryId as o, type PreToolCallDecision as p, type StepResult as q, type SDKProvider as r, type ActiveMemoryPassArgs as s, type ActiveMemoryPassResult as t, type AgentMemory as u, type BudgetCheck as v, type BudgetTotal as w, type BudgetUsageEvent as x, type CloudEnv as y, type CloudRepo as z };
2533
+ export { type GoalOptions as $, type AgentOptions as A, type BudgetTracker as B, type CloudOptions as C, type ContextBudget as D, type ContextManagerKind as E, type ContextSnapshot as F, type GetAgentOptions as G, type ContextSource as H, type InlineSkill as I, type ContextSourceStatus as J, type CreateSkillSpec as K, type LocalOptions as L, type MemorySettings as M, Cron as N, type CronCreateOptions as O, type ProviderRoutingSettings as P, type CronGetOptions as Q, type CronJob as R, type SystemPromptResolver as S, type CronJobStatus as T, type CronListOptions as U, type CronOperationOptions as V, type CronRunOptions as W, type CronRuntime as X, type CronSchedulerStatus as Y, type CronStartOptions as Z, type GoalEvent as _, type AgentDefinition as a, type GoalResult as a0, type HookName as a1, type InvalidateCacheOptions as a2, type MemoryAdapter as a3, type MemoryAdapterCapabilities as a4, type MemoryContext as a5, type MemoryFact as a6, type MemoryProviderHandle as a7, type MemoryProviderInitOptions as a8, type MemoryRevision as a9, type SystemPromptContext as aA, type SystemPromptMemoryFact as aB, type SystemPromptSkillRef as aC, type TelemetrySettings as aD, type MemoryToolSchema as aa, type MemoryTurnMessage as ab, type PersonalityPreset as ac, type PluginContext as ad, type PostAssistantReplyContext as ae, type PreToolCallContext as af, type PreUserSendContext as ag, type PreUserSendResult as ah, type ProviderCapability as ai, type ProviderRoute as aj, type RecordSessionSummaryArgs as ak, type ResolvedProviderRoute as al, type RunUntilIterator as am, type SDKAgentPlugins as an, type SDKAgentSkillDetail as ao, type SDKAgentSkills as ap, type SDKArtifact as aq, type SDKContextManager as ar, type SDKPluginMetadata as as, type SDKProvidersManager as at, type SessionRecord as au, type SessionStore as av, type SettingSource as aw, Skill as ax, type SkillsResolver as ay, type SkillsResolverContext as az, type ContextSettings as b, type PluginsSettings as c, type SkillsSettings as d, type SDKAgent as e, type ListAgentsOptions as f, type ListResult as g, type SDKAgentInfo as h, type ListRunsOptions as i, type GetRunOptions as j, type AgentOperationOptions as k, type ProviderProfile as l, Plugin as m, type MemoryProvider as n, type MemoryId as o, type PreToolCallDecision as p, type StepResult as q, type SDKProvider as r, type ActiveMemoryPassArgs as s, type ActiveMemoryPassResult as t, type AgentMemory as u, type BudgetCheck as v, type BudgetTotal as w, type BudgetUsageEvent as x, type CloudEnv as y, type CloudRepo as z };
package/dist/cron.cjs CHANGED
@@ -3510,6 +3510,72 @@ var init_cloud_tool_parity = __esm({
3510
3510
  init_errors();
3511
3511
  }
3512
3512
  });
3513
+
3514
+ // src/internal/persistence/file-lock.ts
3515
+ async function getProperLockfile() {
3516
+ if (cached !== void 0) return cached;
3517
+ try {
3518
+ const mod = await import('proper-lockfile');
3519
+ if (!validateLockModule(mod)) {
3520
+ if (!warnedStructural) {
3521
+ warnedStructural = true;
3522
+ process.stderr.write(
3523
+ "[theokit-sdk] proper-lockfile: imported module does NOT expose the expected `lock`/`unlock` API surface. This may indicate a supply-chain compromise or an incompatible major version. Falling back to in-process mutex (no cross-process safety). Reinstall with: pnpm add proper-lockfile@^11\n"
3524
+ );
3525
+ }
3526
+ cached = null;
3527
+ return cached;
3528
+ }
3529
+ cached = mod;
3530
+ } catch {
3531
+ cached = null;
3532
+ }
3533
+ return cached;
3534
+ }
3535
+ function validateLockModule(mod) {
3536
+ if (mod === null || mod === void 0 || typeof mod !== "object") return false;
3537
+ const m = mod;
3538
+ return typeof m.lock === "function" && typeof m.unlock === "function";
3539
+ }
3540
+ async function withFileLock(path, fn, options) {
3541
+ const lib = await getProperLockfile();
3542
+ if (lib === null) {
3543
+ if (!warnedMissing) {
3544
+ warnedMissing = true;
3545
+ process.stderr.write(
3546
+ "[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
3547
+ );
3548
+ }
3549
+ return withCwdMutex(`file-lock:${path}`, fn);
3550
+ }
3551
+ return withCwdMutex(`file-lock:${path}`, async () => {
3552
+ const release = await lib.lock(path, {
3553
+ // EC-1: companion lockfile, target path may not exist yet.
3554
+ lockfilePath: `${path}.lock`,
3555
+ realpath: false,
3556
+ stale: 3e4,
3557
+ retries: {
3558
+ retries: 5,
3559
+ factor: 1.5,
3560
+ minTimeout: 100,
3561
+ maxTimeout: 5e3
3562
+ }
3563
+ });
3564
+ try {
3565
+ return await fn();
3566
+ } finally {
3567
+ await release();
3568
+ }
3569
+ });
3570
+ }
3571
+ var cached, warnedMissing, warnedStructural;
3572
+ var init_file_lock = __esm({
3573
+ "src/internal/persistence/file-lock.ts"() {
3574
+ init_cwd_mutex();
3575
+ warnedMissing = false;
3576
+ warnedStructural = false;
3577
+ }
3578
+ });
3513
3579
  function encodeProjectDir(cwd) {
3514
3580
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
3515
3581
  }
@@ -3736,70 +3802,31 @@ var init_session_transcript = __esm({
3736
3802
  };
3737
3803
  }
3738
3804
  });
3739
-
3740
- // src/internal/persistence/file-lock.ts
3741
- async function getProperLockfile() {
3742
- if (cached !== void 0) return cached;
3743
- try {
3744
- const mod = await import('proper-lockfile');
3745
- if (!validateLockModule(mod)) {
3746
- if (!warnedStructural) {
3747
- warnedStructural = true;
3748
- process.stderr.write(
3749
- "[theokit-sdk] proper-lockfile: imported module does NOT expose the expected `lock`/`unlock` API surface. This may indicate a supply-chain compromise or an incompatible major version. Falling back to in-process mutex (no cross-process safety). Reinstall with: pnpm add proper-lockfile@^11\n"
3750
- );
3751
- }
3752
- cached = null;
3753
- return cached;
3754
- }
3755
- cached = mod;
3756
- } catch {
3757
- cached = null;
3758
- }
3759
- return cached;
3760
- }
3761
- function validateLockModule(mod) {
3762
- if (mod === null || mod === void 0 || typeof mod !== "object") return false;
3763
- const m = mod;
3764
- return typeof m.lock === "function" && typeof m.unlock === "function";
3765
- }
3766
- async function withFileLock(path, fn, options) {
3767
- const lib = await getProperLockfile();
3768
- if (lib === null) {
3769
- if (!warnedMissing) {
3770
- warnedMissing = true;
3771
- process.stderr.write(
3772
- "[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
3773
- );
3774
- }
3775
- return withCwdMutex(`file-lock:${path}`, fn);
3776
- }
3777
- return withCwdMutex(`file-lock:${path}`, async () => {
3778
- const release = await lib.lock(path, {
3779
- // EC-1: companion lockfile, target path may not exist yet.
3780
- lockfilePath: `${path}.lock`,
3781
- realpath: false,
3782
- stale: 3e4,
3783
- retries: {
3784
- retries: 5,
3785
- factor: 1.5,
3786
- minTimeout: 100,
3787
- maxTimeout: 5e3
3805
+ var FsSessionStore;
3806
+ var init_fs_session_store = __esm({
3807
+ "src/internal/persistence/fs-session-store.ts"() {
3808
+ init_file_lock();
3809
+ init_session_transcript();
3810
+ FsSessionStore = class {
3811
+ #baseDir;
3812
+ #cwd;
3813
+ constructor(options) {
3814
+ this.#baseDir = options.baseDir;
3815
+ this.#cwd = options.cwd;
3816
+ }
3817
+ async readRecords(agentId) {
3818
+ return readTranscript(transcriptPath(this.#baseDir, this.#cwd, agentId));
3819
+ }
3820
+ async appendRecords(agentId, records) {
3821
+ if (records.length === 0) return;
3822
+ const path$1 = transcriptPath(this.#baseDir, this.#cwd, agentId);
3823
+ await promises.mkdir(path.dirname(path$1), { recursive: true });
3824
+ await withFileLock(path$1, async () => {
3825
+ const prior = await readTranscript(path$1);
3826
+ await writeTranscript(path$1, [...prior, ...records]);
3827
+ });
3788
3828
  }
3789
- });
3790
- try {
3791
- return await fn();
3792
- } finally {
3793
- await release();
3794
- }
3795
- });
3796
- }
3797
- var cached, warnedMissing, warnedStructural;
3798
- var init_file_lock = __esm({
3799
- "src/internal/persistence/file-lock.ts"() {
3800
- init_cwd_mutex();
3801
- warnedMissing = false;
3802
- warnedStructural = false;
3829
+ };
3803
3830
  }
3804
3831
  });
3805
3832
  function getTheokitHome(cwd) {
@@ -5131,6 +5158,8 @@ var init_memory_path_selector = __esm({
5131
5158
  PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
5132
5159
  }
5133
5160
  });
5161
+
5162
+ // src/internal/runtime/session/agent-session-store.ts
5134
5163
  function seedTranscript(prior, opts) {
5135
5164
  return SessionTranscript.fromRecords(prior, opts);
5136
5165
  }
@@ -5168,8 +5197,8 @@ function appendConversation(transcript, conversation) {
5168
5197
  if (toolResults.length > 0) transcript.appendToolResults(toolResults);
5169
5198
  }
5170
5199
  }
5171
- async function readSessionMessages(baseDir, cwd, agentId) {
5172
- const records = await readTranscript(transcriptPath(baseDir, cwd, agentId));
5200
+ async function readSessionMessages(store, agentId) {
5201
+ const records = await store.readRecords(agentId);
5173
5202
  return reconstructMessages(records).map(narrowToSessionMessage);
5174
5203
  }
5175
5204
  function partToText(p) {
@@ -5186,37 +5215,31 @@ function narrowToSessionMessage(m) {
5186
5215
  const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
5187
5216
  return { role, text };
5188
5217
  }
5189
- async function persistTurn(loc, sessionId, turn) {
5190
- const path$1 = transcriptPath(loc.baseDir, loc.cwd, loc.agentId);
5191
- await promises.mkdir(path.dirname(path$1), { recursive: true });
5192
- await withFileLock(path$1, async () => {
5193
- const prior = await readTranscript(path$1);
5194
- const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5195
- transcript.appendUserTurn(turn.userText);
5196
- appendConversation(transcript, turn.conversation);
5197
- await writeTranscript(path$1, transcript.records());
5198
- });
5218
+ function deltaRecords(transcript, priorLength) {
5219
+ return transcript.records().slice(priorLength);
5199
5220
  }
5200
- async function appendCompactBoundaryRecord(loc, sessionId, meta) {
5201
- const path$1 = transcriptPath(loc.baseDir, loc.cwd, loc.agentId);
5202
- await promises.mkdir(path.dirname(path$1), { recursive: true });
5203
- await withFileLock(path$1, async () => {
5204
- const prior = await readTranscript(path$1);
5205
- const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5206
- transcript.appendCompactBoundary(meta);
5207
- await writeTranscript(path$1, transcript.records());
5208
- });
5221
+ async function persistTurn(store, loc, sessionId, turn) {
5222
+ const prior = await store.readRecords(loc.agentId);
5223
+ const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5224
+ transcript.appendUserTurn(turn.userText);
5225
+ appendConversation(transcript, turn.conversation);
5226
+ await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
5227
+ }
5228
+ async function appendCompactBoundaryRecord(store, loc, sessionId, meta) {
5229
+ const prior = await store.readRecords(loc.agentId);
5230
+ const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5231
+ transcript.appendCompactBoundary(meta);
5232
+ await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
5209
5233
  }
5210
5234
  var init_agent_session_store = __esm({
5211
5235
  "src/internal/runtime/session/agent-session-store.ts"() {
5212
- init_file_lock();
5213
5236
  init_session_transcript();
5214
5237
  }
5215
5238
  });
5216
5239
 
5217
5240
  // src/internal/runtime/session/agent-session.ts
5218
- function transcriptKey(baseDir, cwd, agentId) {
5219
- return `${baseDir}::${cwd}::${agentId}`;
5241
+ function transcriptKey(cwd, agentId) {
5242
+ return `${cwd}::${agentId}`;
5220
5243
  }
5221
5244
  function appendSessionMessage(agentId, message) {
5222
5245
  const existing = sessions.get(agentId) ?? [];
@@ -5226,15 +5249,15 @@ function appendSessionMessage(agentId, message) {
5226
5249
  function getSessionMessages(agentId) {
5227
5250
  return sessions.get(agentId) ?? [];
5228
5251
  }
5229
- function persistTurnToTranscript(loc, sessionId, turn, onCompact) {
5230
- const key = transcriptKey(loc.baseDir, loc.cwd, loc.agentId);
5252
+ function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
5253
+ const key = transcriptKey(loc.cwd, loc.agentId);
5231
5254
  const chained = (pendingWrites.get(key) ?? Promise.resolve()).then(async () => {
5232
5255
  try {
5233
- await persistTurn(loc, sessionId, turn);
5256
+ await persistTurn(store, loc, sessionId, turn);
5234
5257
  const count = (recordCounts.get(key) ?? 0) + 1;
5235
5258
  recordCounts.set(key, count);
5236
5259
  if (count % COMPACTION_CHECK_INTERVAL === 0) {
5237
- await appendCompactBoundaryRecord(loc, sessionId, {
5260
+ await appendCompactBoundaryRecord(store, loc, sessionId, {
5238
5261
  preTokens: 0,
5239
5262
  trigger: "auto"
5240
5263
  });
@@ -5257,10 +5280,10 @@ function persistTurnToTranscript(loc, sessionId, turn, onCompact) {
5257
5280
  );
5258
5281
  }
5259
5282
  async function hydrateSession(agentId, loc) {
5260
- const key = transcriptKey(loc.baseDir, loc.cwd, agentId);
5283
+ const key = transcriptKey(loc.cwd, agentId);
5261
5284
  if (hydratedKeys.has(key)) return;
5262
5285
  hydratedKeys.add(key);
5263
- const persisted = await readSessionMessages(loc.baseDir, loc.cwd, agentId);
5286
+ const persisted = await readSessionMessages(loc.store, agentId);
5264
5287
  if (persisted.length === 0) return;
5265
5288
  if (!sessions.has(agentId) || sessions.get(agentId)?.length === 0) {
5266
5289
  sessions.set(agentId, persisted);
@@ -5295,7 +5318,7 @@ async function runPostRunLifecycle(inputs) {
5295
5318
  userText,
5296
5319
  agentId,
5297
5320
  workspaceCwd,
5298
- baseDir,
5321
+ sessionStore,
5299
5322
  model,
5300
5323
  onRunEvent,
5301
5324
  hooksExecutor,
@@ -5314,7 +5337,8 @@ async function runPostRunLifecycle(inputs) {
5314
5337
  }
5315
5338
  const conversation = await safeConversation(run);
5316
5339
  persistTurnToTranscript(
5317
- { baseDir, cwd: workspaceCwd, agentId, model },
5340
+ sessionStore,
5341
+ { cwd: workspaceCwd, agentId, model },
5318
5342
  agentId,
5319
5343
  { userText, conversation },
5320
5344
  onRunEvent !== void 0 ? () => emitRunEvent(onRunEvent, { type: "compact_boundary", trigger: "auto" }) : void 0
@@ -17921,6 +17945,7 @@ var init_local_agent = __esm({
17921
17945
  init_errors();
17922
17946
  init_ids();
17923
17947
  init_cwd_mutex();
17948
+ init_fs_session_store();
17924
17949
  init_session_transcript();
17925
17950
  init_store();
17926
17951
  init_manager();
@@ -17964,6 +17989,12 @@ var init_local_agent = __esm({
17964
17989
  * `~/.theokit`; set `local.baseDir: "~/.claude"` for Claude Code CLI interop.
17965
17990
  */
17966
17991
  transcriptBaseDir;
17992
+ /**
17993
+ * SE41 — the session record store. Defaults to the FS transcript store (byte-
17994
+ * identical to SE40); `local.sessionStore` injects an external store (Postgres /
17995
+ * Redis / KV) so resume works on serverless (ephemeral FS) and multi-host.
17996
+ */
17997
+ sessionStore;
17967
17998
  /**
17968
17999
  * D319: lifecycle AbortController fired on `dispose()`. Composed with the
17969
18000
  * caller's `SendOptions.signal` via `anySignal` so the LLM `fetch()`
@@ -18006,6 +18037,7 @@ var init_local_agent = __esm({
18006
18037
  this.options = options;
18007
18038
  this.workspaceCwd = resolveCwd(options.local?.cwd);
18008
18039
  this.transcriptBaseDir = resolveBaseDir(options.local?.baseDir);
18040
+ this.sessionStore = options.local?.sessionStore ?? new FsSessionStore({ baseDir: this.transcriptBaseDir, cwd: this.workspaceCwd });
18009
18041
  this.settingSourcesIncludeProject = includesSetting(options, "project");
18010
18042
  this.settingSourcesIncludePlugins = includesSetting(options, "plugins");
18011
18043
  const sub = bootstrapSubmanagers({
@@ -18053,7 +18085,7 @@ var init_local_agent = __esm({
18053
18085
  this.settingSourcesIncludeProject,
18054
18086
  this.options.agents
18055
18087
  );
18056
- await hydrateSession(this.agentId, { baseDir: this.transcriptBaseDir, cwd: this.workspaceCwd });
18088
+ await hydrateSession(this.agentId, { store: this.sessionStore, cwd: this.workspaceCwd });
18057
18089
  await this.personalityStore.hydrate(this.agentId);
18058
18090
  }
18059
18091
  /** T4.2 — expose PluginManager so agent-loop can fire pre_tool_call hooks. @internal */
@@ -18109,7 +18141,7 @@ var init_local_agent = __esm({
18109
18141
  userText,
18110
18142
  agentId: this.agentId,
18111
18143
  workspaceCwd: this.workspaceCwd,
18112
- baseDir: this.transcriptBaseDir,
18144
+ sessionStore: this.sessionStore,
18113
18145
  model: this.model?.id ?? "unknown",
18114
18146
  ...options.onRunEvent !== void 0 ? { onRunEvent: options.onRunEvent } : {},
18115
18147
  hooksExecutor: this.hooksExecutor,