zeitlich 0.2.35 → 0.2.37

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 (199) hide show
  1. package/README.md +146 -92
  2. package/dist/{activities-BVI2lTwr.d.ts → activities-Bb-nAjwQ.d.ts} +2 -2
  3. package/dist/{activities-hd4aNnZE.d.cts → activities-vkI4_3CC.d.cts} +2 -2
  4. package/dist/adapters/sandbox/bedrock/index.cjs +14 -11
  5. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
  7. package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
  8. package/dist/adapters/sandbox/bedrock/index.js +14 -11
  9. package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
  10. package/dist/adapters/sandbox/bedrock/workflow.cjs +2 -0
  11. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -1
  12. package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
  13. package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
  14. package/dist/adapters/sandbox/bedrock/workflow.js +2 -0
  15. package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
  16. package/dist/adapters/sandbox/daytona/index.cjs +35 -6
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
  19. package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
  20. package/dist/adapters/sandbox/daytona/index.js +35 -6
  21. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  22. package/dist/adapters/sandbox/daytona/workflow.cjs +2 -0
  23. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  24. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  25. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  26. package/dist/adapters/sandbox/daytona/workflow.js +2 -0
  27. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  28. package/dist/adapters/sandbox/e2b/index.cjs +59 -10
  29. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  30. package/dist/adapters/sandbox/e2b/index.d.cts +5 -3
  31. package/dist/adapters/sandbox/e2b/index.d.ts +5 -3
  32. package/dist/adapters/sandbox/e2b/index.js +59 -10
  33. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  34. package/dist/adapters/sandbox/e2b/workflow.cjs +2 -0
  35. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  36. package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
  37. package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
  38. package/dist/adapters/sandbox/e2b/workflow.js +2 -0
  39. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  40. package/dist/adapters/sandbox/inmemory/index.cjs +5 -0
  41. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  42. package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
  43. package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
  44. package/dist/adapters/sandbox/inmemory/index.js +5 -0
  45. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  46. package/dist/adapters/sandbox/inmemory/workflow.cjs +2 -0
  47. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  48. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  49. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  50. package/dist/adapters/sandbox/inmemory/workflow.js +2 -0
  51. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  52. package/dist/adapters/thread/anthropic/index.cjs +71 -36
  53. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  54. package/dist/adapters/thread/anthropic/index.d.cts +5 -5
  55. package/dist/adapters/thread/anthropic/index.d.ts +5 -5
  56. package/dist/adapters/thread/anthropic/index.js +71 -36
  57. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  58. package/dist/adapters/thread/anthropic/workflow.cjs +5 -1
  59. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  60. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
  61. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
  62. package/dist/adapters/thread/anthropic/workflow.js +5 -1
  63. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  64. package/dist/adapters/thread/google-genai/index.cjs +50 -25
  65. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  66. package/dist/adapters/thread/google-genai/index.d.cts +5 -5
  67. package/dist/adapters/thread/google-genai/index.d.ts +5 -5
  68. package/dist/adapters/thread/google-genai/index.js +50 -25
  69. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  70. package/dist/adapters/thread/google-genai/workflow.cjs +5 -1
  71. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  72. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
  73. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
  74. package/dist/adapters/thread/google-genai/workflow.js +5 -1
  75. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  76. package/dist/adapters/thread/langchain/index.cjs +34 -7
  77. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  78. package/dist/adapters/thread/langchain/index.d.cts +5 -5
  79. package/dist/adapters/thread/langchain/index.d.ts +5 -5
  80. package/dist/adapters/thread/langchain/index.js +34 -7
  81. package/dist/adapters/thread/langchain/index.js.map +1 -1
  82. package/dist/adapters/thread/langchain/workflow.cjs +5 -1
  83. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  84. package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
  85. package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
  86. package/dist/adapters/thread/langchain/workflow.js +5 -1
  87. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  88. package/dist/index.cjs +206 -120
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +17 -11
  91. package/dist/index.d.ts +17 -11
  92. package/dist/index.js +207 -121
  93. package/dist/index.js.map +1 -1
  94. package/dist/{proxy-BjdFGPTm.d.ts → proxy-0smGKvx8.d.ts} +1 -1
  95. package/dist/{proxy-7RnVaPdJ.d.cts → proxy-DEtowJyd.d.cts} +1 -1
  96. package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-3fszQih4.d.ts} +2 -2
  97. package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-C-C4pI2z.d.ts} +2 -2
  98. package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-CzYln2OC.d.cts} +2 -2
  99. package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-D4vgzYrh.d.cts} +2 -2
  100. package/dist/{types-yiXmqedU.d.ts → types-B37hKoWA.d.ts} +1 -1
  101. package/dist/{types-DQ1l_gXL.d.cts → types-BO7Yju20.d.cts} +63 -14
  102. package/dist/{types-wiGLvxWf.d.ts → types-CNuWnvy9.d.ts} +1 -1
  103. package/dist/{types-CADc5V_P.d.ts → types-CPKDl-y_.d.ts} +63 -14
  104. package/dist/{types-Mc_4BCfT.d.cts → types-D08CXPh8.d.cts} +1 -1
  105. package/dist/{types-CBH54cwr.d.cts → types-DWEUmYAJ.d.cts} +1 -1
  106. package/dist/{types-DxCpFNv_.d.cts → types-tQL9njTu.d.cts} +25 -0
  107. package/dist/{types-DxCpFNv_.d.ts → types-tQL9njTu.d.ts} +25 -0
  108. package/dist/{workflow-P2pTSfKu.d.ts → workflow-CjXHbZZc.d.ts} +2 -2
  109. package/dist/{workflow-DhtWRovz.d.cts → workflow-Do_lzJpT.d.cts} +2 -2
  110. package/dist/workflow.cjs +182 -114
  111. package/dist/workflow.cjs.map +1 -1
  112. package/dist/workflow.d.cts +3 -3
  113. package/dist/workflow.d.ts +3 -3
  114. package/dist/workflow.js +183 -115
  115. package/dist/workflow.js.map +1 -1
  116. package/package.json +1 -1
  117. package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
  118. package/src/adapters/sandbox/bedrock/index.ts +10 -8
  119. package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
  120. package/src/adapters/sandbox/daytona/filesystem.ts +29 -6
  121. package/src/adapters/sandbox/daytona/index.ts +6 -0
  122. package/src/adapters/sandbox/daytona/proxy.ts +2 -0
  123. package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
  124. package/src/adapters/sandbox/e2b/index.ts +63 -12
  125. package/src/adapters/sandbox/e2b/proxy.ts +2 -0
  126. package/src/adapters/sandbox/inmemory/index.ts +5 -0
  127. package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
  128. package/src/adapters/thread/anthropic/activities.ts +49 -26
  129. package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
  130. package/src/adapters/thread/anthropic/proxy.ts +6 -2
  131. package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
  132. package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
  133. package/src/adapters/thread/google-genai/activities.ts +7 -2
  134. package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
  135. package/src/adapters/thread/google-genai/proxy.ts +6 -2
  136. package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
  137. package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
  138. package/src/adapters/thread/langchain/activities.ts +46 -24
  139. package/src/adapters/thread/langchain/hooks.test.ts +36 -49
  140. package/src/adapters/thread/langchain/hooks.ts +18 -5
  141. package/src/adapters/thread/langchain/model-invoker.ts +3 -3
  142. package/src/adapters/thread/langchain/proxy.ts +6 -2
  143. package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
  144. package/src/adapters/thread/langchain/thread-manager.ts +20 -9
  145. package/src/index.ts +4 -1
  146. package/src/lib/activity.ts +16 -6
  147. package/src/lib/hooks/types.ts +6 -6
  148. package/src/lib/lifecycle.ts +9 -1
  149. package/src/lib/model/proxy.ts +2 -2
  150. package/src/lib/observability/hooks.ts +4 -5
  151. package/src/lib/observability/index.ts +1 -4
  152. package/src/lib/sandbox/manager.ts +21 -4
  153. package/src/lib/sandbox/node-fs.ts +3 -6
  154. package/src/lib/sandbox/sandbox.test.ts +36 -3
  155. package/src/lib/sandbox/tree.integration.test.ts +10 -3
  156. package/src/lib/sandbox/types.ts +35 -1
  157. package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
  158. package/src/lib/session/session.integration.test.ts +139 -0
  159. package/src/lib/session/session.ts +50 -19
  160. package/src/lib/session/types.ts +13 -5
  161. package/src/lib/skills/fs-provider.ts +12 -8
  162. package/src/lib/skills/handler.ts +1 -1
  163. package/src/lib/skills/parse.ts +3 -1
  164. package/src/lib/skills/register.ts +1 -3
  165. package/src/lib/skills/skills.integration.test.ts +25 -15
  166. package/src/lib/state/manager.integration.test.ts +12 -2
  167. package/src/lib/subagent/define.ts +1 -1
  168. package/src/lib/subagent/handler.ts +186 -71
  169. package/src/lib/subagent/index.ts +1 -5
  170. package/src/lib/subagent/register.ts +3 -2
  171. package/src/lib/subagent/signals.ts +1 -10
  172. package/src/lib/subagent/subagent.integration.test.ts +438 -156
  173. package/src/lib/subagent/tool.ts +4 -3
  174. package/src/lib/subagent/types.ts +50 -20
  175. package/src/lib/subagent/workflow.ts +9 -49
  176. package/src/lib/thread/id.test.ts +1 -1
  177. package/src/lib/thread/id.ts +1 -2
  178. package/src/lib/thread/proxy.ts +3 -4
  179. package/src/lib/thread/types.ts +11 -3
  180. package/src/lib/tool-router/index.ts +1 -5
  181. package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
  182. package/src/lib/tool-router/router.ts +3 -2
  183. package/src/lib/tool-router/types.ts +11 -3
  184. package/src/lib/tool-router/with-sandbox.ts +19 -5
  185. package/src/lib/virtual-fs/filesystem.ts +1 -1
  186. package/src/lib/virtual-fs/index.ts +5 -1
  187. package/src/lib/virtual-fs/mutations.ts +2 -4
  188. package/src/lib/virtual-fs/queries.ts +9 -5
  189. package/src/lib/virtual-fs/types.ts +4 -1
  190. package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
  191. package/src/lib/workflow.test.ts +7 -4
  192. package/src/lib/workflow.ts +1 -5
  193. package/src/tools/ask-user-question/tool.ts +1 -3
  194. package/src/tools/glob/handler.ts +1 -4
  195. package/src/tools/task-get/handler.ts +4 -5
  196. package/src/tools/task-list/handler.ts +1 -4
  197. package/src/tools/task-update/handler.ts +4 -5
  198. package/src/workflow.ts +20 -7
  199. package/tsup.config.ts +9 -6
