zeitlich 0.2.40 → 0.2.42

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 (134) hide show
  1. package/README.md +12 -1
  2. package/dist/{activities-CvUrG3YG.d.cts → activities-Coafq5zr.d.cts} +2 -2
  3. package/dist/{activities-CULxRzJ1.d.ts → activities-CrN-ghLo.d.ts} +2 -2
  4. package/dist/adapters/sandbox/daytona/index.cjs +4 -23
  5. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/daytona/index.d.cts +18 -86
  7. package/dist/adapters/sandbox/daytona/index.d.ts +18 -86
  8. package/dist/adapters/sandbox/daytona/index.js +4 -23
  9. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  10. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -7
  11. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  12. package/dist/adapters/sandbox/daytona/workflow.d.cts +9 -2
  13. package/dist/adapters/sandbox/daytona/workflow.d.ts +9 -2
  14. package/dist/adapters/sandbox/daytona/workflow.js +1 -7
  15. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  16. package/dist/adapters/sandbox/e2b/index.cjs +21 -3
  17. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/e2b/index.d.cts +48 -7
  19. package/dist/adapters/sandbox/e2b/index.d.ts +48 -7
  20. package/dist/adapters/sandbox/e2b/index.js +22 -5
  21. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  22. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  23. package/dist/adapters/sandbox/e2b/workflow.d.cts +4 -2
  24. package/dist/adapters/sandbox/e2b/workflow.d.ts +4 -2
  25. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  26. package/dist/adapters/sandbox/inmemory/index.cjs +11 -0
  27. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/inmemory/index.d.cts +11 -3
  29. package/dist/adapters/sandbox/inmemory/index.d.ts +11 -3
  30. package/dist/adapters/sandbox/inmemory/index.js +11 -1
  31. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  32. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  33. package/dist/adapters/sandbox/inmemory/workflow.d.cts +4 -2
  34. package/dist/adapters/sandbox/inmemory/workflow.d.ts +4 -2
  35. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  36. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  37. package/dist/adapters/thread/anthropic/index.d.cts +6 -6
  38. package/dist/adapters/thread/anthropic/index.d.ts +6 -6
  39. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  40. package/dist/adapters/thread/anthropic/workflow.d.cts +6 -6
  41. package/dist/adapters/thread/anthropic/workflow.d.ts +6 -6
  42. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  43. package/dist/adapters/thread/google-genai/index.d.cts +6 -6
  44. package/dist/adapters/thread/google-genai/index.d.ts +6 -6
  45. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  46. package/dist/adapters/thread/google-genai/workflow.d.cts +6 -6
  47. package/dist/adapters/thread/google-genai/workflow.d.ts +6 -6
  48. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  49. package/dist/adapters/thread/langchain/index.d.cts +6 -6
  50. package/dist/adapters/thread/langchain/index.d.ts +6 -6
  51. package/dist/adapters/thread/langchain/index.js.map +1 -1
  52. package/dist/adapters/thread/langchain/workflow.d.cts +6 -6
  53. package/dist/adapters/thread/langchain/workflow.d.ts +6 -6
  54. package/dist/index.cjs +316 -119
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.d.cts +93 -17
  57. package/dist/index.d.ts +93 -17
  58. package/dist/index.js +317 -120
  59. package/dist/index.js.map +1 -1
  60. package/dist/{proxy-5EbwzaY4.d.cts → proxy-Bf7uI-Hw.d.cts} +1 -1
  61. package/dist/{proxy-wZufFfBh.d.ts → proxy-COqA95FW.d.ts} +1 -1
  62. package/dist/{thread-manager-BqBAIsED.d.ts → thread-manager-BhkOyQ1I.d.ts} +2 -2
  63. package/dist/{thread-manager-BNiIt5r8.d.ts → thread-manager-Bi1XlbpJ.d.ts} +2 -2
  64. package/dist/{thread-manager-DF8WuCRs.d.cts → thread-manager-BsLO3Fgc.d.cts} +2 -2
  65. package/dist/{thread-manager-BoN5DOvG.d.cts → thread-manager-wRVVBFgj.d.cts} +2 -2
  66. package/dist/{types-C7OoY7h8.d.ts → types-BkX4HLzi.d.ts} +1 -1
  67. package/dist/{types-CuISs0Ub.d.cts → types-C66-BVBr.d.cts} +1 -1
  68. package/dist/types-CJ7tCdl6.d.cts +266 -0
  69. package/dist/types-CJ7tCdl6.d.ts +266 -0
  70. package/dist/{types-DeQH84C_.d.ts → types-CdALEF3z.d.cts} +342 -23
  71. package/dist/{types-Cn2r3ol3.d.cts → types-ChAy_jSP.d.ts} +342 -23
  72. package/dist/types-CjY93AWZ.d.cts +84 -0
  73. package/dist/types-gVa5XCWD.d.ts +84 -0
  74. package/dist/{workflow-DhplIN65.d.cts → workflow-BwT5EybR.d.ts} +7 -6
  75. package/dist/{workflow-C2MZZj5K.d.ts → workflow-DMmiaw6w.d.cts} +7 -6
  76. package/dist/workflow.cjs +138 -77
  77. package/dist/workflow.cjs.map +1 -1
  78. package/dist/workflow.d.cts +4 -4
  79. package/dist/workflow.d.ts +4 -4
  80. package/dist/workflow.js +139 -78
  81. package/dist/workflow.js.map +1 -1
  82. package/package.json +17 -33
  83. package/src/adapters/sandbox/daytona/index.ts +25 -48
  84. package/src/adapters/sandbox/daytona/proxy.ts +7 -8
  85. package/src/adapters/sandbox/e2b/README.md +81 -0
  86. package/src/adapters/sandbox/e2b/index.ts +53 -11
  87. package/src/adapters/sandbox/e2b/keep-alive.test.ts +115 -0
  88. package/src/adapters/sandbox/e2b/proxy.ts +3 -2
  89. package/src/adapters/sandbox/e2b/types.ts +34 -2
  90. package/src/adapters/sandbox/inmemory/index.ts +21 -1
  91. package/src/adapters/sandbox/inmemory/proxy.ts +7 -3
  92. package/src/index.ts +1 -1
  93. package/src/lib/activity.ts +5 -0
  94. package/src/lib/sandbox/capability-types.test.ts +859 -0
  95. package/src/lib/sandbox/index.ts +1 -0
  96. package/src/lib/sandbox/manager.ts +187 -31
  97. package/src/lib/sandbox/types.ts +189 -46
  98. package/src/lib/session/index.ts +1 -0
  99. package/src/lib/session/session.integration.test.ts +58 -0
  100. package/src/lib/session/session.ts +109 -50
  101. package/src/lib/session/types.ts +189 -8
  102. package/src/lib/subagent/handler.ts +66 -43
  103. package/src/lib/subagent/subagent.integration.test.ts +2 -0
  104. package/src/lib/subagent/types.ts +492 -16
  105. package/src/lib/subagent/workflow.ts +11 -1
  106. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +158 -0
  107. package/src/lib/tool-router/index.ts +1 -1
  108. package/src/lib/tool-router/with-sandbox.ts +45 -2
  109. package/src/lib/virtual-fs/filesystem.ts +41 -16
  110. package/src/lib/virtual-fs/types.ts +19 -0
  111. package/src/lib/virtual-fs/virtual-fs.test.ts +204 -1
  112. package/src/tools/read-file/handler.test.ts +83 -0
  113. package/src/workflow.ts +3 -0
  114. package/tsup.config.ts +0 -4
  115. package/dist/adapters/sandbox/bedrock/index.cjs +0 -457
  116. package/dist/adapters/sandbox/bedrock/index.cjs.map +0 -1
  117. package/dist/adapters/sandbox/bedrock/index.d.cts +0 -25
  118. package/dist/adapters/sandbox/bedrock/index.d.ts +0 -25
  119. package/dist/adapters/sandbox/bedrock/index.js +0 -454
  120. package/dist/adapters/sandbox/bedrock/index.js.map +0 -1
  121. package/dist/adapters/sandbox/bedrock/workflow.cjs +0 -36
  122. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +0 -1
  123. package/dist/adapters/sandbox/bedrock/workflow.d.cts +0 -29
  124. package/dist/adapters/sandbox/bedrock/workflow.d.ts +0 -29
  125. package/dist/adapters/sandbox/bedrock/workflow.js +0 -34
  126. package/dist/adapters/sandbox/bedrock/workflow.js.map +0 -1
  127. package/dist/types-DAsQ21Rt.d.ts +0 -74
  128. package/dist/types-lm8tMNJQ.d.cts +0 -74
  129. package/dist/types-yx0LzPGn.d.cts +0 -173
  130. package/dist/types-yx0LzPGn.d.ts +0 -173
  131. package/src/adapters/sandbox/bedrock/filesystem.ts +0 -340
  132. package/src/adapters/sandbox/bedrock/index.ts +0 -274
  133. package/src/adapters/sandbox/bedrock/proxy.ts +0 -59
  134. package/src/adapters/sandbox/bedrock/types.ts +0 -24
