@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.
- package/CHANGELOG.md +6 -0
- package/dist/a2a/index.cjs +129 -97
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +129 -97
- package/dist/a2a/index.js.map +1 -1
- package/dist/{cron-L5QTlAtl.d.ts → cron-AzT5D0VP.d.ts} +89 -1
- package/dist/{cron-BfPhZjRJ.d.cts → cron-BlAjYOew.d.cts} +89 -1
- package/dist/cron.cjs +129 -97
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.d.cts +1 -1
- package/dist/cron.d.ts +1 -1
- package/dist/cron.js +129 -97
- package/dist/cron.js.map +1 -1
- package/dist/eval.cjs +129 -97
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +129 -97
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +129 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +129 -97
- package/dist/index.js.map +1 -1
- package/dist/internal/persistence/fs-session-store.d.cts +26 -0
- package/dist/internal/persistence/fs-session-store.d.ts +26 -0
- package/dist/internal/runtime/session/agent-session-store.d.ts +21 -13
- package/dist/types/agent.d.ts +10 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/session-store.d.ts +59 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c4d410c: SE41 — pluggable `SessionStore` seam over the native transcript. A minimal, two-method public port (`SessionStore.readRecords(agentId)` / `appendRecords(agentId, records)`) over the native `SessionRecord` shape, injected via `local.sessionStore`, so an external store (Postgres / Redis / KV / durable object) can be the **primary store AND resume source** — restoring the serverless (ephemeral FS) and multi-host / multi-pod resume use case SE40 dropped when it removed `ConversationStorageAdapter`, WITHOUT reverting that removed ~10-method adapter. DEFAULTS to a shipped `FsSessionStore` that reads and append-writes the native Claude-shaped `.jsonl` transcript (the same file the Claude Code CLI can `--continue`), so omitting `sessionStore` is byte-identical to current behavior — zero consumer change. Resume works across a simulated cold start (`Agent.resume(agentId, { local: { sessionStore } })` rebuilds history via the native DAG reader over `await store.readRecords(agentId)`); append-only compaction (`compact_boundary`) flows through `appendRecords`. `readRecords` throwing on resume surfaces as a typed error (fail-fast — never a silent empty history that would drop the conversation). Additive and back-compat.
|
|
8
|
+
|
|
3
9
|
## 4.0.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/a2a/index.cjs
CHANGED
|
@@ -3506,6 +3506,72 @@ var init_cloud_tool_parity = __esm({
|
|
|
3506
3506
|
init_errors();
|
|
3507
3507
|
}
|
|
3508
3508
|
});
|
|
3509
|
+
|
|
3510
|
+
// src/internal/persistence/file-lock.ts
|
|
3511
|
+
async function getProperLockfile() {
|
|
3512
|
+
if (cached !== void 0) return cached;
|
|
3513
|
+
try {
|
|
3514
|
+
const mod = await import('proper-lockfile');
|
|
3515
|
+
if (!validateLockModule(mod)) {
|
|
3516
|
+
if (!warnedStructural) {
|
|
3517
|
+
warnedStructural = true;
|
|
3518
|
+
process.stderr.write(
|
|
3519
|
+
"[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"
|
|
3520
|
+
);
|
|
3521
|
+
}
|
|
3522
|
+
cached = null;
|
|
3523
|
+
return cached;
|
|
3524
|
+
}
|
|
3525
|
+
cached = mod;
|
|
3526
|
+
} catch {
|
|
3527
|
+
cached = null;
|
|
3528
|
+
}
|
|
3529
|
+
return cached;
|
|
3530
|
+
}
|
|
3531
|
+
function validateLockModule(mod) {
|
|
3532
|
+
if (mod === null || mod === void 0 || typeof mod !== "object") return false;
|
|
3533
|
+
const m = mod;
|
|
3534
|
+
return typeof m.lock === "function" && typeof m.unlock === "function";
|
|
3535
|
+
}
|
|
3536
|
+
async function withFileLock(path, fn, options) {
|
|
3537
|
+
const lib = await getProperLockfile();
|
|
3538
|
+
if (lib === null) {
|
|
3539
|
+
if (!warnedMissing) {
|
|
3540
|
+
warnedMissing = true;
|
|
3541
|
+
process.stderr.write(
|
|
3542
|
+
"[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
|
|
3543
|
+
);
|
|
3544
|
+
}
|
|
3545
|
+
return withCwdMutex(`file-lock:${path}`, fn);
|
|
3546
|
+
}
|
|
3547
|
+
return withCwdMutex(`file-lock:${path}`, async () => {
|
|
3548
|
+
const release = await lib.lock(path, {
|
|
3549
|
+
// EC-1: companion lockfile, target path may not exist yet.
|
|
3550
|
+
lockfilePath: `${path}.lock`,
|
|
3551
|
+
realpath: false,
|
|
3552
|
+
stale: 3e4,
|
|
3553
|
+
retries: {
|
|
3554
|
+
retries: 5,
|
|
3555
|
+
factor: 1.5,
|
|
3556
|
+
minTimeout: 100,
|
|
3557
|
+
maxTimeout: 5e3
|
|
3558
|
+
}
|
|
3559
|
+
});
|
|
3560
|
+
try {
|
|
3561
|
+
return await fn();
|
|
3562
|
+
} finally {
|
|
3563
|
+
await release();
|
|
3564
|
+
}
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
var cached, warnedMissing, warnedStructural;
|
|
3568
|
+
var init_file_lock = __esm({
|
|
3569
|
+
"src/internal/persistence/file-lock.ts"() {
|
|
3570
|
+
init_cwd_mutex();
|
|
3571
|
+
warnedMissing = false;
|
|
3572
|
+
warnedStructural = false;
|
|
3573
|
+
}
|
|
3574
|
+
});
|
|
3509
3575
|
function encodeProjectDir(cwd) {
|
|
3510
3576
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
3511
3577
|
}
|
|
@@ -3732,70 +3798,31 @@ var init_session_transcript = __esm({
|
|
|
3732
3798
|
};
|
|
3733
3799
|
}
|
|
3734
3800
|
});
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
if (mod === null || mod === void 0 || typeof mod !== "object") return false;
|
|
3759
|
-
const m = mod;
|
|
3760
|
-
return typeof m.lock === "function" && typeof m.unlock === "function";
|
|
3761
|
-
}
|
|
3762
|
-
async function withFileLock(path, fn, options) {
|
|
3763
|
-
const lib = await getProperLockfile();
|
|
3764
|
-
if (lib === null) {
|
|
3765
|
-
if (!warnedMissing) {
|
|
3766
|
-
warnedMissing = true;
|
|
3767
|
-
process.stderr.write(
|
|
3768
|
-
"[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
|
|
3769
|
-
);
|
|
3770
|
-
}
|
|
3771
|
-
return withCwdMutex(`file-lock:${path}`, fn);
|
|
3772
|
-
}
|
|
3773
|
-
return withCwdMutex(`file-lock:${path}`, async () => {
|
|
3774
|
-
const release = await lib.lock(path, {
|
|
3775
|
-
// EC-1: companion lockfile, target path may not exist yet.
|
|
3776
|
-
lockfilePath: `${path}.lock`,
|
|
3777
|
-
realpath: false,
|
|
3778
|
-
stale: 3e4,
|
|
3779
|
-
retries: {
|
|
3780
|
-
retries: 5,
|
|
3781
|
-
factor: 1.5,
|
|
3782
|
-
minTimeout: 100,
|
|
3783
|
-
maxTimeout: 5e3
|
|
3801
|
+
var FsSessionStore;
|
|
3802
|
+
var init_fs_session_store = __esm({
|
|
3803
|
+
"src/internal/persistence/fs-session-store.ts"() {
|
|
3804
|
+
init_file_lock();
|
|
3805
|
+
init_session_transcript();
|
|
3806
|
+
FsSessionStore = class {
|
|
3807
|
+
#baseDir;
|
|
3808
|
+
#cwd;
|
|
3809
|
+
constructor(options) {
|
|
3810
|
+
this.#baseDir = options.baseDir;
|
|
3811
|
+
this.#cwd = options.cwd;
|
|
3812
|
+
}
|
|
3813
|
+
async readRecords(agentId) {
|
|
3814
|
+
return readTranscript(transcriptPath(this.#baseDir, this.#cwd, agentId));
|
|
3815
|
+
}
|
|
3816
|
+
async appendRecords(agentId, records) {
|
|
3817
|
+
if (records.length === 0) return;
|
|
3818
|
+
const path$1 = transcriptPath(this.#baseDir, this.#cwd, agentId);
|
|
3819
|
+
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
3820
|
+
await withFileLock(path$1, async () => {
|
|
3821
|
+
const prior = await readTranscript(path$1);
|
|
3822
|
+
await writeTranscript(path$1, [...prior, ...records]);
|
|
3823
|
+
});
|
|
3784
3824
|
}
|
|
3785
|
-
}
|
|
3786
|
-
try {
|
|
3787
|
-
return await fn();
|
|
3788
|
-
} finally {
|
|
3789
|
-
await release();
|
|
3790
|
-
}
|
|
3791
|
-
});
|
|
3792
|
-
}
|
|
3793
|
-
var cached, warnedMissing, warnedStructural;
|
|
3794
|
-
var init_file_lock = __esm({
|
|
3795
|
-
"src/internal/persistence/file-lock.ts"() {
|
|
3796
|
-
init_cwd_mutex();
|
|
3797
|
-
warnedMissing = false;
|
|
3798
|
-
warnedStructural = false;
|
|
3825
|
+
};
|
|
3799
3826
|
}
|
|
3800
3827
|
});
|
|
3801
3828
|
function getTheokitHome(cwd) {
|
|
@@ -5127,6 +5154,8 @@ var init_memory_path_selector = __esm({
|
|
|
5127
5154
|
PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
|
|
5128
5155
|
}
|
|
5129
5156
|
});
|
|
5157
|
+
|
|
5158
|
+
// src/internal/runtime/session/agent-session-store.ts
|
|
5130
5159
|
function seedTranscript(prior, opts) {
|
|
5131
5160
|
return SessionTranscript.fromRecords(prior, opts);
|
|
5132
5161
|
}
|
|
@@ -5164,8 +5193,8 @@ function appendConversation(transcript, conversation) {
|
|
|
5164
5193
|
if (toolResults.length > 0) transcript.appendToolResults(toolResults);
|
|
5165
5194
|
}
|
|
5166
5195
|
}
|
|
5167
|
-
async function readSessionMessages(
|
|
5168
|
-
const records = await
|
|
5196
|
+
async function readSessionMessages(store, agentId) {
|
|
5197
|
+
const records = await store.readRecords(agentId);
|
|
5169
5198
|
return reconstructMessages(records).map(narrowToSessionMessage);
|
|
5170
5199
|
}
|
|
5171
5200
|
function partToText(p) {
|
|
@@ -5182,37 +5211,31 @@ function narrowToSessionMessage(m) {
|
|
|
5182
5211
|
const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
|
|
5183
5212
|
return { role, text };
|
|
5184
5213
|
}
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
5188
|
-
await withFileLock(path$1, async () => {
|
|
5189
|
-
const prior = await readTranscript(path$1);
|
|
5190
|
-
const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
|
|
5191
|
-
transcript.appendUserTurn(turn.userText);
|
|
5192
|
-
appendConversation(transcript, turn.conversation);
|
|
5193
|
-
await writeTranscript(path$1, transcript.records());
|
|
5194
|
-
});
|
|
5214
|
+
function deltaRecords(transcript, priorLength) {
|
|
5215
|
+
return transcript.records().slice(priorLength);
|
|
5195
5216
|
}
|
|
5196
|
-
async function
|
|
5197
|
-
const
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5217
|
+
async function persistTurn(store, loc, sessionId, turn) {
|
|
5218
|
+
const prior = await store.readRecords(loc.agentId);
|
|
5219
|
+
const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
|
|
5220
|
+
transcript.appendUserTurn(turn.userText);
|
|
5221
|
+
appendConversation(transcript, turn.conversation);
|
|
5222
|
+
await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
|
|
5223
|
+
}
|
|
5224
|
+
async function appendCompactBoundaryRecord(store, loc, sessionId, meta) {
|
|
5225
|
+
const prior = await store.readRecords(loc.agentId);
|
|
5226
|
+
const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
|
|
5227
|
+
transcript.appendCompactBoundary(meta);
|
|
5228
|
+
await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
|
|
5205
5229
|
}
|
|
5206
5230
|
var init_agent_session_store = __esm({
|
|
5207
5231
|
"src/internal/runtime/session/agent-session-store.ts"() {
|
|
5208
|
-
init_file_lock();
|
|
5209
5232
|
init_session_transcript();
|
|
5210
5233
|
}
|
|
5211
5234
|
});
|
|
5212
5235
|
|
|
5213
5236
|
// src/internal/runtime/session/agent-session.ts
|
|
5214
|
-
function transcriptKey(
|
|
5215
|
-
return `${
|
|
5237
|
+
function transcriptKey(cwd, agentId) {
|
|
5238
|
+
return `${cwd}::${agentId}`;
|
|
5216
5239
|
}
|
|
5217
5240
|
function appendSessionMessage(agentId, message) {
|
|
5218
5241
|
const existing = sessions.get(agentId) ?? [];
|
|
@@ -5222,15 +5245,15 @@ function appendSessionMessage(agentId, message) {
|
|
|
5222
5245
|
function getSessionMessages(agentId) {
|
|
5223
5246
|
return sessions.get(agentId) ?? [];
|
|
5224
5247
|
}
|
|
5225
|
-
function persistTurnToTranscript(loc, sessionId, turn, onCompact) {
|
|
5226
|
-
const key = transcriptKey(loc.
|
|
5248
|
+
function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
|
|
5249
|
+
const key = transcriptKey(loc.cwd, loc.agentId);
|
|
5227
5250
|
const chained = (pendingWrites.get(key) ?? Promise.resolve()).then(async () => {
|
|
5228
5251
|
try {
|
|
5229
|
-
await persistTurn(loc, sessionId, turn);
|
|
5252
|
+
await persistTurn(store, loc, sessionId, turn);
|
|
5230
5253
|
const count = (recordCounts.get(key) ?? 0) + 1;
|
|
5231
5254
|
recordCounts.set(key, count);
|
|
5232
5255
|
if (count % COMPACTION_CHECK_INTERVAL === 0) {
|
|
5233
|
-
await appendCompactBoundaryRecord(loc, sessionId, {
|
|
5256
|
+
await appendCompactBoundaryRecord(store, loc, sessionId, {
|
|
5234
5257
|
preTokens: 0,
|
|
5235
5258
|
trigger: "auto"
|
|
5236
5259
|
});
|
|
@@ -5253,10 +5276,10 @@ function persistTurnToTranscript(loc, sessionId, turn, onCompact) {
|
|
|
5253
5276
|
);
|
|
5254
5277
|
}
|
|
5255
5278
|
async function hydrateSession(agentId, loc) {
|
|
5256
|
-
const key = transcriptKey(loc.
|
|
5279
|
+
const key = transcriptKey(loc.cwd, agentId);
|
|
5257
5280
|
if (hydratedKeys.has(key)) return;
|
|
5258
5281
|
hydratedKeys.add(key);
|
|
5259
|
-
const persisted = await readSessionMessages(loc.
|
|
5282
|
+
const persisted = await readSessionMessages(loc.store, agentId);
|
|
5260
5283
|
if (persisted.length === 0) return;
|
|
5261
5284
|
if (!sessions.has(agentId) || sessions.get(agentId)?.length === 0) {
|
|
5262
5285
|
sessions.set(agentId, persisted);
|
|
@@ -5291,7 +5314,7 @@ async function runPostRunLifecycle(inputs) {
|
|
|
5291
5314
|
userText,
|
|
5292
5315
|
agentId,
|
|
5293
5316
|
workspaceCwd,
|
|
5294
|
-
|
|
5317
|
+
sessionStore,
|
|
5295
5318
|
model,
|
|
5296
5319
|
onRunEvent,
|
|
5297
5320
|
hooksExecutor,
|
|
@@ -5310,7 +5333,8 @@ async function runPostRunLifecycle(inputs) {
|
|
|
5310
5333
|
}
|
|
5311
5334
|
const conversation = await safeConversation(run);
|
|
5312
5335
|
persistTurnToTranscript(
|
|
5313
|
-
|
|
5336
|
+
sessionStore,
|
|
5337
|
+
{ cwd: workspaceCwd, agentId, model },
|
|
5314
5338
|
agentId,
|
|
5315
5339
|
{ userText, conversation },
|
|
5316
5340
|
onRunEvent !== void 0 ? () => emitRunEvent(onRunEvent, { type: "compact_boundary", trigger: "auto" }) : void 0
|
|
@@ -17758,6 +17782,7 @@ var init_local_agent = __esm({
|
|
|
17758
17782
|
init_errors();
|
|
17759
17783
|
init_ids();
|
|
17760
17784
|
init_cwd_mutex();
|
|
17785
|
+
init_fs_session_store();
|
|
17761
17786
|
init_session_transcript();
|
|
17762
17787
|
init_store();
|
|
17763
17788
|
init_manager();
|
|
@@ -17801,6 +17826,12 @@ var init_local_agent = __esm({
|
|
|
17801
17826
|
* `~/.theokit`; set `local.baseDir: "~/.claude"` for Claude Code CLI interop.
|
|
17802
17827
|
*/
|
|
17803
17828
|
transcriptBaseDir;
|
|
17829
|
+
/**
|
|
17830
|
+
* SE41 — the session record store. Defaults to the FS transcript store (byte-
|
|
17831
|
+
* identical to SE40); `local.sessionStore` injects an external store (Postgres /
|
|
17832
|
+
* Redis / KV) so resume works on serverless (ephemeral FS) and multi-host.
|
|
17833
|
+
*/
|
|
17834
|
+
sessionStore;
|
|
17804
17835
|
/**
|
|
17805
17836
|
* D319: lifecycle AbortController fired on `dispose()`. Composed with the
|
|
17806
17837
|
* caller's `SendOptions.signal` via `anySignal` so the LLM `fetch()`
|
|
@@ -17843,6 +17874,7 @@ var init_local_agent = __esm({
|
|
|
17843
17874
|
this.options = options;
|
|
17844
17875
|
this.workspaceCwd = resolveCwd(options.local?.cwd);
|
|
17845
17876
|
this.transcriptBaseDir = resolveBaseDir(options.local?.baseDir);
|
|
17877
|
+
this.sessionStore = options.local?.sessionStore ?? new FsSessionStore({ baseDir: this.transcriptBaseDir, cwd: this.workspaceCwd });
|
|
17846
17878
|
this.settingSourcesIncludeProject = includesSetting(options, "project");
|
|
17847
17879
|
this.settingSourcesIncludePlugins = includesSetting(options, "plugins");
|
|
17848
17880
|
const sub = bootstrapSubmanagers({
|
|
@@ -17890,7 +17922,7 @@ var init_local_agent = __esm({
|
|
|
17890
17922
|
this.settingSourcesIncludeProject,
|
|
17891
17923
|
this.options.agents
|
|
17892
17924
|
);
|
|
17893
|
-
await hydrateSession(this.agentId, {
|
|
17925
|
+
await hydrateSession(this.agentId, { store: this.sessionStore, cwd: this.workspaceCwd });
|
|
17894
17926
|
await this.personalityStore.hydrate(this.agentId);
|
|
17895
17927
|
}
|
|
17896
17928
|
/** T4.2 — expose PluginManager so agent-loop can fire pre_tool_call hooks. @internal */
|
|
@@ -17946,7 +17978,7 @@ var init_local_agent = __esm({
|
|
|
17946
17978
|
userText,
|
|
17947
17979
|
agentId: this.agentId,
|
|
17948
17980
|
workspaceCwd: this.workspaceCwd,
|
|
17949
|
-
|
|
17981
|
+
sessionStore: this.sessionStore,
|
|
17950
17982
|
model: this.model?.id ?? "unknown",
|
|
17951
17983
|
...options.onRunEvent !== void 0 ? { onRunEvent: options.onRunEvent } : {},
|
|
17952
17984
|
hooksExecutor: this.hooksExecutor,
|