@@ -7,7 +7,7 @@ import {
7
7
  } from "@temporalio/workflow";
8
8
  import type { SessionExitReason } from "../types";
9
9
  import type { SessionConfig, ZeitlichSession } from "./types";
10
- import type { SandboxOps } from "../sandbox/types";
10
+ import type { SandboxOps, SandboxSnapshot } from "../sandbox/types";
11
11
  import type {
12
12
  AgentState,
13
13
  AgentStateManager,
@@ -146,12 +146,14 @@ export async function createSession<
146
146
 
147
147
  const plugins: ToolMap[string][] = [];
148
148
  let destroySubagentSandboxes: (() => Promise<void>) | undefined;
149
+ let cleanupSubagentSnapshots: (() => Promise<void>) | undefined;
149
150
 
150
151
  if (subagents) {
151
152
  const result = buildSubagentRegistration(subagents);
152
153
  if (result) {
153
154
  plugins.push(result.registration);
154
155
  destroySubagentSandboxes = result.destroySubagentSandboxes;
156
+ cleanupSubagentSnapshots = result.cleanupSubagentSnapshots;
155
157
  }
156
158
  }
157
159
  if (skills) {
@@ -210,10 +212,13 @@ export async function createSession<
210
212
  }
211
213
  );
212
214
 