@@ -895,6 +895,64 @@ describe("createSession integration", () => {
895
895
  });
896
896
  });
897
897
 
898
+ it("embeds skill resourceContents on synthetic file tree entries via inlineContent", async () => {
899
+ const { ops } = createMockThreadOps();
900
+
901
+ const session = await createSession({
902
+ agentName: "TestAgent",
903
+ thread: { mode: "new", threadId: "thread-1" },
904
+ runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
905
+ threadOps: ops,
906
+ buildContextMessage: () => "go",
907
+ virtualFs: { ctx: { projectId: "p" } },
908
+ virtualFsOps: {
909
+ resolveFileTree: async () => ({ fileTree: [] }),
910
+ },
911
+ skills: [
912
+ {
913
+ name: "test-skill",
914
+ description: "Test",
915
+ instructions: "Do test",
916
+ location: "/skills/test-skill",
917
+ resourceContents: {
918
+ "references/alpha.md": "# Alpha doc",
919
+ "references/beta.md": "# Beta doc",
920
+ },
921
+ },
922
+ ],
923
+ hooks: {
924
+ onSessionStart: async () => {},
925
+ },
926
+ });
927
+
928
+ const stateManager = createAgentStateManager({
929
+ initialState: { systemPrompt: "test" },
930
+ });
931
+
932
+ await session.runSession({ stateManager });
933
+
934
+ const capturedFileTree = stateManager.getCurrentState().fileTree;
935
+ expect(Array.isArray(capturedFileTree)).toBe(true);
936
+ const entries = capturedFileTree as Array<{
937
+ path: string;
938
+ inlineContent?: string;
939
+ }>;
940
+
941
+ const alpha = entries.find(
942
+ (e) => e.path === "/skills/test-skill/references/alpha.md"
943
+ );
944
+ const beta = entries.find(
945
+ (e) => e.path === "/skills/test-skill/references/beta.md"
946
+ );
947
+ expect(alpha?.inlineContent).toBe("# Alpha doc");
948
+ expect(beta?.inlineContent).toBe("# Beta doc");
949
+
950
+ expect(stateManager.getCurrentState().inlineFiles).toEqual({
951
+ "/skills/test-skill/references/alpha.md": "# Alpha doc",
952
+ "/skills/test-skill/references/beta.md": "# Beta doc",
953
+ });
954
+ });
955
+
898
956
  it("does not pass initialFiles when skills have no resourceContents", async () => {
899
957
  const { ops } = createMockThreadOps();
900
958
  let capturedOptions: Record<string, unknown> | undefined;
@@ -1,5 +1,4 @@
1
1
  import {
2
- condition,
3
2
  defineUpdate,
4
3
  setHandler,
5
4
  ApplicationFailure,
@@ -7,11 +6,14 @@ import {
7
6
  } from "@temporalio/workflow";
8
7
  import type { SessionExitReason } from "../types";
9
8
  import type { SessionConfig, ZeitlichSession } from "./types";
9
+ import { resolveSessionLifecycle } from "./types";
10
10
  import type {
11
+ SandboxCapability,
11
12
  SandboxCreateOptions,
12
13
  SandboxOps,
13
14
  SandboxSnapshot,
14
15
  } from "../sandbox/types";
16
+ import type { SandboxInit, SubagentSandboxShutdown } from "../lifecycle";
15
17
  import type {
16
18
  AgentState,
17
19
  AgentStateManager,
@@ -80,42 +82,89 @@ export async function createSession<
80
82
  T extends ToolMap,
81
83
  M = unknown,
82
84
  TContent = string,
85
+ TInit extends SandboxInit | undefined = undefined,
86
+ TShutdown extends SubagentSandboxShutdown | undefined = undefined,
83
87
  >(
84
- config: SessionConfig<T, M, TContent> & { sandboxOps: SandboxOps }
88
+ config: SessionConfig<T, M, TContent, TInit, TShutdown> & {
89
+ sandboxOps: NonNullable<
90
+ SessionConfig<T, M, TContent, TInit, TShutdown>["sandboxOps"]
91
+ >;
92
+ }
85
93
  ): Promise<ZeitlichSession<M, true>>;
86
94
  export async function createSession<
87
95
  T extends ToolMap,
88
96
  M = unknown,
89
97
  TContent = string,
90
- >(config: SessionConfig<T, M, TContent>): Promise<ZeitlichSession<M, false>>;
98
+ TInit extends SandboxInit | undefined = undefined,
99
+ TShutdown extends SubagentSandboxShutdown | undefined = undefined,
100
+ >(
101
+ config: SessionConfig<T, M, TContent, TInit, TShutdown>
102
+ ): Promise<ZeitlichSession<M, false>>;
103
+ // Implementation. The overloads above narrow the public contract per
104
+ // `SessionRequiredCaps<TInit, TShutdown>`. The impl signature uses the
105
+ // `never` cap floor — the structurally-narrowest shape that every
106
+ // adapter (including Daytona/Bedrock) satisfies — so each overload's
107
+ // possibly-narrow `sandboxOps` is assignable here. Gated method calls
108
+ // inside the body go through `wideOps()` below, which casts the
109
+ // narrow-typed binding to the wide shape. This is structurally safe:
110
+ // the runtime gates each gated call on `sandboxMode === …` /
111
+ // `resolvedShutdown === …`, and the type system has already ruled out
112
+ // any (mode, shutdown, adapter) cell that would invoke a missing
113
+ // method.
91
114
  export async function createSession<
92
115
  T extends ToolMap,
93
116
  M = unknown,
94
117
  TContent = string,
95
- >({
96
- agentName,
97
- maxTurns = 50,
98
- metadata = {},
99
- runAgent,
100
- threadOps,
101
- buildContextMessage,
102
- subagents,
103
- skills,
104
- tools = {} as T,
105
- processToolsInParallel = true,
106
- hooks = {},
107
- appendSystemPrompt = true,
108
- waitForInputTimeout = "48h",
109
- threadKey,
110
- sandboxOps,
111
- thread: threadInit,
112
- sandbox: sandboxInit,
113
- sandboxShutdown = "destroy",
114
- onSandboxReady,
115
- onSessionExit,
116
- virtualFs: virtualFsConfig,
117
- virtualFsOps,
118
- }: SessionConfig<T, M, TContent>): Promise<ZeitlichSession<M, boolean>> {
118
+ >(
119
+ config: Omit<
120
+ SessionConfig<T, M, TContent, SandboxInit | undefined, undefined>,
121
+ "sandboxOps"
122
+ > & {
123
+ sandboxOps?: SandboxOps<SandboxCreateOptions, unknown, never>;
124
+ }
125
+ ): Promise<ZeitlichSession<M, boolean>> {
126
+ const {
127
+ agentName,
128
+ maxTurns = 50,
129
+ metadata = {},
130
+ runAgent,
131
+ threadOps,
132
+ buildContextMessage,
133
+ subagents,
134
+ skills,
135
+ tools = {} as T,
136
+ processToolsInParallel = true,
137
+ hooks = {},
138
+ appendSystemPrompt = true,
139
+ threadKey,
140
+ sandboxOps,
141
+ thread: threadInit,
142
+ sandbox: sandboxInit,
143
+ sandboxShutdown,
144
+ onSandboxReady,
145
+ onSessionExit,
146
+ virtualFs: virtualFsConfig,
147
+ virtualFsOps,
148
+ } = config;
149
+
150
+ /**
151
+ * The narrow-typed `sandboxOps` binding cast to its wide
152
+ * (`SandboxCapability`) form. The overload signatures +
153
+ * `SessionRequiredCaps` SSOT have already ruled out `(sandbox.mode,
154
+ * sandboxShutdown, adapter)` combinations where a referenced method
155
+ * would be missing on the proxy, and each gated call site below is
156
+ * guarded by an `if (sandboxOps)` / `sandboxOps &&` runtime check,
157
+ * so the cast is structurally safe. `wideOps` returns the wide-cap
158
+ * shape; callers use it after their own runtime guard establishes
159
+ * that `sandboxOps` is defined.
160
+ */
161
+ type WideSandboxOps = SandboxOps<
162
+ SandboxCreateOptions,
163
+ unknown,
164
+ SandboxCapability
165
+ >;
166
+ const wideOps = (): WideSandboxOps =>
167
+ sandboxOps as unknown as WideSandboxOps;
119
168
  // ---------------------------------------------------------------------------
120
169
  // Thread resolution
121
170
  // ---------------------------------------------------------------------------
@@ -221,7 +270,18 @@ export async function createSession<
221
270
  );
222
271
 
223
272
  // --- Sandbox lifecycle: create, continue, fork, from-snapshot, or inherit ---
224
- const sandboxMode = sandboxInit?.mode;
273
+ // Resolve `sandbox` / `sandboxShutdown` defaults through the SSOT
274
+ // so the runtime dispatch below and the type-level
275
+ // `SessionRequiredCaps` cannot disagree on what the documented
276
+ // defaults are. Both surfaces consult `resolveSessionLifecycle`
277
+ // (or its type-level equivalent) before checking individual
278
+ // mode/shutdown values.
279
+ const lifecycle = resolveSessionLifecycle(
280
+ sandboxInit,
281
+ sandboxShutdown
282
+ );
283
+ const sandboxMode: SandboxInit["mode"] | undefined = lifecycle.mode;
284
+ const resolvedShutdown: SubagentSandboxShutdown = lifecycle.shutdown;
225
285
  let sandboxId: string | undefined;
226
286
  let sandboxOwned = false;
227
287
  let baseSnapshot: SandboxSnapshot | undefined;
@@ -250,8 +310,8 @@ export async function createSession<
250
310
  }
251
311
  sandboxId = (sandboxInit as { mode: "continue"; sandboxId: string })
252
312
  .sandboxId;
253
- if (sandboxShutdown === "pause-until-parent-close") {
254
- await sandboxOps.resumeSandbox(sandboxId);
313
+ if (resolvedShutdown === "pause-until-parent-close") {
314
+ await wideOps().resumeSandbox(sandboxId);
255
315
  }
256
316
  sandboxOwned = true;
257
317
  } else if (sandboxMode === "fork") {
@@ -266,7 +326,7 @@ export async function createSession<
266
326
  sandboxId: string;
267
327
  options?: SandboxCreateOptions;
268
328
  };
269
- sandboxId = await sandboxOps.forkSandbox(
329
+ sandboxId = await wideOps().forkSandbox(
270
330
  forkInit.sandboxId,
271
331
  forkInit.options
272
332
  );
@@ -283,7 +343,7 @@ export async function createSession<
283
343
  snapshot: SandboxSnapshot;
284
344
  options?: SandboxCreateOptions;
285
345
  };
286
- sandboxId = await sandboxOps.restoreSandbox(
346
+ sandboxId = await wideOps().restoreSandbox(
287
347
  restoreInit.snapshot,
288
348
  restoreInit.options
289
349
  );
@@ -310,10 +370,10 @@ export async function createSession<
310
370
  sandboxId &&
311
371
  sandboxOwned &&
312
372
  freshlyCreated &&
313
- sandboxShutdown === "snapshot" &&
373
+ resolvedShutdown === "snapshot" &&
314
374
  sandboxOps
315
375
  ) {
316
- baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
376
+ baseSnapshot = await wideOps().snapshotSandbox(sandboxId);
317
377
  }
318
378
 
319
379
  if (sandboxId && sandboxOwned && onSandboxReady) {
@@ -342,12 +402,23 @@ export async function createSession<
342
402
  size: content.length,
343
403
  mtime: new Date().toISOString(),
344
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,
345
409
  })),
346
410
  ]
347
411
  : result.fileTree;
348
412
  stateManager.mergeUpdate({
349
413
  fileTree,
350
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.
351
422
  ...(skillFiles && { inlineFiles: skillFiles }),
352
423
  } as Partial<AgentState<TState>>);
353
424
  }
