zeitlich 0.2.28 → 0.2.30

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 (144) hide show
  1. package/README.md +121 -13
  2. package/dist/{activities-3xj_fEJK.d.ts → activities-BeveyY9b.d.cts} +2 -3
  3. package/dist/{activities-BzYq6jf7.d.cts → activities-NT3rcw66.d.ts} +2 -3
  4. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  5. package/dist/adapters/sandbox/bedrock/index.d.cts +3 -3
  6. package/dist/adapters/sandbox/bedrock/index.d.ts +3 -3
  7. package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
  8. package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
  9. package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
  10. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  11. package/dist/adapters/sandbox/daytona/index.d.cts +1 -1
  12. package/dist/adapters/sandbox/daytona/index.d.ts +1 -1
  13. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  14. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  15. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  16. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  17. package/dist/adapters/sandbox/e2b/index.d.cts +1 -1
  18. package/dist/adapters/sandbox/e2b/index.d.ts +1 -1
  19. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  20. package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
  21. package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
  22. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  23. package/dist/adapters/sandbox/inmemory/index.d.cts +1 -1
  24. package/dist/adapters/sandbox/inmemory/index.d.ts +1 -1
  25. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  26. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  27. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  28. package/dist/adapters/thread/anthropic/index.cjs +0 -1
  29. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  30. package/dist/adapters/thread/anthropic/index.d.cts +6 -7
  31. package/dist/adapters/thread/anthropic/index.d.ts +6 -7
  32. package/dist/adapters/thread/anthropic/index.js +0 -1
  33. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  34. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -6
  35. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -6
  36. package/dist/adapters/thread/google-genai/index.cjs +0 -1
  37. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  38. package/dist/adapters/thread/google-genai/index.d.cts +6 -7
  39. package/dist/adapters/thread/google-genai/index.d.ts +6 -7
  40. package/dist/adapters/thread/google-genai/index.js +0 -1
  41. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  42. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -6
  43. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -6
  44. package/dist/adapters/thread/langchain/index.cjs +0 -1
  45. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  46. package/dist/adapters/thread/langchain/index.d.cts +6 -7
  47. package/dist/adapters/thread/langchain/index.d.ts +6 -7
  48. package/dist/adapters/thread/langchain/index.js +0 -1
  49. package/dist/adapters/thread/langchain/index.js.map +1 -1
  50. package/dist/adapters/thread/langchain/workflow.d.cts +5 -6
  51. package/dist/adapters/thread/langchain/workflow.d.ts +5 -6
  52. package/dist/index.cjs +558 -50
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.d.cts +136 -22
  55. package/dist/index.d.ts +136 -22
  56. package/dist/index.js +554 -52
  57. package/dist/index.js.map +1 -1
  58. package/dist/{proxy-7e7v8ccg.d.ts → proxy-BgswT47M.d.ts} +1 -1
  59. package/dist/{proxy-CsB8r0RR.d.cts → proxy-OJihshQF.d.cts} +1 -1
  60. package/dist/{thread-manager-D8C5QvLi.d.ts → thread-manager-BS477gj8.d.ts} +1 -1
  61. package/dist/{thread-manager-DdVFl1IY.d.cts → thread-manager-DH0zv05W.d.cts} +1 -1
  62. package/dist/{thread-manager-B5qA4v7V.d.ts → thread-manager-iUplxEZt.d.ts} +1 -1
  63. package/dist/{thread-manager-DFJ3sKKU.d.cts → thread-manager-lfN0V-gH.d.cts} +1 -1
  64. package/dist/{types-ChAMwU3q.d.cts → types-AujBIMMn.d.cts} +5 -8
  65. package/dist/{types-ChAMwU3q.d.ts → types-AujBIMMn.d.ts} +5 -8
  66. package/dist/{types-BZ75HpYd.d.ts → types-CCIc7Eam.d.ts} +1 -1
  67. package/dist/types-D90Q5aOh.d.ts +1212 -0
  68. package/dist/{types-BdCdR41N.d.ts → types-DBk-C8zM.d.ts} +1 -1
  69. package/dist/{types-ZHs2v9Ap.d.cts → types-DUvEZSDe.d.cts} +1 -1
  70. package/dist/types-DVdT5ybA.d.cts +1212 -0
  71. package/dist/{types-HbjqzyJH.d.cts → types-DgIVPOa1.d.cts} +1 -1
  72. package/dist/workflow-Cj4DxGdM.d.cts +750 -0
  73. package/dist/workflow-CzrBdCcJ.d.ts +750 -0
  74. package/dist/workflow.cjs +194 -40
  75. package/dist/workflow.cjs.map +1 -1
  76. package/dist/workflow.d.cts +5 -579
  77. package/dist/workflow.d.ts +5 -579
  78. package/dist/workflow.js +193 -42
  79. package/dist/workflow.js.map +1 -1
  80. package/package.json +3 -23
  81. package/src/adapters/thread/anthropic/thread-manager.ts +6 -6
  82. package/src/adapters/thread/google-genai/thread-manager.ts +6 -6
  83. package/src/adapters/thread/langchain/thread-manager.ts +6 -6
  84. package/src/index.ts +8 -0
  85. package/src/lib/lifecycle.ts +8 -3
  86. package/src/lib/observability/hooks.ts +117 -0
  87. package/src/lib/observability/index.ts +13 -0
  88. package/src/lib/observability/sinks.ts +88 -0
  89. package/src/lib/sandbox/index.ts +2 -4
  90. package/src/lib/sandbox/manager.ts +131 -16
  91. package/src/lib/sandbox/sandbox.test.ts +136 -16
  92. package/src/lib/sandbox/types.ts +6 -5
  93. package/src/lib/session/session-edge-cases.integration.test.ts +1 -0
  94. package/src/lib/session/session.integration.test.ts +7 -39
  95. package/src/lib/session/session.ts +119 -42
  96. package/src/lib/session/types.ts +39 -9
  97. package/src/lib/state/manager.integration.test.ts +1 -0
  98. package/src/lib/state/types.ts +9 -6
  99. package/src/lib/subagent/handler.ts +35 -12
  100. package/src/lib/subagent/register.ts +11 -12
  101. package/src/lib/subagent/subagent.integration.test.ts +1 -0
  102. package/src/lib/tool-router/router-edge-cases.integration.test.ts +2 -0
  103. package/src/lib/tool-router/router.integration.test.ts +2 -0
  104. package/src/lib/tool-router/router.ts +24 -2
  105. package/src/lib/types.ts +2 -0
  106. package/src/{adapters/sandbox/virtual → lib/virtual-fs}/filesystem.ts +4 -4
  107. package/src/lib/virtual-fs/index.ts +18 -0
  108. package/src/lib/virtual-fs/manager.ts +48 -0
  109. package/src/lib/virtual-fs/proxy.ts +45 -0
  110. package/src/{adapters/sandbox/virtual → lib/virtual-fs}/types.ts +41 -37
  111. package/src/{adapters/sandbox/virtual/virtual-sandbox.test.ts → lib/virtual-fs/virtual-fs.test.ts} +15 -130
  112. package/src/lib/virtual-fs/with-virtual-fs.ts +94 -0
  113. package/src/tools/bash/bash.test.ts +2 -1
  114. package/src/workflow.ts +25 -8
  115. package/tsup.config.ts +0 -2
  116. package/dist/adapters/sandbox/virtual/index.cjs +0 -487
  117. package/dist/adapters/sandbox/virtual/index.cjs.map +0 -1
  118. package/dist/adapters/sandbox/virtual/index.d.cts +0 -90
  119. package/dist/adapters/sandbox/virtual/index.d.ts +0 -90
  120. package/dist/adapters/sandbox/virtual/index.js +0 -479
  121. package/dist/adapters/sandbox/virtual/index.js.map +0 -1
  122. package/dist/adapters/sandbox/virtual/workflow.cjs +0 -33
  123. package/dist/adapters/sandbox/virtual/workflow.cjs.map +0 -1
  124. package/dist/adapters/sandbox/virtual/workflow.d.cts +0 -28
  125. package/dist/adapters/sandbox/virtual/workflow.d.ts +0 -28
  126. package/dist/adapters/sandbox/virtual/workflow.js +0 -31
  127. package/dist/adapters/sandbox/virtual/workflow.js.map +0 -1
  128. package/dist/queries-DVnukByF.d.cts +0 -44
  129. package/dist/queries-kjlvsUfz.d.ts +0 -44
  130. package/dist/types-BclYm5Ic.d.cts +0 -581
  131. package/dist/types-BclYm5Ic.d.ts +0 -581
  132. package/dist/types-BgsAwN3L.d.cts +0 -125
  133. package/dist/types-BtqbM1bO.d.ts +0 -490
  134. package/dist/types-BuCEZ4dF.d.cts +0 -490
  135. package/dist/types-yU5AINiP.d.ts +0 -125
  136. package/src/adapters/sandbox/virtual/index.ts +0 -92
  137. package/src/adapters/sandbox/virtual/provider.ts +0 -121
  138. package/src/adapters/sandbox/virtual/proxy.ts +0 -53
  139. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +0 -97
  140. package/src/lib/.env +0 -1
  141. package/src/tools/bash/.env +0 -1
  142. /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/mutations.ts +0 -0
  143. /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/queries.ts +0 -0
  144. /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/tree.ts +0 -0