213
- // --- Sandbox lifecycle: create, continue, fork, or inherit ----------
215
+ // --- Sandbox lifecycle: create, continue, fork, from-snapshot, or inherit ---
214
216
  const sandboxMode = sandboxInit?.mode;
215
217
  let sandboxId: string | undefined;
216
218
  let sandboxOwned = false;
219
+ let baseSnapshot: SandboxSnapshot | undefined;
220
+ let exitSnapshot: SandboxSnapshot | undefined;
221
+ let freshlyCreated = false;
217
222
 
218
223
  if (sandboxMode === "inherit") {
219
224
  const inheritInit = sandboxInit as {
@@ -252,6 +257,18 @@ export async function createSession<
252
257
  (sandboxInit as { mode: "fork"; sandboxId: string }).sandboxId
253
258
  );
254
259
  sandboxOwned = true;
260
+ } else if (sandboxMode === "from-snapshot") {
261
+ if (!sandboxOps) {
262
+ throw ApplicationFailure.create({
263
+ message: "No sandboxOps provided — cannot restore sandbox",
264
+ nonRetryable: true,
265
+ });
266
+ }
267
+ const snap = (
268
+ sandboxInit as { mode: "from-snapshot"; snapshot: SandboxSnapshot }
269
+ ).snapshot;
270
+ sandboxId = await sandboxOps.restoreSandbox(snap);
271
+ sandboxOwned = true;
255
272
  } else if (sandboxOps) {
256
273
  const skillFiles = skills ? collectSkillFiles(skills) : undefined;
257
274
  const ctx = (sandboxInit as { mode: "new"; ctx?: unknown } | undefined)
@@ -263,10 +280,24 @@ export async function createSession<
263
280
  if (result) {
264
281
  sandboxId = result.sandboxId;
265
282
  sandboxOwned = true;
283
+ freshlyCreated = true;
266
284
  }
267
285
  }
