zeitlich 0.2.43 → 0.2.44

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 (56) hide show
  1. package/dist/{activities-CrN-ghLo.d.ts → activities-CPIB2v2C.d.ts} +4 -4
  2. package/dist/{activities-Coafq5zr.d.cts → activities-DnmNOnq4.d.cts} +4 -4
  3. package/dist/adapters/sandbox/daytona/index.d.cts +2 -2
  4. package/dist/adapters/sandbox/daytona/index.d.ts +2 -2
  5. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  6. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  7. package/dist/adapters/sandbox/e2b/index.d.cts +1 -1
  8. package/dist/adapters/sandbox/e2b/index.d.ts +1 -1
  9. package/dist/adapters/thread/anthropic/index.d.cts +4 -4
  10. package/dist/adapters/thread/anthropic/index.d.ts +4 -4
  11. package/dist/adapters/thread/anthropic/workflow.d.cts +4 -4
  12. package/dist/adapters/thread/anthropic/workflow.d.ts +4 -4
  13. package/dist/adapters/thread/google-genai/index.d.cts +4 -4
  14. package/dist/adapters/thread/google-genai/index.d.ts +4 -4
  15. package/dist/adapters/thread/google-genai/workflow.d.cts +4 -4
  16. package/dist/adapters/thread/google-genai/workflow.d.ts +4 -4
  17. package/dist/adapters/thread/langchain/index.cjs +11 -11
  18. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  19. package/dist/adapters/thread/langchain/index.d.cts +4 -4
  20. package/dist/adapters/thread/langchain/index.d.ts +4 -4
  21. package/dist/adapters/thread/langchain/index.js +8 -12
  22. package/dist/adapters/thread/langchain/index.js.map +1 -1
  23. package/dist/adapters/thread/langchain/workflow.d.cts +4 -4
  24. package/dist/adapters/thread/langchain/workflow.d.ts +4 -4
  25. package/dist/index.cjs +68 -48
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +6 -6
  28. package/dist/index.d.ts +6 -6
  29. package/dist/index.js +68 -49
  30. package/dist/index.js.map +1 -1
  31. package/dist/{proxy-COqA95FW.d.ts → proxy-B7Xi1znZ.d.ts} +1 -1
  32. package/dist/{proxy-Bf7uI-Hw.d.cts → proxy-DTnc5rqT.d.cts} +1 -1
  33. package/dist/{thread-manager-Bi1XlbpJ.d.ts → thread-manager-BAv340mi.d.ts} +3 -3
  34. package/dist/{thread-manager-wRVVBFgj.d.cts → thread-manager-BWv6ZXI3.d.cts} +4 -4
  35. package/dist/{thread-manager-BsLO3Fgc.d.cts → thread-manager-BlX2TwRN.d.cts} +3 -3
  36. package/dist/{thread-manager-BhkOyQ1I.d.ts → thread-manager-D2xorI-J.d.ts} +4 -4
  37. package/dist/{types-CdALEF3z.d.cts → types-4Wmk-wRq.d.cts} +1 -1
  38. package/dist/{types-CjY93AWZ.d.cts → types-C90VoEpt.d.cts} +1 -1
  39. package/dist/{types-BkX4HLzi.d.ts → types-Clhqautb.d.ts} +1 -1
  40. package/dist/{types-ChAy_jSP.d.ts → types-DKsCdAtQ.d.ts} +1 -1
  41. package/dist/{types-C66-BVBr.d.cts → types-DRJt1TMi.d.cts} +1 -1
  42. package/dist/{types-gVa5XCWD.d.ts → types-DpFD8ofR.d.ts} +1 -1
  43. package/dist/{workflow-BwT5EybR.d.ts → workflow-D32TRMr-.d.ts} +2 -2
  44. package/dist/{workflow-DMmiaw6w.d.cts → workflow-XVt0ww8K.d.cts} +2 -2
  45. package/dist/workflow.cjs +60 -37
  46. package/dist/workflow.cjs.map +1 -1
  47. package/dist/workflow.d.cts +2 -2
  48. package/dist/workflow.d.ts +2 -2
  49. package/dist/workflow.js +60 -37
  50. package/dist/workflow.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/lib/.env +1 -0
  53. package/src/lib/session/session.ts +46 -40
  54. package/src/lib/state/manager.integration.test.ts +27 -0
  55. package/src/lib/state/manager.ts +39 -1
  56. package/src/tools/bash/.env +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.43",