@@ -1,8 +1,5 @@
1
1
  import type { Duration } from "@temporalio/common";
2
- import type {
3
- ToolResultConfig,
4
- SessionExitReason,
5
- } from "../types";
2
+ import type { ToolResultConfig, SessionExitReason } from "../types";
6
3
  import type {
7
4
  ToolMap,
8
5
  ToolCallResultUnion,
@@ -12,10 +9,15 @@ import type { Hooks } from "../hooks/types";
12
9
  import type { SubagentConfig } from "../subagent/types";
13
10
  import type { Skill } from "../skills/types";
14
11
  import type { SandboxOps } from "../sandbox/types";
12
+ import type { VirtualFsOps } from "../virtual-fs/types";
15
13
  import type { RunAgentActivity } from "../model/types";
16
14
  import type { AgentStateManager, JsonSerializable } from "../state/types";
17
15
  import type { ActivityInterfaceFor } from "@temporalio/workflow";
18
- import type { ThreadInit, SandboxInit, SubagentSandboxShutdown } from "../lifecycle";
16
+ import type {
17
+ ThreadInit,
18
+ SandboxInit,
19
+ SubagentSandboxShutdown,
20
+ } from "../lifecycle";
19
21
 
20
22
  /**
21
23
  * Thread operations required by a session.
@@ -33,7 +35,7 @@ export interface ThreadOps<TContent = string> {
33
35
  threadId: string,
34
36
  id: string,
35
37
  content: TContent,
36
- threadKey?: string,
38
+ threadKey?: string
37
39
  ): Promise<void>;
38
40
  /** Append a tool result to the thread */
39
41
  appendToolResult(id: string, config: ToolResultConfig): Promise<void>;
@@ -42,10 +44,14 @@ export interface ThreadOps<TContent = string> {
42
44
  threadId: string,
43
45
  id: string,
44
46
  content: string,
45
- threadKey?: string,
47
+ threadKey?: string
46
48
  ): Promise<void>;
47
49
  /** Copy all messages from sourceThreadId into a new thread at targetThreadId */
48
- forkThread(sourceThreadId: string, targetThreadId: string, threadKey?: string): Promise<void>;
50
+ forkThread(
51
+ sourceThreadId: string,
52
+ targetThreadId: string,
53
+ threadKey?: string
54
+ ): Promise<void>;
49
55
  }