268
286
 
269
- if (sandboxId && onSandboxReady) {
287
+ // Capture a base snapshot immediately after seeding so it can be reused
288
+ // as a template for future runs that want to skip the (potentially
289
+ // expensive) seed step.
290
+ if (
291
+ sandboxId &&
292
+ sandboxOwned &&
293
+ freshlyCreated &&
294
+ sandboxShutdown === "snapshot" &&
295
+ sandboxOps
296
+ ) {
297
+ baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
298
+ }
299
+
300
+ if (sandboxId && sandboxOwned && onSandboxReady) {
270
301
  onSandboxReady(sandboxId);
271
302
  }
272
303
 
@@ -347,6 +378,7 @@ export async function createSession<
347
378
  );
348
379
 
349
380
  let exitReason: SessionExitReason = "completed";
381
+ let finalMessage: M | null = null;
350
382
 
351
383
  try {
352
384
  while (
@@ -385,21 +417,8 @@ export async function createSession<
385
417
  if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
386
418
  stateManager.complete();
387
419
  exitReason = "completed";
388
- log.info("session ended", {
389
- agentName,
390
- threadId,
391
- exitReason,
392
- turns: currentTurn,
393
- durationMs: Date.now() - sessionStartMs,
394
- usage: stateManager.getTotalUsage(),
395
- });
396
- return {
397
- threadId,
398
- finalMessage: message,
399
- exitReason,
400
- usage: stateManager.getTotalUsage(),
401
- sandboxId,
402
- } as Awaited<ReturnType<ZeitlichSession<M, boolean>["runSession"]>>;
420
+ finalMessage = message;
421
+ break;
403
422
  }
404
423
 
405
424
  const parsedToolCalls: ParsedToolCallUnion<T>[] = [];
@@ -480,12 +499,20 @@ export async function createSession<
480
499
  case "keep":
481
500
  case "keep-until-parent-close":
482
501
  break;
502
+ case "snapshot":
503
+ exitSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
504
+ await sandboxOps.destroySandbox(sandboxId);
505
+ break;
483
506
  }
484
507
  }
485
508
 
486
509
  if (destroySubagentSandboxes) {
487
510
  await destroySubagentSandboxes();
488
511
  }
512
+
513
+ if (cleanupSubagentSnapshots) {
514
+ await cleanupSubagentSnapshots();
515
+ }
489
516
  }
490
517
 
491
518
  log.info("session ended", {
@@ -495,14 +522,18 @@ export async function createSession<
495
522
  turns: stateManager.getTurns(),
496
523
  durationMs: Date.now() - sessionStartMs,
497
524
  usage: stateManager.getTotalUsage(),
525
+ ...(baseSnapshot && { hasBaseSnapshot: true }),
526
+ ...(exitSnapshot && { hasExitSnapshot: true }),
498
527
  });
499
528
 
500
529
  return {
501
530
  threadId,
502
- finalMessage: null,
531
+ finalMessage,
503
532
  exitReason,
504
533
  usage: stateManager.getTotalUsage(),
505
534
  sandboxId,
535
+ ...(baseSnapshot && { baseSnapshot }),
536
+ ...(exitSnapshot && { snapshot: exitSnapshot }),
506
537
  } as Awaited<ReturnType<ZeitlichSession<M, boolean>["runSession"]>>;
507
538
  },
508
539
  };
@@ -1,8 +1,5 @@
1
1
  import type { Duration } from "@temporalio/common";
