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.
- package/README.md +121 -13
- package/dist/{activities-3xj_fEJK.d.ts → activities-BeveyY9b.d.cts} +2 -3
- package/dist/{activities-BzYq6jf7.d.cts → activities-NT3rcw66.d.ts} +2 -3
- package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/index.d.cts +3 -3
- package/dist/adapters/sandbox/bedrock/index.d.ts +3 -3
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +1 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +1 -1
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +1 -1
- package/dist/adapters/sandbox/e2b/index.d.ts +1 -1
- package/dist/adapters/sandbox/e2b/index.js.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.ts +1 -1
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +0 -1
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +6 -7
- package/dist/adapters/thread/anthropic/index.d.ts +6 -7
- package/dist/adapters/thread/anthropic/index.js +0 -1
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -6
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -6
- package/dist/adapters/thread/google-genai/index.cjs +0 -1
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +6 -7
- package/dist/adapters/thread/google-genai/index.d.ts +6 -7
- package/dist/adapters/thread/google-genai/index.js +0 -1
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -6
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -6
- package/dist/adapters/thread/langchain/index.cjs +0 -1
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +6 -7
- package/dist/adapters/thread/langchain/index.d.ts +6 -7
- package/dist/adapters/thread/langchain/index.js +0 -1
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -6
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -6
- package/dist/index.cjs +558 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +136 -22
- package/dist/index.d.ts +136 -22
- package/dist/index.js +554 -52
- package/dist/index.js.map +1 -1
- package/dist/{proxy-7e7v8ccg.d.ts → proxy-BgswT47M.d.ts} +1 -1
- package/dist/{proxy-CsB8r0RR.d.cts → proxy-OJihshQF.d.cts} +1 -1
- package/dist/{thread-manager-D8C5QvLi.d.ts → thread-manager-BS477gj8.d.ts} +1 -1
- package/dist/{thread-manager-DdVFl1IY.d.cts → thread-manager-DH0zv05W.d.cts} +1 -1
- package/dist/{thread-manager-B5qA4v7V.d.ts → thread-manager-iUplxEZt.d.ts} +1 -1
- package/dist/{thread-manager-DFJ3sKKU.d.cts → thread-manager-lfN0V-gH.d.cts} +1 -1
- package/dist/{types-ChAMwU3q.d.cts → types-AujBIMMn.d.cts} +5 -8
- package/dist/{types-ChAMwU3q.d.ts → types-AujBIMMn.d.ts} +5 -8
- package/dist/{types-BZ75HpYd.d.ts → types-CCIc7Eam.d.ts} +1 -1
- package/dist/types-D90Q5aOh.d.ts +1212 -0
- package/dist/{types-BdCdR41N.d.ts → types-DBk-C8zM.d.ts} +1 -1
- package/dist/{types-ZHs2v9Ap.d.cts → types-DUvEZSDe.d.cts} +1 -1
- package/dist/types-DVdT5ybA.d.cts +1212 -0
- package/dist/{types-HbjqzyJH.d.cts → types-DgIVPOa1.d.cts} +1 -1
- package/dist/workflow-Cj4DxGdM.d.cts +750 -0
- package/dist/workflow-CzrBdCcJ.d.ts +750 -0
- package/dist/workflow.cjs +194 -40
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +5 -579
- package/dist/workflow.d.ts +5 -579
- package/dist/workflow.js +193 -42
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -23
- package/src/adapters/thread/anthropic/thread-manager.ts +6 -6
- package/src/adapters/thread/google-genai/thread-manager.ts +6 -6
- package/src/adapters/thread/langchain/thread-manager.ts +6 -6
- package/src/index.ts +8 -0
- package/src/lib/lifecycle.ts +8 -3
- package/src/lib/observability/hooks.ts +117 -0
- package/src/lib/observability/index.ts +13 -0
- package/src/lib/observability/sinks.ts +88 -0
- package/src/lib/sandbox/index.ts +2 -4
- package/src/lib/sandbox/manager.ts +131 -16
- package/src/lib/sandbox/sandbox.test.ts +136 -16
- package/src/lib/sandbox/types.ts +6 -5
- package/src/lib/session/session-edge-cases.integration.test.ts +1 -0
- package/src/lib/session/session.integration.test.ts +7 -39
- package/src/lib/session/session.ts +119 -42
- package/src/lib/session/types.ts +39 -9
- package/src/lib/state/manager.integration.test.ts +1 -0
- package/src/lib/state/types.ts +9 -6
- package/src/lib/subagent/handler.ts +35 -12
- package/src/lib/subagent/register.ts +11 -12
- package/src/lib/subagent/subagent.integration.test.ts +1 -0
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +2 -0
- package/src/lib/tool-router/router.integration.test.ts +2 -0
- package/src/lib/tool-router/router.ts +24 -2
- package/src/lib/types.ts +2 -0
- package/src/{adapters/sandbox/virtual → lib/virtual-fs}/filesystem.ts +4 -4
- package/src/lib/virtual-fs/index.ts +18 -0
- package/src/lib/virtual-fs/manager.ts +48 -0
- package/src/lib/virtual-fs/proxy.ts +45 -0
- package/src/{adapters/sandbox/virtual → lib/virtual-fs}/types.ts +41 -37
- package/src/{adapters/sandbox/virtual/virtual-sandbox.test.ts → lib/virtual-fs/virtual-fs.test.ts} +15 -130
- package/src/lib/virtual-fs/with-virtual-fs.ts +94 -0
- package/src/tools/bash/bash.test.ts +2 -1
- package/src/workflow.ts +25 -8
- package/tsup.config.ts +0 -2
- package/dist/adapters/sandbox/virtual/index.cjs +0 -487
- package/dist/adapters/sandbox/virtual/index.cjs.map +0 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +0 -90
- package/dist/adapters/sandbox/virtual/index.d.ts +0 -90
- package/dist/adapters/sandbox/virtual/index.js +0 -479
- package/dist/adapters/sandbox/virtual/index.js.map +0 -1
- package/dist/adapters/sandbox/virtual/workflow.cjs +0 -33
- package/dist/adapters/sandbox/virtual/workflow.cjs.map +0 -1
- package/dist/adapters/sandbox/virtual/workflow.d.cts +0 -28
- package/dist/adapters/sandbox/virtual/workflow.d.ts +0 -28
- package/dist/adapters/sandbox/virtual/workflow.js +0 -31
- package/dist/adapters/sandbox/virtual/workflow.js.map +0 -1
- package/dist/queries-DVnukByF.d.cts +0 -44
- package/dist/queries-kjlvsUfz.d.ts +0 -44
- package/dist/types-BclYm5Ic.d.cts +0 -581
- package/dist/types-BclYm5Ic.d.ts +0 -581
- package/dist/types-BgsAwN3L.d.cts +0 -125
- package/dist/types-BtqbM1bO.d.ts +0 -490
- package/dist/types-BuCEZ4dF.d.cts +0 -490
- package/dist/types-yU5AINiP.d.ts +0 -125
- package/src/adapters/sandbox/virtual/index.ts +0 -92
- package/src/adapters/sandbox/virtual/provider.ts +0 -121
- package/src/adapters/sandbox/virtual/proxy.ts +0 -53
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +0 -97
- package/src/lib/.env +0 -1
- package/src/tools/bash/.env +0 -1
- /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/mutations.ts +0 -0
- /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/queries.ts +0 -0
- /package/src/{adapters/sandbox/virtual → lib/virtual-fs}/tree.ts +0 -0
package/src/lib/session/types.ts
CHANGED
|
@@ -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 {
|
|
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(
|
|
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<
|
|
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
|
|
package/src/lib/state/types.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
102
|
-
mergeUpdate(update: Partial<TCustom
|
|
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 {
|
|
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
|
-
>(
|
|
49
|
-
|
|
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 = {
|
|
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" ||
|
|
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 =
|
|
218
|
-
|
|
219
|
-
|
|
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 {
|
|
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 } =
|
|
47
|
-
|
|
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> =>
|
|
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
|
-
|
|
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 "
|
|
6
|
-
import { SandboxNotSupportedError } from "
|
|
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
|
|
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
|
|
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 {
|
|
2
|
-
import type {
|
|
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
|
|
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
|
-
//
|
|
75
|
+
// VirtualFsOps — workflow-side activity interface
|
|
77
76
|
// ============================================================================
|
|
78
77
|
|
|
79
78
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
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
|
|
84
|
-
TCtx
|
|
85
|
-
>
|
|
86
|
-
|
|
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
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
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
|
|
99
|
+
export type PrefixedVirtualFsOps<
|
|
100
|
+
TPrefix extends string,
|
|
101
101
|
TCtx = unknown,
|
|
102
102
|
TMeta = FileEntryMetadata,
|
|
103
|
-
> {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
//
|
|
111
|
+
// Workflow State Shape
|
|
112
112
|
// ============================================================================
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
128
|
-
* Guarantees a live (ephemeral)
|
|
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
|
|
134
|
+
export interface VirtualFsContext<
|
|
131
135
|
TCtx = unknown,
|
|
132
136
|
TMeta = FileEntryMetadata,
|
|
133
137
|
> extends RouterContext {
|
|
134
|
-
|
|
138
|
+
virtualFs: VirtualFileSystem<TCtx, TMeta>;
|
|
135
139
|
}
|