50
56
 
51
57
  /**
@@ -86,7 +92,11 @@ export type PrefixedThreadOps<TPrefix extends string, TContent = string> = {
86
92
  * @typeParam M - SDK-native message type returned by the model invoker
87
93
  * @typeParam TContent - SDK-native content type for human messages (defaults to `string`)
88
94
  */
89
- export interface SessionConfig<T extends ToolMap, M = unknown, TContent = string> {
95
+ export interface SessionConfig<
96
+ T extends ToolMap,
97
+ M = unknown,
98
+ TContent = string,
99
+ > {
90
100
  /** The name of the agent, should be unique within the workflows */
91
101
  agentName: string;
92
102
  /** Metadata for the session */
@@ -163,6 +173,26 @@ export interface SessionConfig<T extends ToolMap, M = unknown, TContent = string
163
173
  * Has no effect when the sandbox is inherited (`sandbox.mode === "inherit"`).
164
174
  */
165
175
  sandboxShutdown?: SubagentSandboxShutdown;
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // Virtual filesystem
179
+ // ---------------------------------------------------------------------------
180
+
181
+ virtualFsOps?: VirtualFsOps;
182
+
183
+ /**
184
+ * Virtual filesystem configuration (optional — independent of sandbox).
185
+ *
186
+ * When provided, the session resolves the file tree on start and merges
187
+ * `fileTree`, `ctx`, and `workspaceBase` into `AgentState`.
188
+ * Tool handlers wrapped with `withVirtualFs` can then read this state.
189
+ *
190
+ * Can be used alongside `sandboxOps` for agents that need both a real
191
+ * sandbox (e.g. for execution) and a virtual filesystem.
192
+ */
193
+ virtualFs?: {
194
+ ctx: unknown;
195
+ };
166
196
  }
167
197
 
168
198
  export type SessionResult<
@@ -11,6 +11,7 @@ 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
15
  };
15
16
  });
16
17
 
@@ -1,8 +1,11 @@
1
- import type {
2
- QueryDefinition,
3
- } from "@temporalio/workflow";
1
+ import type { QueryDefinition } from "@temporalio/workflow";
4
2
  import type { UpdateDefinition } from "@temporalio/common/lib/interfaces";