@@ -516,19 +587,6 @@ export async function createSession<
516
587
 
517
588
  // Turn committed: fresh id for the next turn.
518
589
  assistantId = undefined;
519
-
520
- if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
521
- const conditionMet = await condition(
522
- () => stateManager.getStatus() === "RUNNING",
523
- waitForInputTimeout
524
- );
525
- if (!conditionMet) {
526
- stateManager.cancel();
527
- exitReason = "cancelled";
528
- await condition(() => false, "2s");
529
- break;
530
- }
531
- }
532
590
  }
533
591
 
534
592
  if (stateManager.getTurns() >= maxTurns && stateManager.isRunning()) {
@@ -575,19 +633,19 @@ export async function createSession<
575
633
  await callSessionEnd(exitReason, stateManager.getTurns());
576
634
 
577
635
  if (sandboxOwned && sandboxId && sandboxOps) {
578
- switch (sandboxShutdown) {
636
+ switch (resolvedShutdown) {
579
637
  case "destroy":
580
638
  await sandboxOps.destroySandbox(sandboxId);
581
639
  break;
582
640
  case "pause":
583
641
  case "pause-until-parent-close":
584
- await sandboxOps.pauseSandbox(sandboxId);
642
+ await wideOps().pauseSandbox(sandboxId);
585
643
  break;
586
644
  case "keep":
587
645
  case "keep-until-parent-close":
588
646
  break;
589
647
  case "snapshot":
590
- exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
648
+ exitSnapshot = await wideOps().snapshotSandbox(sandboxId);
591
649
  await sandboxOps.destroySandbox(sandboxId);
592
650
  break;
593
651
  }
@@ -618,6 +676,7 @@ export async function createSession<
618
676
  ...(sandboxId && { sandboxId }),
619
677
  ...(exitSnapshot && { snapshot: exitSnapshot }),
620
678
  threadId,
679
+ usage: stateManager.getTotalUsage(),
621
680
  });
622
681
  }
