@theokit/sdk 4.0.1 → 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.
package/dist/cron.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- export { N as Cron } from './cron-BfPhZjRJ.cjs';
1
+ export { N as Cron } from './cron-BlAjYOew.cjs';
2
2
  import './run-BTvkk53K.cjs';
3
3
  import 'zod';
package/dist/cron.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { N as Cron } from './cron-L5QTlAtl.js';
1
+ export { N as Cron } from './cron-AzT5D0VP.js';
2
2
  import './run-BTvkk53K.js';
3
3
  import 'zod';
package/dist/cron.js CHANGED
@@ -3507,6 +3507,72 @@ var init_cloud_tool_parity = __esm({
3507
3507
  init_errors();
3508
3508
  }
3509
3509
  });
3510
+
3511
+ // src/internal/persistence/file-lock.ts
3512
+ async function getProperLockfile() {
3513
+ if (cached !== void 0) return cached;
3514
+ try {
3515
+ const mod = await import('proper-lockfile');
3516
+ if (!validateLockModule(mod)) {
3517
+ if (!warnedStructural) {
3518
+ warnedStructural = true;
3519
+ process.stderr.write(
3520
+ "[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"
3521
+ );
3522
+ }
3523
+ cached = null;
3524
+ return cached;
3525
+ }
3526
+ cached = mod;
3527
+ } catch {
3528
+ cached = null;
3529
+ }
3530
+ return cached;
3531
+ }
3532
+ function validateLockModule(mod) {
3533
+ if (mod === null || mod === void 0 || typeof mod !== "object") return false;
3534
+ const m = mod;
3535
+ return typeof m.lock === "function" && typeof m.unlock === "function";
3536
+ }
3537
+ async function withFileLock(path, fn, options) {
3538
+ const lib = await getProperLockfile();
3539
+ if (lib === null) {
3540
+ if (!warnedMissing) {
3541
+ warnedMissing = true;
3542
+ process.stderr.write(
3543
+ "[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
3544
+ );
3545
+ }
3546
+ return withCwdMutex(`file-lock:${path}`, fn);
3547
+ }
3548
+ return withCwdMutex(`file-lock:${path}`, async () => {
3549
+ const release = await lib.lock(path, {
3550
+ // EC-1: companion lockfile, target path may not exist yet.
3551
+ lockfilePath: `${path}.lock`,
3552
+ realpath: false,
3553
+ stale: 3e4,
3554
+ retries: {
3555
+ retries: 5,
3556
+ factor: 1.5,
3557
+ minTimeout: 100,
3558
+ maxTimeout: 5e3
3559
+ }
3560
+ });
3561
+ try {
3562
+ return await fn();
3563
+ } finally {
3564
+ await release();
3565
+ }
3566
+ });
3567
+ }
3568
+ var cached, warnedMissing, warnedStructural;
3569
+ var init_file_lock = __esm({
3570
+ "src/internal/persistence/file-lock.ts"() {
3571
+ init_cwd_mutex();
3572
+ warnedMissing = false;
3573
+ warnedStructural = false;
3574
+ }
3575
+ });
3510
3576
  function encodeProjectDir(cwd) {
3511
3577
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
3512
3578
  }
@@ -3733,70 +3799,31 @@ var init_session_transcript = __esm({
3733
3799
  };
3734
3800
  }
3735
3801
  });