5
- import type { AgentStatus, BaseAgentState, TokenUsage, WorkflowTask } from "../types";
3
+ import type {
4
+ AgentStatus,
5
+ BaseAgentState,
6
+ TokenUsage,
7
+ WorkflowTask,
8
+ } from "../types";
6
9
  import type { ToolDefinition } from "../tool-router/types";
7
10
 
8
11
  /**
@@ -98,8 +101,8 @@ export interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
98
101
  /** Set a custom state value by key */
99
102
  set<K extends keyof TCustom>(key: K, value: TCustom[K]): void;
100
103
 
101
- /** Bulk-merge a partial update into custom state (e.g. from sandbox stateUpdate) */
102
- mergeUpdate(update: Partial<TCustom>): void;
104
+ /** Bulk-merge a partial update into custom state */
105
+ mergeUpdate(update: Partial<AgentState<TCustom>>): void;
103
106
 
104
107
  /** Get full state for query handler */
105
108
  getCurrentState(): AgentState<TCustom>;
@@ -17,7 +17,11 @@ import type {
17
17
  } from "./types";
18
18
  import type { SubagentArgs } from "./tool";
19
19
  import type { z } from "zod";
20
- import type { ThreadInit, SandboxInit, SubagentSandboxShutdown } from "../lifecycle";
20
+ import type {
21
+ ThreadInit,
22
+ SandboxInit,
23
+ SubagentSandboxShutdown,
24
+ } from "../lifecycle";
21
25
  import { childResultSignal, destroySandboxSignal } from "./signals";
22
26
 
23
27
  /**
@@ -45,9 +49,9 @@ function resolveSandboxConfig(config?: SubagentSandboxConfig): {
45
49
  */
46
50
  export function createSubagentHandler<
47
51
  const T extends readonly SubagentConfig[],