623
682
 
@@ -1,4 +1,3 @@
1
- import type { Duration } from "@temporalio/common";
2
1
  import type { SessionExitReason, ToolResultConfig } from "../types";
3
2
  import type {
4
3
  ToolMap,
@@ -8,7 +7,12 @@ import type {
8
7
  import type { Hooks } from "../hooks/types";
9
8
  import type { SubagentConfig } from "../subagent/types";
10
9
  import type { Skill } from "../skills/types";
11
- import type { SandboxOps, SandboxSnapshot } from "../sandbox/types";
10
+ import type {
11
+ SandboxCapability,
12
+ SandboxCreateOptions,
13
+ SandboxOps,
14
+ SandboxSnapshot,
15
+ } from "../sandbox/types";
12
16
  import type { VirtualFsOps } from "../virtual-fs/types";
13
17
  import type { RunAgentActivity } from "../model/types";
14
18
  import type {
@@ -138,17 +142,171 @@ export type PrefixedThreadOps<TPrefix extends string, TContent = string> = {
138
142
  [K in keyof ThreadOps<TContent> as `${TPrefix}${Capitalize<K & string>}`]: ThreadOps<TContent>[K];
139
143
  };
140
144
 
145
+ // ============================================================================
146
+ // Session sandbox-lifecycle decision table (SSOT)
147
+ //
148
+ // `createSession` (`src/lib/session/session.ts`) dispatches gated
149
+ // sandbox methods based on `(sandbox.mode, sandboxShutdown)`. When a
150
+ // caller omits either field, the session uses the documented defaults
151
+ // (`sandbox: { mode: "new" }` and `sandboxShutdown: "destroy"`),
152
+ // which trigger only base ops (`createSandbox` / `destroySandbox`).
153
+ //
154
+ // Two surfaces have to agree on what gated methods may fire for a
155
+ // given config:
156
+ //
157
+ // 1. The runtime — what the session dispatches per `(mode,
158
+ // shutdown)` pair.
159
+ // 2. The type level — what caps `sandboxOps` has to advertise.
160
+ //
161
+ // `SessionRequiredCaps<TInit, TShutdown>` is the SSOT for the type
162
+ // level. `resolveSessionLifecycle(init, shutdown)` (in `session.ts`,
163
+ // re-exported below) is the runtime mirror. Adding a new branch to
164
+ // `session.ts` requires extending **both**; the
165
+ // `(sandbox.mode × sandboxShutdown × adapter)` matrix in
166
+ // `src/lib/sandbox/capability-types.test.ts` enforces agreement.
167
+ // ============================================================================
168
+
169
+ /**
170
+ * Caps the session's sandbox-init dispatch may invoke for a given
171
+ * `(mode, shutdown)`. Mirror of `src/lib/session/session.ts:241-313`.
172
+ *
173
+ * The conditional branches are written non-distributively (each cap
174
+ * checked against the input `M` / `S` rather than over a union) so
175
+ * that the *omitted* case (defaults: M = "new", S = "destroy") flows
176
+ * through to `never`, not to "I don't know, assume everything."
177
+ */
178
+ type _SessionInitCaps<M, S> =
179
+ | (M extends "fork" ? "fork" : never)
180
+ | (M extends "from-snapshot" ? "restore" : never)
181
+ | (M extends "continue"
182
+ ? S extends "pause-until-parent-close"
183
+ ? "resume"
184
+ : never
185
+ : never);
186
+
187
+ /**
188
+ * Caps the session's exit dispatch may invoke. `"inherit"` mode keeps
189
+ * `sandboxOwned = false` so exit-shutdown caps NEVER fire — mirror of
190
+ * `src/lib/session/session.ts:598-615`.
191
+ */
192
+ type _SessionExitCaps<M, S> = M extends "inherit"
193
+ ? never
194
+ :
195
+ | (S extends "snapshot" ? "snapshot" : never)
196
+ | (S extends "pause" | "pause-until-parent-close" ? "pause" : never);
197
+
198
+ /**
199
+ * Cap captured on entry for `mode: "new"` + `shutdown: "snapshot"` (the
200
+ * seeding-base-snapshot path). Mirror of
201
+ * `src/lib/session/session.ts:316-317`.
202
+ */
203
+ type _SessionSeedCaps<M, S> = M extends "new"
204
+ ? S extends "snapshot"
205
+ ? "snapshot"
206
+ : never
207
+ : never;
208
+
209
+ /**
210
+ * Resolves an omitted `sandbox` field to the runtime default
211
+ * `{ mode: "new" }`, and an omitted `sandboxShutdown` field to the
212
+ * runtime default `"destroy"`. The two `_Resolve…` helpers are the
213
+ * sole bridge between "user didn't set the field" and the SSOT below
214
+ * — keeping the defaults out of the conditional table itself.
215
+ */
216
+ type _ResolveInit<TInit> = [TInit] extends [undefined]
217
+ ? { mode: "new" }
218
+ : Exclude<TInit, undefined> extends infer Defined
219
+ ? // If the user passed `SandboxInit | undefined` (the wide default),
220
+ // collapse `undefined` to the runtime default.
221
+ undefined extends TInit
222
+ ? Defined | { mode: "new" }
223
+ : Defined
224
+ : never;
225
+
226
+ type _ResolveShutdown<TShutdown> = [TShutdown] extends [undefined]
227
+ ? "destroy"
228
+ : Exclude<TShutdown, undefined> extends infer Defined
229
+ ? undefined extends TShutdown
230
+ ? Defined | "destroy"
231
+ : Defined
232
+ : never;
233
+
234
+ /**
235
+ * Sandbox capabilities a session actually invokes on its
236
+ * `sandboxOps`, derived from the literal types of the surrounding
237
+ * `sandbox` and `sandboxShutdown` fields.
238
+ *
239
+ * `TInit` / `TShutdown` default to `undefined` so an un-parameterised
240
+ * `SessionRequiredCaps` resolves to the caps the runtime *defaults*
241
+ * (`{ mode: "new" }` + `"destroy"`) actually require — i.e.
242
+ * `never`. This lets a narrow adapter (e.g. `proxyDaytonaSandboxOps`)
243
+ * satisfy `sandboxOps?: SandboxOps<…, SessionRequiredCaps<…>>` when
244
+ * the caller doesn't pin literals, matching what's runtime-safe.
245
+ *
246
+ * Pin literals via `as const` or by ascribing `SessionConfig<…, TInit,
247
+ * TShutdown>` to tighten the requirement on a per-call basis.
248
+ */
249
+ export type SessionRequiredCaps<
250
+ TInit extends SandboxInit | undefined = undefined,
251
+ TShutdown extends SubagentSandboxShutdown | undefined = undefined,
252
+ > = _ResolveInit<TInit> extends infer M
253
+ ? _ResolveShutdown<TShutdown> extends infer S
254
+ ? M extends { mode: infer Mode }
255
+ ? S extends SubagentSandboxShutdown
256
+ ?
257
+ | _SessionInitCaps<Mode, S>
258
+ | _SessionExitCaps<Mode, S>
259
+ | _SessionSeedCaps<Mode, S>
260
+ : never
261
+ : never
262
+ : never
263
+ : never;
264
+
265
+ /**
266
+ * Runtime mirror of `SessionRequiredCaps`. Returns the names of the
267
+ * gated `sandboxOps` methods the session will invoke for a given
268
+ * `(sandboxInit, sandboxShutdown)` pair (after resolving documented
269
+ * defaults). Mirror of the dispatch in
270
+ * `src/lib/session/session.ts:241-313` and `:598-615`.
271
+ *
272
+ * Both surfaces consult this same table — the `(mode × shutdown ×
273
+ * adapter)` matrix in `src/lib/sandbox/capability-types.test.ts`
274
+ * enforces agreement.
275
+ */
276
+ export function resolveSessionLifecycle(
277
+ init: SandboxInit | undefined,
278
+ shutdown: SubagentSandboxShutdown | undefined
279
+ ): { mode: SandboxInit["mode"]; shutdown: SubagentSandboxShutdown } {
280
+ const resolvedInit: SandboxInit = init ?? { mode: "new" };
281
+ const resolvedShutdown: SubagentSandboxShutdown = shutdown ?? "destroy";
282
+ return {
283
+ mode: resolvedInit.mode,
284
+ shutdown: resolvedShutdown,
285
+ };
286
+ }
287
+
141
288
  /**
142
289
  * Configuration for a Zeitlich agent session.
143
290
  *
144
291
  * @typeParam T - Tool map
145
292
  * @typeParam M - SDK-native message type returned by the model invoker
146
293
  * @typeParam TContent - SDK-native content type for human messages (defaults to `string`)
294
+ * @typeParam TInit - Literal type of `sandbox` (a {@link SandboxInit}
295
+ * variant or `undefined`). Defaults to the wide union; pin it via
296
+ * `as const` on the call site to narrow the cap requirement on
297
+ * `sandboxOps`.
298
+ * @typeParam TShutdown - Literal type of `sandboxShutdown`. Defaults
299
+ * to the wide union; pin it via `as const` on the call site to
300
+ * narrow the cap requirement on `sandboxOps`.
147
301
  */
148
302
  export interface SessionConfig<
149
303
  T extends ToolMap,
150
304
  M = unknown,
151
305
  TContent = string,
306
+ TInit extends SandboxInit | undefined = SandboxInit | undefined,
307
+ TShutdown extends
308
+ | SubagentSandboxShutdown
309
+ | undefined = SubagentSandboxShutdown | undefined,
152
310
  > {
153
311
  /** The name of the agent, should be unique within the workflows */
154
312
  agentName: string;
@@ -177,8 +335,6 @@ export interface SessionConfig<
177
335
  * Returns SDK-native content for the initial human message.
178
336
  */
179
337
  buildContextMessage: () => TContent | Promise<TContent>;
180
- /** How long to wait for input before cancelling the workflow */
181
- waitForInputTimeout?: Duration;
182
338
 
183
339
  // ---------------------------------------------------------------------------
184
340
  // Thread lifecycle
@@ -206,8 +362,25 @@ export interface SessionConfig<
206
362
  // Sandbox lifecycle
207
363
  // ---------------------------------------------------------------------------
208
364
 
209
- /** Sandbox lifecycle operations (optional — omit for agents that don't need a sandbox) */
210
- sandboxOps?: SandboxOps;
365
+ /**
366
+ * Sandbox lifecycle operations (optional — omit for agents that don't
367
+ * need a sandbox).
368
+ *
369
+ * The `TCaps` argument is derived from {@link SessionRequiredCaps}
370
+ * over the literal types of the surrounding `sandbox` and
371
+ * `sandboxShutdown` fields. With the default wide
372
+ * `TInit` / `TShutdown`, this resolves to the full
373
+ * {@link SandboxCapability} union (current behaviour). When the
374
+ * caller pins those literals via `as const`, the cap requirement
375
+ * tightens so a narrow adapter (e.g. Daytona's
376
+ * `SandboxOps<…, never>`) can satisfy combinations that don't need
377
+ * a gated method.
378
+ */
379
+ sandboxOps?: SandboxOps<
380
+ SandboxCreateOptions,
381
+ unknown,
382
+ SessionRequiredCaps<TInit, TShutdown> & SandboxCapability
383
+ >;
211
384
  /**
212
385
  * Sandbox initialization strategy.
213
386
  *
@@ -218,14 +391,14 @@ export interface SessionConfig<
218
391
  *
219
392
  * When omitted and `sandboxOps` is provided, defaults to `{ mode: "new" }`.
220
393
  */
221
- sandbox?: SandboxInit;
394
+ sandbox?: TInit;
222
395
  /**
223
396
  * What to do with the sandbox when this session exits.
224
397
  *
225
398
  * Defaults to `"destroy"` when omitted.
226
399
  * Has no effect when the sandbox is inherited (`sandbox.mode === "inherit"`).
227
400
  */
228
- sandboxShutdown?: SubagentSandboxShutdown;
401
+ sandboxShutdown?: TShutdown;
229
402
  /**
230
403
  * Called as soon as the sandbox is created (or resumed/forked), before the
231
404
  * agent loop starts. Useful for signalling sandbox readiness to a parent.
@@ -248,6 +421,14 @@ export interface SessionConfig<
248
421
  threadId: string;
249
422
  sandboxId?: string;
250
423
  snapshot?: SandboxSnapshot;
424
+ usage: {
425
+ totalInputTokens: number;
426
+ totalOutputTokens: number;
427
+ totalCachedWriteTokens: number;
428
+ totalCachedReadTokens: number;
429
+ totalReasonTokens: number;
430
+ turns: number;
431
+ };
251
432
  }) => void;
252
433
 
253
434
  // ---------------------------------------------------------------------------