3
+ "version": "0.2.44",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
package/src/lib/.env ADDED
@@ -0,0 +1 @@
1
+ E2B_API_KEY=e2b_39af116424059782e2aee6942fd70237cc2126c9
@@ -383,46 +383,6 @@ export async function createSession<
383
383
  });
384
384
  }
385
385
 
386
- // --- Virtual filesystem init (independent of sandbox) ----------------
387
- if (virtualFsConfig) {
388
- if (!virtualFsOps) {
389
- throw ApplicationFailure.create({
390
- message: "No virtualFsOps provided — cannot resolve file tree",
391
- nonRetryable: true,
392
- });
393
- }
394
- const result = await virtualFsOps.resolveFileTree(virtualFsConfig.ctx);
395
- const skillFiles = skills ? collectSkillFiles(skills) : undefined;
396
- const fileTree = skillFiles
397
- ? [
398
- ...result.fileTree,
399
- ...Object.entries(skillFiles).map(([path, content]) => ({
400
- id: `skill:${path}`,
401
- path,
402
- size: content.length,
403
- mtime: new Date().toISOString(),
404
- metadata: {},
405
- // Carry the content directly on the entry so any handler that
406
- // constructs a VirtualFileSystem from `fileTree` can read it
407
- // without needing to also wire up `inlineFiles` from state.
408
- inlineContent: content,
409
- })),
410
- ]
411
- : result.fileTree;
412
- stateManager.mergeUpdate({
413
- fileTree,
414
- virtualFsCtx: virtualFsConfig.ctx,
415
- // `inlineFiles` is still the source of truth at read time:
416
- // VirtualFileSystem checks the inlineFiles map first and only
417
- // falls through to entry.inlineContent. Embedding the content on
418
- // the entry is the migration target so that handlers building a
419
- // VirtualFileSystem from `fileTree` alone (without forwarding
420
- // `inlineFiles` from state) can read skill resources. Until a
421
- // follow-up drops `inlineFiles`, both fields are populated.
422
- ...(skillFiles && { inlineFiles: skillFiles }),
423
- } as Partial<AgentState<TState>>);
424
- }
425
-
426
386
  if (hooks.onSessionStart) {
427
387
  await hooks.onSessionStart({
428
388
  threadId,
@@ -474,6 +434,52 @@ export async function createSession<
474
434
  await initializeThread(threadId, threadKey);
475
435
  }
476
436
  }
437
+
438
+ // --- Virtual filesystem init (independent of sandbox) ----------------
439
+ // Runs AFTER thread rehydration so the freshly resolved tree is
440
+ // authoritative. Otherwise a stale `fileTree` carried in a persisted
441
+ // slice (from a run on older code that didn't strip it) could
442
+ // overwrite entries that no longer exist in the backing store and
443
+ // cause subsequent FileWrite calls to fail with "not_found".
444
+ if (virtualFsConfig) {
445
+ if (!virtualFsOps) {
446
+ throw ApplicationFailure.create({
447
+ message: "No virtualFsOps provided — cannot resolve file tree",
448
+ nonRetryable: true,
449
+ });
450
+ }
451
+ const result = await virtualFsOps.resolveFileTree(virtualFsConfig.ctx);
452
+ const skillFiles = skills ? collectSkillFiles(skills) : undefined;
453
+ const fileTree = skillFiles
454
+ ? [
455
+ ...result.fileTree,
456
+ ...Object.entries(skillFiles).map(([path, content]) => ({
457
+ id: `skill:${path}`,
458
+ path,
459
+ size: content.length,
460
+ mtime: new Date().toISOString(),
461
+ metadata: {},
462
+ // Carry the content directly on the entry so any handler that
463
+ // constructs a VirtualFileSystem from `fileTree` can read it
464
+ // without needing to also wire up `inlineFiles` from state.
465
+ inlineContent: content,
466
+ })),
467
+ ]
468
+ : result.fileTree;
469
+ stateManager.mergeUpdate({
470
+ fileTree,
471
+ virtualFsCtx: virtualFsConfig.ctx,
472
+ // `inlineFiles` is still the source of truth at read time:
473
+ // VirtualFileSystem checks the inlineFiles map first and only
474
+ // falls through to entry.inlineContent. Embedding the content on
475
+ // the entry is the migration target so that handlers building a
476
+ // VirtualFileSystem from `fileTree` alone (without forwarding
477
+ // `inlineFiles` from state) can read skill resources. Until a
478
+ // follow-up drops `inlineFiles`, both fields are populated.
479
+ ...(skillFiles && { inlineFiles: skillFiles }),
480
+ } as Partial<AgentState<TState>>);
481
+ }
482
+
477
483
  await appendHumanMessage(
478
484
  threadId,
479
485
  uuid4(),
@@ -385,6 +385,33 @@ describe("createAgentStateManager integration", () => {
385
385
  expect("tools" in slice.custom).toBe(false);
386
386
  });
387
387
 
388
+ it("getPersistedSlice strips runtime fields injected via mergeUpdate", () => {
389
+ const sm = createAgentStateManager<{ label: string }>({
390
+ initialState: { systemPrompt: "test", label: "original" },
391
+ });
392
+
393
+ sm.mergeUpdate({
394
+ label: "updated",
395
+ fileTree: [
396
+ {
397
+ id: "f1",
398
+ path: "/a.txt",
399
+ size: 1,
400
+ mtime: "2026-01-01T00:00:00Z",
401
+ metadata: {},
402
+ },
403
+ ],
404
+ virtualFsCtx: { workspaceBase: "/tmp" },
405
+ inlineFiles: { "/a.txt": "hi" },
406
+ } as Parameters<typeof sm.mergeUpdate>[0]);
407
+
408
+ const slice = sm.getPersistedSlice();
409
+ expect(slice.custom).toEqual({ label: "updated" });
410
+ expect("fileTree" in slice.custom).toBe(false);
411
+ expect("virtualFsCtx" in slice.custom).toBe(false);
412
+ expect("inlineFiles" in slice.custom).toBe(false);
413
+ });
414
+
388
415
  it("mergeUpdate replaces the task map when given a tasks field", () => {
389
416
  const sm = createAgentStateManager<{ label: string; extra?: string }>({
390
417
  initialState: {
@@ -20,6 +20,32 @@ import type {
20
20
  } from "./types";
21
21
  import { z } from "zod";
22
22
 
23
+ /**
24
+ * Fields that live on `AgentState` at runtime but must NOT be persisted into
25
+ * `PersistedThreadState`. They're either managed by the state manager itself
26
+ * (status/version/turns/tasks/tools/usage), kept in their own slot
27
+ * (systemPrompt), or rebuilt on each run (the virtual-fs trio).
28
+ *
29
+ * Centralizing the list keeps the constructor's destructure-omit and
30
+ * `getPersistedSlice` in lockstep.
31
+ */
32
+ const RESERVED_STATE_KEYS = [
33
+ "status",
34
+ "version",
35
+ "turns",
36
+ "tasks",
37
+ "tools",
38
+ "systemPrompt",
39
+ "fileTree",
40
+ "inlineFiles",
41
+ "virtualFsCtx",
42
+ "totalInputTokens",
43
+ "totalOutputTokens",
44
+ "totalReasonTokens",
45
+ "cachedWriteTokens",
46
+ "cachedReadTokens",
47
+ ] as const;
48
+
23
49
  /**
24
50
  * Creates an agent state manager for tracking workflow state.
25
51
  * Automatically registers Temporal query and update handlers for the agent.
@@ -238,9 +264,21 @@ export function createAgentStateManager<
238
264
  },
239
265
 
240
266
  getPersistedSlice(): PersistedThreadState {
267
+ // `customState` can pick up reserved/runtime fields via `mergeUpdate`
268
+ // (e.g. `fileTree`, `virtualFsCtx`, `inlineFiles` written by the
269
+ // virtual-fs bootstrap on every run). Those are rebuilt per run and
270
+ // must never round-trip through the thread store, so strip them here
271
+ // rather than relying on callers to remember.
272
+ const source = customState as unknown as Record<string, JsonValue>;
273
+ const custom: Record<string, JsonValue> = {};
274
+ const reserved = new Set<string>(RESERVED_STATE_KEYS);
275
+ for (const [key, value] of Object.entries(source)) {
276
+ if (reserved.has(key)) continue;
277
+ custom[key] = value;
278
+ }
241
279
  return {
242
280
  tasks: Array.from(tasks.entries()),
243
- custom: { ...(customState as unknown as Record<string, JsonValue>) },
281
+ custom,
244
282
  };
245
283
  },
246
284
 
@@ -0,0 +1 @@
1
+ E2B_API_KEY=e2b_39af116424059782e2aee6942fd70237cc2126c9