3736
-
3737
- // src/internal/persistence/file-lock.ts
3738
- async function getProperLockfile() {
3739
- if (cached !== void 0) return cached;
3740
- try {
3741
- const mod = await import('proper-lockfile');
3742
- if (!validateLockModule(mod)) {
3743
- if (!warnedStructural) {
3744
- warnedStructural = true;
3745
- process.stderr.write(
3746
- "[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"
3747
- );
3748
- }
3749
- cached = null;
3750
- return cached;
3751
- }
3752
- cached = mod;
3753
- } catch {
3754
- cached = null;
3755
- }
3756
- return cached;
3757
- }
3758
- function validateLockModule(mod) {
3759
- if (mod === null || mod === void 0 || typeof mod !== "object") return false;
3760
- const m = mod;
3761
- return typeof m.lock === "function" && typeof m.unlock === "function";
3762
- }
3763
- async function withFileLock(path, fn, options) {
3764
- const lib = await getProperLockfile();
3765
- if (lib === null) {
3766
- if (!warnedMissing) {
3767
- warnedMissing = true;
3768
- process.stderr.write(
3769
- "[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
3770
- );
3771
- }
3772
- return withCwdMutex(`file-lock:${path}`, fn);
3773
- }
3774
- return withCwdMutex(`file-lock:${path}`, async () => {
3775
- const release = await lib.lock(path, {
3776
- // EC-1: companion lockfile, target path may not exist yet.
3777
- lockfilePath: `${path}.lock`,
3778
- realpath: false,
3779
- stale: 3e4,
3780
- retries: {
3781
- retries: 5,
3782
- factor: 1.5,
3783
- minTimeout: 100,
3784
- maxTimeout: 5e3
3802
+ var FsSessionStore;
3803
+ var init_fs_session_store = __esm({
3804
+ "src/internal/persistence/fs-session-store.ts"() {
3805
+ init_file_lock();
3806
+ init_session_transcript();
3807
+ FsSessionStore = class {
3808
+ #baseDir;
3809
+ #cwd;
3810
+ constructor(options) {
3811
+ this.#baseDir = options.baseDir;
3812
+ this.#cwd = options.cwd;
3813
+ }
3814
+ async readRecords(agentId) {
3815
+ return readTranscript(transcriptPath(this.#baseDir, this.#cwd, agentId));
3816
+ }
3817
+ async appendRecords(agentId, records) {
3818
+ if (records.length === 0) return;
3819
+ const path = transcriptPath(this.#baseDir, this.#cwd, agentId);
3820
+ await mkdir(dirname(path), { recursive: true });
3821
+ await withFileLock(path, async () => {
3822
+ const prior = await readTranscript(path);
3823
+ await writeTranscript(path, [...prior, ...records]);
3824
+ });
3785
3825
  }
3786
- });
3787
- try {
3788
- return await fn();
3789
- } finally {
3790
- await release();
3791
- }
3792
- });
3793
- }
3794
- var cached, warnedMissing, warnedStructural;
3795
- var init_file_lock = __esm({
3796
- "src/internal/persistence/file-lock.ts"() {
3797
- init_cwd_mutex();
3798
- warnedMissing = false;
3799
- warnedStructural = false;
3826
+ };
3800
3827
  }
3801
3828
  });
3802
3829
  function getTheokitHome(cwd) {
@@ -5128,6 +5155,8 @@ var init_memory_path_selector = __esm({
5128
5155
  PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
5129
5156
  }
5130
5157
  });
5158
+
5159
+ // src/internal/runtime/session/agent-session-store.ts
5131
5160
  function seedTranscript(prior, opts) {
5132
5161
  return SessionTranscript.fromRecords(prior, opts);
5133
5162
  }
@@ -5165,8 +5194,8 @@ function appendConversation(transcript, conversation) {
5165
5194
  if (toolResults.length > 0) transcript.appendToolResults(toolResults);
5166
5195
  }
5167
5196
  }
5168
- async function readSessionMessages(baseDir, cwd, agentId) {
5169
- const records = await readTranscript(transcriptPath(baseDir, cwd, agentId));
5197
+ async function readSessionMessages(store, agentId) {
5198
+ const records = await store.readRecords(agentId);
5170
5199
  return reconstructMessages(records).map(narrowToSessionMessage);
5171
5200
  }
5172
5201
  function partToText(p) {
@@ -5183,37 +5212,31 @@ function narrowToSessionMessage(m) {
5183
5212
  const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
5184
5213
  return { role, text };
5185
5214
  }
5186
- async function persistTurn(loc, sessionId, turn) {
5187
- const path = transcriptPath(loc.baseDir, loc.cwd, loc.agentId);
5188
- await mkdir(dirname(path), { recursive: true });
5189
- await withFileLock(path, async () => {
5190
- const prior = await readTranscript(path);
5191
- const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5192
- transcript.appendUserTurn(turn.userText);
5193
- appendConversation(transcript, turn.conversation);
5194
- await writeTranscript(path, transcript.records());
5195
- });
5215
+ function deltaRecords(transcript, priorLength) {
5216
+ return transcript.records().slice(priorLength);
5196
5217
  }
5197
- async function appendCompactBoundaryRecord(loc, sessionId, meta) {
5198
- const path = transcriptPath(loc.baseDir, loc.cwd, loc.agentId);
5199
- await mkdir(dirname(path), { recursive: true });
5200
- await withFileLock(path, async () => {
5201
- const prior = await readTranscript(path);
5202
- const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5203
- transcript.appendCompactBoundary(meta);
5204
- await writeTranscript(path, transcript.records());
5205
- });
5218
+ async function persistTurn(store, loc, sessionId, turn) {
5219
+ const prior = await store.readRecords(loc.agentId);
5220
+ const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5221
+ transcript.appendUserTurn(turn.userText);
5222
+ appendConversation(transcript, turn.conversation);
5223
+ await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
5224
+ }
5225
+ async function appendCompactBoundaryRecord(store, loc, sessionId, meta) {
5226
+ const prior = await store.readRecords(loc.agentId);
5227
+ const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
5228
+ transcript.appendCompactBoundary(meta);
5229
+ await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
5206
5230
  }
5207
5231
  var init_agent_session_store = __esm({
5208
5232
  "src/internal/runtime/session/agent-session-store.ts"() {
5209
- init_file_lock();
5210
5233
  init_session_transcript();
5211
5234
  }
5212
5235
  });
5213
5236
 
5214
5237
  // src/internal/runtime/session/agent-session.ts
5215
- function transcriptKey(baseDir, cwd, agentId) {
5216
- return `${baseDir}::${cwd}::${agentId}`;
5238
+ function transcriptKey(cwd, agentId) {
5239
+ return `${cwd}::${agentId}`;
5217
5240
  }
5218
5241
  function appendSessionMessage(agentId, message) {
5219
5242
  const existing = sessions.get(agentId) ?? [];
@@ -5223,15 +5246,15 @@ function appendSessionMessage(agentId, message) {
5223
5246
  function getSessionMessages(agentId) {
5224
5247
  return sessions.get(agentId) ?? [];
5225
5248
  }
5226
- function persistTurnToTranscript(loc, sessionId, turn, onCompact) {
5227
- const key = transcriptKey(loc.baseDir, loc.cwd, loc.agentId);
5249
+ function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
5250
+ const key = transcriptKey(loc.cwd, loc.agentId);
5228
5251
  const chained = (pendingWrites.get(key) ?? Promise.resolve()).then(async () => {
5229
5252
  try {
5230
- await persistTurn(loc, sessionId, turn);
5253
+ await persistTurn(store, loc, sessionId, turn);
5231
5254
  const count = (recordCounts.get(key) ?? 0) + 1;
5232
5255
  recordCounts.set(key, count);
5233
5256
  if (count % COMPACTION_CHECK_INTERVAL === 0) {
5234
- await appendCompactBoundaryRecord(loc, sessionId, {
5257
+ await appendCompactBoundaryRecord(store, loc, sessionId, {
5235
5258
  preTokens: 0,
5236
5259
  trigger: "auto"
5237
5260
  });
@@ -5254,10 +5277,10 @@ function persistTurnToTranscript(loc, sessionId, turn, onCompact) {
5254
5277
  );
5255
5278
  }
5256
5279
  async function hydrateSession(agentId, loc) {
5257
- const key = transcriptKey(loc.baseDir, loc.cwd, agentId);
5280
+ const key = transcriptKey(loc.cwd, agentId);
5258
5281
  if (hydratedKeys.has(key)) return;
5259
5282
  hydratedKeys.add(key);
5260
- const persisted = await readSessionMessages(loc.baseDir, loc.cwd, agentId);
5283
+ const persisted = await readSessionMessages(loc.store, agentId);
5261
5284
  if (persisted.length === 0) return;
5262
5285
  if (!sessions.has(agentId) || sessions.get(agentId)?.length === 0) {
5263
5286
  sessions.set(agentId, persisted);
@@ -5292,7 +5315,7 @@ async function runPostRunLifecycle(inputs) {
5292
5315
  userText,
5293
5316
  agentId,
5294
5317
  workspaceCwd,
5295
- baseDir,
5318
+ sessionStore,
5296
5319
  model,
5297
5320
  onRunEvent,
5298
5321
  hooksExecutor,
@@ -5311,7 +5334,8 @@ async function runPostRunLifecycle(inputs) {
5311
5334
  }
5312
5335
  const conversation = await safeConversation(run);
5313
5336
  persistTurnToTranscript(
5314
- { baseDir, cwd: workspaceCwd, agentId, model },
5337
+ sessionStore,
5338
+ { cwd: workspaceCwd, agentId, model },
5315
5339
  agentId,
5316
5340
  { userText, conversation },
5317
5341
  onRunEvent !== void 0 ? () => emitRunEvent(onRunEvent, { type: "compact_boundary", trigger: "auto" }) : void 0
@@ -17918,6 +17942,7 @@ var init_local_agent = __esm({
17918
17942
  init_errors();
17919
17943
  init_ids();
17920
17944
  init_cwd_mutex();
17945
+ init_fs_session_store();
17921
17946
  init_session_transcript();
17922
17947
  init_store();
17923
17948
  init_manager();
@@ -17961,6 +17986,12 @@ var init_local_agent = __esm({
17961
17986
  * `~/.theokit`; set `local.baseDir: "~/.claude"` for Claude Code CLI interop.
17962
17987
  */
17963
17988
  transcriptBaseDir;
17989
+ /**
17990
+ * SE41 — the session record store. Defaults to the FS transcript store (byte-
17991
+ * identical to SE40); `local.sessionStore` injects an external store (Postgres /
17992
+ * Redis / KV) so resume works on serverless (ephemeral FS) and multi-host.
17993
+ */
17994
+ sessionStore;
17964
17995
  /**
17965
17996
  * D319: lifecycle AbortController fired on `dispose()`. Composed with the
17966
17997
  * caller's `SendOptions.signal` via `anySignal` so the LLM `fetch()`
@@ -18003,6 +18034,7 @@ var init_local_agent = __esm({
18003
18034
  this.options = options;
18004
18035
  this.workspaceCwd = resolveCwd(options.local?.cwd);
18005
18036
  this.transcriptBaseDir = resolveBaseDir(options.local?.baseDir);
18037
+ this.sessionStore = options.local?.sessionStore ?? new FsSessionStore({ baseDir: this.transcriptBaseDir, cwd: this.workspaceCwd });
18006
18038
  this.settingSourcesIncludeProject = includesSetting(options, "project");
18007
18039
  this.settingSourcesIncludePlugins = includesSetting(options, "plugins");
18008
18040
  const sub = bootstrapSubmanagers({
@@ -18050,7 +18082,7 @@ var init_local_agent = __esm({
18050
18082
  this.settingSourcesIncludeProject,
18051
18083
  this.options.agents
18052
18084
  );
18053
- await hydrateSession(this.agentId, { baseDir: this.transcriptBaseDir, cwd: this.workspaceCwd });
18085
+ await hydrateSession(this.agentId, { store: this.sessionStore, cwd: this.workspaceCwd });
18054
18086
  await this.personalityStore.hydrate(this.agentId);
18055
18087
  }
18056
18088
  /** T4.2 — expose PluginManager so agent-loop can fire pre_tool_call hooks. @internal */
@@ -18106,7 +18138,7 @@ var init_local_agent = __esm({
18106
18138
  userText,
18107
18139
  agentId: this.agentId,
18108
18140
  workspaceCwd: this.workspaceCwd,
18109
- baseDir: this.transcriptBaseDir,
18141
+ sessionStore: this.sessionStore,
18110
18142
  model: this.model?.id ?? "unknown",
18111
18143
  ...options.onRunEvent !== void 0 ? { onRunEvent: options.onRunEvent } : {},
18112
18144
  hooksExecutor: this.hooksExecutor,