48
- >(subagents: [...T], options?: {
49
- getSandboxStateForInheritance?: () => Record<string, unknown> | undefined;
50
- }): {
52
+ >(
53
+ subagents: [...T]
54
+ ): {
51
55
  handler: (
52
56
  args: SubagentArgs,
53
57
  context: RouterContext
@@ -99,18 +103,18 @@ export function createSubagentHandler<
99
103
  // --- Build thread init ---
100
104
  let thread: ThreadInit | undefined;
101
105
  if (continuationThreadId) {
102
- thread = { mode: threadMode as "fork" | "continue", threadId: continuationThreadId };
103
-
106
+ thread = {
107
+ mode: threadMode as "fork" | "continue",
108
+ threadId: continuationThreadId,
109
+ };
104
110
  }
105
111
 
106
112
  // --- Build sandbox init ---
107
113
  let sandbox: SandboxInit | undefined;
108
114
  if (sandboxCfg.source === "inherit" && parentSandboxId) {
109
- const stateUpdate = options?.getSandboxStateForInheritance?.();
110
115
  sandbox = {
111
116
  mode: "inherit",
112
117
  sandboxId: parentSandboxId,
113
- ...(stateUpdate && { stateUpdate }),
114
118
  };
115
119
  } else if (sandboxCfg.source === "own") {
116
120
  const prevSbId = continuationThreadId
@@ -144,12 +148,20 @@ export function createSubagentHandler<
144
148
  taskQueue: config.taskQueue ?? parentTaskQueue,
145
149
  };
146
150
 
151
+ log.info("subagent spawned", {
152
+ subagent: config.agentName,
153
+ childWorkflowId,
154
+ threadMode,
155
+ sandboxSource: sandboxCfg.source,
156
+ });
157
+
147
158
  const childHandle = await startChild(config.workflow, childOpts);
148
159
 
149
160
  const effectiveShutdown = sandboxCfg.shutdown ?? "destroy";
150
161
  const shouldDeferDestroy =
151
162
  effectiveShutdown === "pause-until-parent-close" &&
152
- (sandboxCfg.source === "own" || (allowsContinuation && sandboxCfg.source !== "inherit"));
163
+ (sandboxCfg.source === "own" ||
164
+ (allowsContinuation && sandboxCfg.source !== "inherit"));
153
165
 
154
166
  if (shouldDeferDestroy) {
155
167
  pendingDestroys.set(childWorkflowId, childHandle);
@@ -168,12 +180,22 @@ export function createSubagentHandler<
168
180
  childResults.delete(childWorkflowId);
169
181
 
170
182
  if (!childResult) {
183
+ log.warn("subagent returned no result", {
184
+ subagent: config.agentName,
185
+ childWorkflowId,
186
+ });
171
187
  return {
172
188
  toolResponse: "Subagent workflow did not signal a result",
173
189
  data: null,
174
190
  };
175
191
  }
176
192
 
193
+ log.info("subagent completed", {
194
+ subagent: config.agentName,
195
+ childWorkflowId,
196
+ ...(childResult.usage && { usage: childResult.usage }),
197
+ });
198
+
177
199
  const {
178
200
  toolResponse,
179
201
  data,
@@ -214,9 +236,10 @@ export function createSubagentHandler<
214
236
  let finalToolResponse: JsonValue = toolResponse;
215
237
 
216
238
  if (allowsContinuation && childThreadId) {
217
- const responseStr = typeof toolResponse === "string"
218
- ? toolResponse
219
- : JSON.stringify(toolResponse);
239
+ const responseStr =
240
+ typeof toolResponse === "string"
241
+ ? toolResponse
242
+ : JSON.stringify(toolResponse);
220
243
  finalToolResponse = `${responseStr}\n\n[${config.agentName} Thread ID: ${childThreadId}]`;
221
244
  }
222
245
 
@@ -6,7 +6,11 @@ import type {
6
6
  } from "../tool-router/types";
7
7
  import type { SubagentConfig, SubagentHooks } from "./types";
8
8
  import type { z } from "zod";
9
- import { createSubagentTool, SUBAGENT_TOOL_NAME, type SubagentArgs } from "./tool";
9
+ import {
10
+ createSubagentTool,
11
+ SUBAGENT_TOOL_NAME,
12
+ type SubagentArgs,
13
+ } from "./tool";
10
14
  import { createSubagentHandler } from "./handler";
11
15
 
12
16
  /**
@@ -19,12 +23,7 @@ import { createSubagentHandler } from "./handler";
19
23
  *
20
24
  * Returns null if no subagents are configured.
21
25
  */
22
- export function buildSubagentRegistration(
23
- subagents: SubagentConfig[],
24
- options?: {
25
- getSandboxStateForInheritance?: () => Record<string, unknown> | undefined;
26
- },
27
- ): {
26
+ export function buildSubagentRegistration(subagents: SubagentConfig[]): {
28
27
  registration: ToolMap[string];
29
28
  destroySubagentSandboxes: () => Promise<void>;
30
29
  } | null {
@@ -32,7 +31,7 @@ export function buildSubagentRegistration(
32
31
 
33
32
  const getEnabled = (): SubagentConfig[] =>
34
33
  subagents.filter((s) =>
35
- typeof s.enabled === "function" ? s.enabled() : (s.enabled ?? true),
34
+ typeof s.enabled === "function" ? s.enabled() : (s.enabled ?? true)
36
35
  );
37
36
 
38
37
  const subagentHooksMap = new Map<string, SubagentHooks>();
@@ -43,15 +42,15 @@ export function buildSubagentRegistration(
43
42
  const resolveSubagentName = (args: unknown): string =>
44
43
  (args as SubagentArgs).subagent;
45
44
 
46
- const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents, {
47
- getSandboxStateForInheritance: options?.getSandboxStateForInheritance,
48
- });
45
+ const { handler, destroySubagentSandboxes } =
46
+ createSubagentHandler(subagents);
49
47
 
50
48
  const registration: ToolMap[string] = {
51
49
  name: SUBAGENT_TOOL_NAME,
52
50
  enabled: (): boolean => getEnabled().length > 0,
53
51
  description: (): string => createSubagentTool(getEnabled()).description,
54
- schema: (): z.ZodObject<z.ZodRawShape> => createSubagentTool(getEnabled()).schema,
52
+ schema: (): z.ZodObject<z.ZodRawShape> =>
53
+ createSubagentTool(getEnabled()).schema,
55
54
  handler,
56
55
  ...(subagentHooksMap.size > 0 && {
57
56
  hooks: {
@@ -81,6 +81,7 @@ vi.mock("@temporalio/workflow", () => {
81
81
  ).join("");
82
82
  return `${bytes.slice(0, 8)}-${bytes.slice(8, 12)}-${bytes.slice(12, 16)}-${bytes.slice(16, 20)}-${bytes.slice(20, 32)}`;
83
83
  },
84
+ log: { trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
84
85
  };
85
86
  });
86
87
 
@@ -22,9 +22,11 @@ vi.mock("@temporalio/workflow", () => {
22
22
  return err;
23
23
  }
24
24
  }
25
+ const noop = () => {};
25
26
  return {
26
27
  ApplicationFailure: MockApplicationFailure,
27
28
  uuid4: () => "00000000-0000-0000-0000-000000000000",
29
+ log: { trace: noop, debug: noop, info: noop, warn: noop, error: noop },
28
30
  };
29
31
  });
30
32
 
@@ -22,9 +22,11 @@ vi.mock("@temporalio/workflow", () => {
22
22
  return err;
23
23
  }
24
24
  }
25
+ const noop = () => {};
25
26
  return {
26
27
  ApplicationFailure: MockApplicationFailure,
27
28
  uuid4: () => "00000000-0000-0000-0000-000000000000",
29
+ log: { trace: noop, debug: noop, info: noop, warn: noop, error: noop },
28
30
  };
29
31
  });
30
32
 
@@ -20,7 +20,7 @@ import type {
20
20
 
21
21
  import type { JsonValue } from "../state/types";
22
22
  import type { z } from "zod";
23
- import { uuid4 } from "@temporalio/workflow";
23
+ import { uuid4, log } from "@temporalio/workflow";
24
24
 
25
25
  /**
26
26
  * Creates a tool router for declarative tool call processing.
@@ -223,6 +223,12 @@ export function createToolRouter<T extends ToolMap>(
223
223
  }
224
224
  const effectiveArgs = preResult.args;
225
225
 
226
+ log.debug("tool call dispatched", {
227
+ toolName: toolCall.name,
228
+ toolCallId: toolCall.id,
229
+ turn,
230
+ });
231
+
226
232
  // --- Execute handler ---
227
233
  let result: unknown;
228
234
  let content!: JsonValue;
@@ -251,6 +257,13 @@ export function createToolRouter<T extends ToolMap>(
251
257
  content = JSON.stringify(result, null, 2);
252
258
  }
253
259
  } catch (error) {
260
+ log.warn("tool call failed", {
261
+ toolName: toolCall.name,
262
+ toolCallId: toolCall.id,
263
+ turn,
264
+ durationMs: Date.now() - startTime,
265
+ error: error instanceof Error ? error.message : String(error),
266
+ });
254
267
  const recovery = await runFailureHooks(
255
268
  toolCall,
256
269
  tool,
@@ -279,6 +292,8 @@ export function createToolRouter<T extends ToolMap>(
279
292
  );
280
293
  }
281
294
 
295
+ const durationMs = Date.now() - startTime;
296
+
282
297
  const toolResult = {
283
298
  toolCallId: toolCall.id,
284
299
  name: toolCall.name,
@@ -286,6 +301,13 @@ export function createToolRouter<T extends ToolMap>(
286
301
  ...(metadata && { metadata }),
287
302
  } as ToolCallResultUnion<TResults>;
288
303
 
304
+ log.debug("tool call completed", {
305
+ toolName: toolCall.name,
306
+ toolCallId: toolCall.id,
307
+ turn,
308
+ durationMs,
309
+ });
310
+
289
311
  // --- Post-hooks ---
290
312
  await runPostHooks(
291
313
  toolCall,
@@ -293,7 +315,7 @@ export function createToolRouter<T extends ToolMap>(
293
315
  toolResult,
294
316
  effectiveArgs,
295
317
  turn,
296
- Date.now() - startTime
318
+ durationMs
297
319
  );
298
320
 
299
321
  return toolResult;
package/src/lib/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { JsonValue } from "./state/types";
2
+ import type { FileEntry } from "./virtual-fs/types";
2
3
 
3
4
  // ============================================================================
4
5
  // Agent core types
@@ -23,6 +24,7 @@ export interface BaseAgentState {
23
24
  version: number;
24
25
  turns: number;
25
26
  tasks: Map<string, WorkflowTask>;
27
+ fileTree: FileEntry[];
26
28
  systemPrompt?: string;
27
29
  totalInputTokens: number;
28
30
  totalOutputTokens: number;
@@ -2,8 +2,8 @@ import type {
2
2
  SandboxFileSystem,
3
3
  DirentEntry,
4
4
  FileStat,
5
- } from "../../../lib/sandbox/types";
6
- import { SandboxNotSupportedError } from "../../../lib/sandbox/types";
5
+ } from "../sandbox/types";
6
+ import { SandboxNotSupportedError } from "../sandbox/types";
7
7
  import { posix } from "node:path";
8
8
  import type {
9
9
  FileEntry,
@@ -45,13 +45,13 @@ function inferDirectories(
45
45
  }
46
46
 
47
47
  /**
48
- * Ephemeral {@link SandboxFileSystem} backed by a {@link FileResolver}.
48
+ * Ephemeral virtual filesystem backed by a {@link FileResolver}.
49
49
  *
50
50
  * Created fresh for each tool invocation from the current workflow file tree.
51
51
  * Directory structure is inferred from file paths. All mutations are tracked
52
52
  * and can be retrieved via {@link getMutations} after the handler completes.
53
53
  */
54
- export class VirtualSandboxFileSystem<
54
+ export class VirtualFileSystem<
55
55
  TCtx = unknown,
56
56
  TMeta = FileEntryMetadata,
57
57
  > implements SandboxFileSystem {
@@ -0,0 +1,18 @@
1
+ export { VirtualFileSystem } from "./filesystem";
2
+ export { withVirtualFs } from "./with-virtual-fs";
3
+ export { createVirtualFsActivities } from "./manager";
4
+ export { hasFileWithMimeType, filesWithMimeType, hasDirectory } from "./queries";
5
+ export { applyVirtualTreeMutations } from "./mutations";
6
+ export { formatVirtualFileTree } from "./tree";
7
+ export type { FileTreeAccessor } from "./queries";
8
+ export type {
9
+ FileEntry,
10
+ FileEntryMetadata,
11
+ FileResolver,
12
+ VirtualFileTree,
13
+ VirtualFsOps,
14
+ PrefixedVirtualFsOps,
15
+ VirtualFsState,
16
+ VirtualFsContext,
17
+ TreeMutation,
18
+ } from "./types";
@@ -0,0 +1,48 @@
1
+ import type {
2
+ FileEntryMetadata,
3
+ FileResolver,
4
+ PrefixedVirtualFsOps,
5
+ VirtualFsOps,
6
+ } from "./types";
7
+
8
+ /**
9
+ * Creates prefixed Temporal activity functions for a {@link FileResolver}.
10
+ *
11
+ * Pair with {@link proxyVirtualFsOps} on the workflow side using the same
12
+ * scope string.
13
+ *
14
+ * @param resolver - Consumer-provided bridge to DB / S3 / CRUD layer
15
+ * @param scope - Workflow name used to namespace the activities
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { createVirtualFsActivities } from 'zeitlich';
20
+ *
21
+ * const activities = {
22
+ * ...createVirtualFsActivities(resolver, "CodingAgent"),
23
+ * };
24
+ * // registers: codingAgentResolveFileTree
25
+ * ```
26
+ */
27
+ export function createVirtualFsActivities<
28
+ S extends string,
29
+ TCtx = unknown,
30
+ TMeta = FileEntryMetadata,
31
+ >(
32
+ resolver: FileResolver<TCtx, TMeta>,
33
+ scope: S,
34
+ ): PrefixedVirtualFsOps<S, TCtx, TMeta> {
35
+ const ops: VirtualFsOps<TCtx, TMeta> = {
36
+ resolveFileTree: async (ctx: TCtx) => {
37
+ const fileTree = await resolver.resolveEntries(ctx);
38
+ return { fileTree };
39
+ },
40
+ };
41
+
42
+ const prefix = scope;
43
+ const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
44
+
45
+ return Object.fromEntries(
46
+ Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v]),
47
+ ) as PrefixedVirtualFsOps<S, TCtx, TMeta>;
48
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Workflow-safe proxy for virtual filesystem operations.
3
+ *
4
+ * Import this from `zeitlich/workflow` in your Temporal workflow files.
5
+ *
6
+ * By default the scope is derived from `workflowInfo().workflowType`,
7
+ * so activities are automatically namespaced per workflow.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { proxyVirtualFsOps } from 'zeitlich/workflow';
12
+ *
13
+ * const virtualFsOps = proxyVirtualFsOps();
14
+ * ```
15
+ */
16
+ import { proxyActivities, workflowInfo } from "@temporalio/workflow";
17
+ import type { VirtualFsOps } from "./types";
18
+
19
+ export function proxyVirtualFsOps<TCtx = unknown>(
20
+ scope?: string,
21
+ options?: Parameters<typeof proxyActivities>[0],
22
+ ): VirtualFsOps<TCtx> {
23
+ const resolvedScope = scope ?? workflowInfo().workflowType;
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ const acts = proxyActivities<Record<string, (...args: any[]) => any>>(
27
+ options ?? {
28
+ startToCloseTimeout: "30s",
29
+ retry: {
30
+ maximumAttempts: 3,
31
+ initialInterval: "2s",
32
+ maximumInterval: "30s",
33
+ backoffCoefficient: 2,
34
+ },
35
+ },
36
+ );
37
+
38
+ const prefix = resolvedScope;
39
+ const p = (key: string): string =>
40
+ `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
41
+
42
+ return {
43
+ resolveFileTree: acts[p("resolveFileTree")],
44
+ } as VirtualFsOps<TCtx>;
45
+ }
@@ -1,6 +1,5 @@
1
- import type { Sandbox, SandboxCreateOptions } from "../../../lib/sandbox/types";
2
- import type { RouterContext } from "../../../lib/tool-router/types";
3
- import type { VirtualSandboxFileSystem } from "./filesystem";
1
+ import type { RouterContext } from "../tool-router/types";
2
+ import type { VirtualFileSystem } from "./filesystem";
4
3
 
5
4
  // ============================================================================
6
5
  // File Entry
@@ -15,7 +14,7 @@ export type FileEntryMetadata = Record<
15
14
  /** JSON-serializable metadata for a single file in the virtual tree. */
16
15
  export interface FileEntry<TMeta = FileEntryMetadata> {
17
16
  id: string;
18
- /** Virtual path inside the sandbox, e.g. "/src/index.ts" */
17
+ /** Virtual path, e.g. "/src/index.ts" */
19
18
  path: string;
20
19
  size: number;
21
20
  /** ISO-8601 date string (JSON-safe) */
@@ -73,63 +72,68 @@ export interface FileResolver<TCtx = unknown, TMeta = FileEntryMetadata> {
73
72
  }
74
73
 
75
74
  // ============================================================================
76
- // Create Options
75
+ // VirtualFsOps — workflow-side activity interface
77
76
  // ============================================================================
78
77
 
79
78
  /**
80
- * Options for {@link VirtualSandboxProvider.create}.
81
- * Extends base options with resolver context.
79
+ * Workflow-side operations for the virtual filesystem.
80
+ *
81
+ * Unlike {@link SandboxOps}, this only exposes what is actually needed:
82
+ * resolving the initial file tree from the consumer's data layer.
82
83
  */
83
- export interface VirtualSandboxCreateOptions<
84
- TCtx,
85
- > extends SandboxCreateOptions {
86
- resolverContext: TCtx;
87
- /** Base path for resolving relative filesystem paths (default "/"). */
88
- workspaceBase?: string;
84
+ export interface VirtualFsOps<TCtx = unknown, TMeta = FileEntryMetadata> {
85
+ resolveFileTree(ctx: TCtx): Promise<{
86
+ fileTree: FileEntry<TMeta>[];
87
+ }>;
89
88
  }
90
89
 
91
- // ============================================================================
92
- // Workflow State Shape
93
- // ============================================================================
94
-
95
90
  /**
96
- * The portion of workflow `AgentState` that the virtual sandbox reads via
97
- * {@link queryParentWorkflowState}. Populated automatically by the session
98
- * from the provider's `stateUpdate` after `createSandbox`.
91
+ * Maps generic {@link VirtualFsOps} method names to scope-prefixed names.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * type Ops = PrefixedVirtualFsOps<"codingAgent">;
96
+ * // → { codingAgentResolveFileTree: ... }
97
+ * ```
99
98
  */
100
- export interface VirtualSandboxState<
99
+ export type PrefixedVirtualFsOps<
100
+ TPrefix extends string,
101
101
  TCtx = unknown,
102
102
  TMeta = FileEntryMetadata,
103
- > {
104
- sandboxId: string;
105
- fileTree: FileEntry<TMeta>[];
106
- resolverContext: TCtx;
107
- workspaceBase?: string;
108
- }
103
+ > = {
104
+ [K in keyof VirtualFsOps<
105
+ TCtx,
106
+ TMeta
107
+ > as `${TPrefix}${Capitalize<K & string>}`]: VirtualFsOps<TCtx, TMeta>[K];
108
+ };
109
109
 
110
110
  // ============================================================================
111
- // VirtualSandbox instance type
111
+ // Workflow State Shape
112
112
  // ============================================================================
113
113
 
114
114
  /**
115
- * A {@link Sandbox} whose filesystem is backed by a {@link VirtualSandboxFileSystem}.
115
+ * The portion of workflow `AgentState` that the virtual filesystem reads via
116
+ * {@link queryParentWorkflowState}. Populated automatically by the session
117
+ * when `virtualFs` config is provided.
116
118
  */
117
- export type VirtualSandbox<
118
- TCtx = unknown,
119
- TMeta = FileEntryMetadata,
120
- > = Sandbox & { fs: VirtualSandboxFileSystem<TCtx, TMeta> };
119
+ export interface VirtualFsState<TCtx = unknown, TMeta = FileEntryMetadata> {
120
+ fileTree: FileEntry<TMeta>[];
121
+ ctx: TCtx;
122
+ workspaceBase?: string;
123
+ }
121
124
 
122
125
  // ============================================================================
123
126
  // Handler Context
124
127
  // ============================================================================
125
128
 
126
129
  /**
127
- * Extended router context injected by {@link withVirtualSandbox}.
128
- * Guarantees a live (ephemeral) sandbox built from the workflow file tree.
130
+ * Extended router context injected by {@link withVirtualFs}.
131
+ * Guarantees a live (ephemeral) virtual filesystem built from the workflow
132
+ * file tree.
129
133
  */
130
- export interface VirtualSandboxContext<
134
+ export interface VirtualFsContext<
131
135
  TCtx = unknown,
132
136
  TMeta = FileEntryMetadata,
133
137
  > extends RouterContext {
134
- sandbox: VirtualSandbox<TCtx, TMeta>;
138
+ virtualFs: VirtualFileSystem<TCtx, TMeta>;
135
139
  }