2
- import type {
3
- SessionExitReason,
4
- ToolResultConfig,
5
- } from "../types";
2
+ import type { SessionExitReason, ToolResultConfig } from "../types";
6
3
  import type {
7
4
  ToolMap,
8
5
  ToolCallResultUnion,
@@ -11,7 +8,7 @@ import type {
11
8
  import type { Hooks } from "../hooks/types";
12
9
  import type { SubagentConfig } from "../subagent/types";
13
10
  import type { Skill } from "../skills/types";
14
- import type { SandboxOps } from "../sandbox/types";
11
+ import type { SandboxOps, SandboxSnapshot } from "../sandbox/types";
15
12
  import type { VirtualFsOps } from "../virtual-fs/types";
16
13
  import type { RunAgentActivity } from "../model/types";
17
14
  import type { AgentStateManager, JsonSerializable } from "../state/types";
@@ -219,6 +216,17 @@ export type SessionResult<
219
216
  finalMessage: M | null;
220
217
  exitReason: SessionExitReason;
221
218
  usage: ReturnType<AgentStateManager<TState>["getTotalUsage"]>;
219
+ /**
220
+ * Snapshot captured on exit when `sandboxShutdown === "snapshot"`.
221
+ */
222
+ snapshot?: SandboxSnapshot;
223
+ /**
224
+ * Snapshot captured immediately after sandbox seeding (before the agent
225
+ * loop starts) when `sandbox.mode === "new"` and
226
+ * `sandboxShutdown === "snapshot"`. Intended as a reusable "base" for new
227
+ * threads that want to skip re-seeding.
228
+ */
229
+ baseSnapshot?: SandboxSnapshot;
222
230
  } & (HasSandbox extends true
223
231
  ? { sandboxId: string }
224
232
  : { sandboxId?: undefined });
@@ -24,7 +24,7 @@ import { parseSkillFile } from "./parse";
24
24
  export class FileSystemSkillProvider implements SkillProvider {
25
25
  constructor(
26
26
  private readonly fs: SandboxFileSystem,
27
- private readonly baseDir: string,
27
+ private readonly baseDir: string
28
28
  ) {}
29
29
 
30
30
  async listSkills(): Promise<SkillMetadata[]> {
@@ -45,20 +45,21 @@ export class FileSystemSkillProvider implements SkillProvider {
45
45
  }
46
46
 
47
47
  async getSkill(name: string): Promise<Skill> {
48
- const raw = await this.fs.readFile(
49
- join(this.baseDir, name, "SKILL.md"),
50
- );
48
+ const raw = await this.fs.readFile(join(this.baseDir, name, "SKILL.md"));
51
49
  const { frontmatter, body } = parseSkillFile(raw);
52
50
 
53
51
  if (frontmatter.name !== name) {
54
52
  throw new Error(
55
- `Skill directory "${name}" contains SKILL.md with mismatched name "${frontmatter.name}"`,
53
+ `Skill directory "${name}" contains SKILL.md with mismatched name "${frontmatter.name}"`
56
54
  );
57
55
  }
58
56
 
59
57
  const location = join(this.baseDir, name);
60
58
  const resourcePaths = await this.discoverResources(name);
61
- const resourceContents = await this.readResourceContents(location, resourcePaths);
59
+ const resourceContents = await this.readResourceContents(
60
+ location,
61
+ resourcePaths
62
+ );
62
63
  return {
63
64
  ...frontmatter,
64
65
  instructions: body,
@@ -80,7 +81,10 @@ export class FileSystemSkillProvider implements SkillProvider {
80
81
  const { frontmatter, body } = parseSkillFile(raw);
81
82
  const location = join(this.baseDir, dir);
82
83
  const resourcePaths = await this.discoverResources(dir);
83
- const resourceContents = await this.readResourceContents(location, resourcePaths);
84
+ const resourceContents = await this.readResourceContents(
85
+ location,
86
+ resourcePaths
87
+ );
84
88
  skills.push({
85
89
  ...frontmatter,
86
90
  instructions: body,
@@ -119,7 +123,7 @@ export class FileSystemSkillProvider implements SkillProvider {
119
123
 
120
124
  private async readResourceContents(
121
125
  location: string,
122
- resources: string[],
126
+ resources: string[]
123
127
  ): Promise<Record<string, string> | undefined> {
124
128
  if (resources.length === 0) return undefined;
125
129
  const contents: Record<string, string> = {};
@@ -18,7 +18,7 @@ function formatSkillResponse(skill: Skill): string {
18
18
  if (skill.location) {
19
19
  parts.push(`\nSkill directory: ${skill.location}`);
20
20
  parts.push(
21
- "Relative paths in this skill resolve against the skill directory above.",
21
+ "Relative paths in this skill resolve against the skill directory above."
22
22
  );
23
23
  }
24
24
 
@@ -12,7 +12,9 @@ export function parseSkillFile(raw: string): {
12
12
  body: string;
13
13
  } {
14
14
  const trimmed = raw.replace(/^\uFEFF/, ""); // strip BOM
15
- const match = trimmed.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/);
15
+ const match = trimmed.match(
16
+ /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n?([\s\S]*)$/
17
+ );
16
18
 
17
19
  if (!match) {
18
20
  throw new Error(
@@ -11,9 +11,7 @@ function validateSkillNames(skills: SkillMetadata[]): void {
11
11
  const names = skills.map((s) => s.name);
12
12
  const dupes = names.filter((n, i) => names.indexOf(n) !== i);
13
13
  if (dupes.length > 0) {
14
- throw new Error(
15
- `Duplicate skill names: ${[...new Set(dupes)].join(", ")}`
16
- );
14
+ throw new Error(`Duplicate skill names: ${[...new Set(dupes)].join(", ")}`);
17
15
  }
18
16
  }
19
17
 
@@ -47,7 +47,10 @@ Body content here.`;
47
47
  expect(frontmatter.license).toBe("MIT");
48
48
  expect(frontmatter.compatibility).toBe("linux-only");
49
49
  expect(frontmatter.allowedTools).toEqual(["bash", "grep", "read-file"]);
50
- expect(frontmatter.metadata).toEqual({ author: "test-author", version: "1.0" });
50
+ expect(frontmatter.metadata).toEqual({
51
+ author: "test-author",
52
+ version: "1.0",
53
+ });
51
54
  expect(body).toBe("Body content here.");
52
55
  });
53
56
 
@@ -76,7 +79,7 @@ Body`;
76
79
 
77
80
  it("throws when frontmatter is missing", () => {
78
81
  expect(() => parseSkillFile("No frontmatter here")).toThrow(
79
- "SKILL.md must start with YAML frontmatter",
82
+ "SKILL.md must start with YAML frontmatter"
80
83
  );
81
84
  });
82
85
 
@@ -87,7 +90,7 @@ description: Missing name
87
90
  Body`;
88
91
 
89
92
  expect(() => parseSkillFile(raw)).toThrow(
90
- "SKILL.md frontmatter must include a 'name' field",
93
+ "SKILL.md frontmatter must include a 'name' field"
91
94
  );
92
95
  });
93
96
 
@@ -98,7 +101,7 @@ name: no-desc
98
101
  Body`;
99
102
 
100
103
  expect(() => parseSkillFile(raw)).toThrow(
101
- "SKILL.md frontmatter must include a 'description' field",
104
+ "SKILL.md frontmatter must include a 'description' field"
102
105
  );
103
106
  });
104
107
 
@@ -114,7 +117,8 @@ description: No body content
114
117
  });
115
118
 
116
119
  it("handles CRLF line endings", () => {
117
- const raw = "---\r\nname: crlf-skill\r\ndescription: CRLF test\r\n---\r\nBody with CRLF";
120
+ const raw =
121
+ "---\r\nname: crlf-skill\r\ndescription: CRLF test\r\n---\r\nBody with CRLF";
118
122
 
119
123
  const { frontmatter, body } = parseSkillFile(raw);
120
124
  expect(frontmatter.name).toBe("crlf-skill");
@@ -204,7 +208,7 @@ describe("createReadSkillTool", () => {
204
208
 
205
209
  it("throws when no skills are provided", () => {
206
210
  expect(() => createReadSkillTool([])).toThrow(
207
- "createReadSkillTool requires at least one skill",
211
+ "createReadSkillTool requires at least one skill"
208
212
  );
209
213
  });
210
214
  });
@@ -242,7 +246,9 @@ describe("createReadSkillHandler", () => {
242
246
 
243
247
  const text = result.toolResponse as string;
244
248
  expect(text).toContain("Skill directory: /skills/skill-a");
245
- expect(text).toContain("Relative paths in this skill resolve against the skill directory above.");
249
+ expect(text).toContain(
250
+ "Relative paths in this skill resolve against the skill directory above."
251
+ );
246
252
  });
247
253
 
248
254
  it("lists resources derived from resourceContents keys", () => {
@@ -298,17 +304,21 @@ describe("createReadSkillHandler", () => {
298
304
  const result = handler({ skill_name: "nonexistent" });
299
305
 
300
306
  expect(typeof result.toolResponse).toBe("string");
301
- expect((result.toolResponse as string)).toContain("not found");
307
+ expect(result.toolResponse as string).toContain("not found");
302
308
  expect(result.data).toBeNull();
303
309
  });
304
310
 
305
311
  it("handles single skill", () => {
306
312
  const skills: Skill[] = [
307
- { name: "skill-a", description: "First", instructions: "Instructions for A" },
313
+ {
314
+ name: "skill-a",
315
+ description: "First",
316
+ instructions: "Instructions for A",
317
+ },
308
318
  ];
309
319
  const handler = createReadSkillHandler(skills);
310
320
  const result = handler({ skill_name: "skill-a" });
311
- expect((result.toolResponse as string)).toContain("Instructions for A");
321
+ expect(result.toolResponse as string).toContain("Instructions for A");
312
322
  });
313
323
  });
314
324
 
@@ -350,7 +360,9 @@ describe("buildSkillRegistration", () => {
350
360
  { name: "dupe", description: "First", instructions: "A" },
351
361
  { name: "dupe", description: "Second", instructions: "B" },
352
362
  ];
353
- expect(() => buildSkillRegistration(skills)).toThrow("Duplicate skill names: dupe");
363
+ expect(() => buildSkillRegistration(skills)).toThrow(
364
+ "Duplicate skill names: dupe"
365
+ );
354
366
  });
355
367
 
356
368
  it("returns a complete tool entry with handler", () => {
@@ -389,7 +401,7 @@ describe("buildSkillRegistration", () => {
389
401
  if (!registration) return;
390
402
  const result = registration.handler(
391
403
  { skill_name: "test-skill" },
392
- { threadId: "t-1", toolCallId: "tc-1", toolName: "ReadSkill" },
404
+ { threadId: "t-1", toolCallId: "tc-1", toolName: "ReadSkill" }
393
405
  );
394
406
 
395
407
  if (result instanceof Promise) {
@@ -414,9 +426,7 @@ describe("buildSkillRegistration", () => {
414
426
  // FileSystemSkillProvider — resource discovery
415
427
  // ---------------------------------------------------------------------------
416
428
 
417
- function createMockFs(
418
- tree: Record<string, string | "DIR">,
419
- ): SandboxFileSystem {
429
+ function createMockFs(tree: Record<string, string | "DIR">): SandboxFileSystem {
420
430
  const dir = (entries: DirentEntry[]): DirentEntry[] => entries;
421
431
 
422
432
  return {
@@ -11,7 +11,13 @@ vi.mock("@temporalio/workflow", () => {
11
11
  setHandler: (_def: unknown, _handler: unknown) => {},
12
12
  uuid4: () =>
13
13
  `00000000-0000-0000-0000-${String(++idCounter).padStart(12, "0")}`,
14
- log: { trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
14
+ log: {
15
+ trace: () => {},
16
+ debug: () => {},
17
+ info: () => {},
18
+ warn: () => {},
19
+ error: () => {},
20
+ },
15
21
  };
16
22
  });
17
23
 
@@ -148,7 +154,11 @@ describe("createAgentStateManager integration", () => {
148
154
  const sm = createAgentStateManager({});
149
155
 
150
156
  sm.updateUsage({ inputTokens: 100, outputTokens: 50 });
151
- sm.updateUsage({ inputTokens: 200, outputTokens: 100, cachedWriteTokens: 30 });
157
+ sm.updateUsage({
158
+ inputTokens: 200,
159
+ outputTokens: 100,
160
+ cachedWriteTokens: 30,
161
+ });
152
162
  sm.updateUsage({ cachedReadTokens: 20, reasonTokens: 10 });
153
163
 
154
164
  expect(sm.getTotalUsage()).toEqual({
@@ -45,7 +45,7 @@ export function defineSubagent<
45
45
  taskQueue?: string;
46
46
  thread?: "new" | "fork" | "continue";
47
47
  sandbox?: SubagentSandboxConfig;
48
- },
48
+ }
49
49
  ): SubagentConfig<TResult> {
50
50
  return {
51
51
  agentName: definition.agentName,