zeitlich 0.2.18 → 0.2.20

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 (59) hide show
  1. package/dist/adapters/sandbox/daytona/index.cjs +25 -10
  2. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  3. package/dist/adapters/sandbox/daytona/index.d.cts +4 -1
  4. package/dist/adapters/sandbox/daytona/index.d.ts +4 -1
  5. package/dist/adapters/sandbox/daytona/index.js +25 -10
  6. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  7. package/dist/adapters/sandbox/virtual/index.d.cts +4 -3
  8. package/dist/adapters/sandbox/virtual/index.d.ts +4 -3
  9. package/dist/adapters/thread/google-genai/index.cjs +0 -2
  10. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  11. package/dist/adapters/thread/google-genai/index.d.cts +2 -2
  12. package/dist/adapters/thread/google-genai/index.d.ts +2 -2
  13. package/dist/adapters/thread/google-genai/index.js +0 -2
  14. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  15. package/dist/adapters/thread/langchain/index.cjs +3 -1
  16. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  17. package/dist/adapters/thread/langchain/index.d.cts +3 -3
  18. package/dist/adapters/thread/langchain/index.d.ts +3 -3
  19. package/dist/adapters/thread/langchain/index.js +3 -1
  20. package/dist/adapters/thread/langchain/index.js.map +1 -1
  21. package/dist/index.cjs +153 -123
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +13 -13
  24. package/dist/index.d.ts +13 -13
  25. package/dist/index.js +153 -123
  26. package/dist/index.js.map +1 -1
  27. package/dist/{queries-TwukRZ8b.d.ts → queries-KHj5Otv7.d.ts} +1 -1
  28. package/dist/{queries-DnX72m_Y.d.cts → queries-nIdzTCDS.d.cts} +1 -1
  29. package/dist/{types-CdB2D5Sq.d.ts → types-By80IE1x.d.ts} +3 -3
  30. package/dist/{types-CmOSypVk.d.ts → types-Ct2igz9y.d.cts} +6 -4
  31. package/dist/{types-CmOSypVk.d.cts → types-Ct2igz9y.d.ts} +6 -4
  32. package/dist/{types-DRvq2miV.d.cts → types-DZ7BkA3-.d.cts} +3 -3
  33. package/dist/workflow.cjs +70 -40
  34. package/dist/workflow.cjs.map +1 -1
  35. package/dist/workflow.d.cts +16 -12
  36. package/dist/workflow.d.ts +16 -12
  37. package/dist/workflow.js +70 -40
  38. package/dist/workflow.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/adapters/sandbox/daytona/filesystem.ts +21 -12
  41. package/src/adapters/sandbox/daytona/index.ts +24 -23
  42. package/src/adapters/thread/langchain/thread-manager.ts +11 -7
  43. package/src/lib/session/session-edge-cases.integration.test.ts +20 -2
  44. package/src/lib/session/session.integration.test.ts +16 -2
  45. package/src/lib/session/session.ts +2 -1
  46. package/src/lib/session/types.ts +2 -1
  47. package/src/lib/subagent/handler.ts +1 -1
  48. package/src/lib/subagent/register.ts +3 -9
  49. package/src/lib/subagent/subagent.integration.test.ts +10 -9
  50. package/src/lib/subagent/tool.ts +1 -1
  51. package/src/lib/thread/index.ts +0 -1
  52. package/src/lib/tool-router/router-edge-cases.integration.test.ts +8 -3
  53. package/src/lib/tool-router/router.integration.test.ts +8 -3
  54. package/src/lib/tool-router/router.ts +61 -31
  55. package/src/lib/tool-router/types.ts +14 -9
  56. package/src/lib/workflow.test.ts +18 -6
  57. package/src/lib/workflow.ts +13 -3
  58. package/src/tools/task-create/handler.ts +3 -6
  59. package/src/workflow.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -19,7 +19,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
19
19
 
20
20
  constructor(
21
21
  private sandbox: DaytonaSdkSandbox,
22
- workspaceBase = "/home/daytona",
22
+ workspaceBase = "/home/daytona"
23
23
  ) {
24
24
  this.workspaceBase = posix.resolve("/", workspaceBase);
25
25
  }
@@ -49,10 +49,18 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
49
49
  await this.sandbox.fs.uploadFile(buf, norm);
50
50
  }
51
51
 
52
- async appendFile(
53
- path: string,
54
- content: string | Uint8Array,
52
+ async writeFiles(
53
+ files: { path: string; content: string | Uint8Array }[]
55
54
  ): Promise<void> {
55
+ await this.sandbox.fs.uploadFiles(
56
+ files.map((f) => ({
57
+ source: Buffer.from(f.content),
58
+ destination: f.path,
59
+ }))
60
+ );
61
+ }
62
+
63
+ async appendFile(path: string, content: string | Uint8Array): Promise<void> {
56
64
  const norm = this.normalisePath(path);
57
65
  let existing: Buffer;
58
66
  try {
@@ -89,10 +97,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
89
97
  };
90
98
  }
91
99
 
92
- async mkdir(
93
- path: string,
94
- _options?: { recursive?: boolean },
95
- ): Promise<void> {
100
+ async mkdir(path: string, _options?: { recursive?: boolean }): Promise<void> {
96
101
  const norm = this.normalisePath(path);
97
102
  await this.sandbox.fs.createFolder(norm, "755");
98
103
  }
@@ -116,7 +121,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
116
121
 
117
122
  async rm(
118
123
  path: string,
119
- options?: { recursive?: boolean; force?: boolean },
124
+ options?: { recursive?: boolean; force?: boolean }
120
125
  ): Promise<void> {
121
126
  const norm = this.normalisePath(path);
122
127
  try {
@@ -129,7 +134,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
129
134
  async cp(
130
135
  src: string,
131
136
  dest: string,
132
- options?: { recursive?: boolean },
137
+ options?: { recursive?: boolean }
133
138
  ): Promise<void> {
134
139
  const normSrc = this.normalisePath(src);
135
140
  const normDest = this.normalisePath(dest);
@@ -138,9 +143,13 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
138
143
  if (!options?.recursive) {
139
144
  throw new Error(`EISDIR: is a directory (use recursive): ${src}`);
140
145
  }
141
- await this.sandbox.process.executeCommand(`cp -r "${normSrc}" "${normDest}"`);
146
+ await this.sandbox.process.executeCommand(
147
+ `cp -r "${normSrc}" "${normDest}"`
148
+ );
142
149
  } else {
143
- await this.sandbox.process.executeCommand(`cp "${normSrc}" "${normDest}"`);
150
+ await this.sandbox.process.executeCommand(
151
+ `cp "${normSrc}" "${normDest}"`
152
+ );
144
153
  }
145
154
  }
146
155
 
@@ -1,7 +1,4 @@
1
- import {
2
- Daytona,
3
- type Sandbox as DaytonaSdkSandbox,
4
- } from "@daytonaio/sdk";
1
+ import { Daytona, type Sandbox as DaytonaSdkSandbox } from "@daytonaio/sdk";
5
2
  import type {
6
3
  Sandbox,
7
4
  SandboxCapabilities,
@@ -38,7 +35,7 @@ class DaytonaSandboxImpl implements Sandbox {
38
35
  constructor(
39
36
  readonly id: string,
40
37
  private sdkSandbox: DaytonaSdkSandbox,
41
- workspaceBase = "/home/daytona",
38
+ workspaceBase = "/home/daytona"
42
39
  ) {
43
40
  this.fs = new DaytonaSandboxFileSystem(sdkSandbox, workspaceBase);
44
41
  }
@@ -48,7 +45,7 @@ class DaytonaSandboxImpl implements Sandbox {
48
45
  command,
49
46
  options?.cwd,
50
47
  options?.env,
51
- options?.timeout,
48
+ options?.timeout
52
49
  );
53
50
 
54
51
  return {
@@ -67,9 +64,10 @@ class DaytonaSandboxImpl implements Sandbox {
67
64
  // DaytonaSandboxProvider
68
65
  // ============================================================================
69
66
 
70
- export class DaytonaSandboxProvider
71
- implements SandboxProvider<DaytonaSandboxCreateOptions, DaytonaSandbox>
72
- {
67
+ export class DaytonaSandboxProvider implements SandboxProvider<
68
+ DaytonaSandboxCreateOptions,
69
+ DaytonaSandbox
70
+ > {
73
71
  readonly id = "daytona";
74
72
  readonly capabilities: SandboxCapabilities = {
75
73
  filesystem: true,
@@ -79,7 +77,6 @@ export class DaytonaSandboxProvider
79
77
 
80
78
  private client: Daytona;
81
79
  private readonly defaultWorkspaceBase: string;
82
- private workspaceBaseById = new Map<string, string>();
83
80
 
84
81
  constructor(config?: DaytonaSandboxConfig) {
85
82
  this.client = new Daytona(config);
@@ -87,7 +84,7 @@ export class DaytonaSandboxProvider
87
84
  }
88
85
 
89
86
  async create(
90
- options?: DaytonaSandboxCreateOptions,
87
+ options?: DaytonaSandboxCreateOptions
91
88
  ): Promise<SandboxCreateResult> {
92
89
  const sdkSandbox = await this.client.create(
93
90
  {
@@ -99,21 +96,24 @@ export class DaytonaSandboxProvider
99
96
  autoArchiveInterval: options?.autoArchiveInterval,
100
97
  autoDeleteInterval: options?.autoDeleteInterval,
101
98
  },
102
- { timeout: options?.timeout ?? 60 },
99
+ { timeout: options?.timeout ?? 60 }
103
100
  );
104
101
 
105
102
  const workspaceBase = options?.workspaceBase ?? this.defaultWorkspaceBase;
106
- this.workspaceBaseById.set(sdkSandbox.id, workspaceBase);
103
+
107
104
  const sandbox = new DaytonaSandboxImpl(
108
105
  sdkSandbox.id,
109
106
  sdkSandbox,
110
- workspaceBase,
107
+ workspaceBase
111
108
  );
112
109
 
113
110
  if (options?.initialFiles) {
114
- for (const [path, content] of Object.entries(options.initialFiles)) {
115
- await sandbox.fs.writeFile(path, content);
116
- }
111
+ await sandbox.fs.writeFiles(
112
+ Object.entries(options.initialFiles).map(([path, content]) => ({
113
+ path,
114
+ content,
115
+ }))
116
+ );
117
117
  }
118
118
 
119
119
  return { sandbox };
@@ -122,9 +122,11 @@ export class DaytonaSandboxProvider
122
122
  async get(sandboxId: string): Promise<DaytonaSandbox> {
123
123
  try {
124
124
  const sdkSandbox = await this.client.get(sandboxId);
125
- const workspaceBase =
126
- this.workspaceBaseById.get(sandboxId) ?? this.defaultWorkspaceBase;
127
- return new DaytonaSandboxImpl(sdkSandbox.id, sdkSandbox, workspaceBase);
125
+ return new DaytonaSandboxImpl(
126
+ sdkSandbox.id,
127
+ sdkSandbox,
128
+ this.defaultWorkspaceBase
129
+ );
128
130
  } catch {
129
131
  throw new SandboxNotFoundError(sandboxId);
130
132
  }
@@ -134,7 +136,6 @@ export class DaytonaSandboxProvider
134
136
  try {
135
137
  const sdkSandbox = await this.client.get(sandboxId);
136
138
  await this.client.delete(sdkSandbox);
137
- this.workspaceBaseById.delete(sandboxId);
138
139
  } catch {
139
140
  // Already gone
140
141
  }
@@ -142,13 +143,13 @@ export class DaytonaSandboxProvider
142
143
 
143
144
  async snapshot(_sandboxId: string): Promise<SandboxSnapshot> {
144
145
  throw new SandboxNotSupportedError(
145
- "snapshot (use Daytona's native snapshot API directly)",
146
+ "snapshot (use Daytona's native snapshot API directly)"
146
147
  );
147
148
  }
148
149
 
149
150
  async restore(_snapshot: SandboxSnapshot): Promise<never> {
150
151
  throw new SandboxNotSupportedError(
151
- "restore (use Daytona's native snapshot API directly)",
152
+ "restore (use Daytona's native snapshot API directly)"
152
153
  );
153
154
  }
154
155
  }
@@ -34,22 +34,26 @@ export interface LangChainThreadManager extends BaseThreadManager<StoredMessage>
34
34
  createSystemMessage(content: string): StoredMessage;
35
35
  createAIMessage(
36
36
  content: string | MessageContent,
37
- kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },
37
+ kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
38
38
  ): StoredMessage;
39
39
  createToolMessage(
40
40
  content: LangChainToolMessageContent,
41
- toolCallId: string,
41
+ toolCallId: string
42
42
  ): StoredMessage;
43
43
  appendHumanMessage(content: string | MessageContent): Promise<void>;
44
44
  appendSystemMessage(content: string): Promise<void>;
45
45
  appendToolMessage(
46
46
  content: LangChainToolMessageContent,
47
- toolCallId: string,
47
+ toolCallId: string
48
48
  ): Promise<void>;
49
49
  appendAIMessage(content: string | MessageContent): Promise<void>;
50
50
  }
51
51
 
52
52
  function storedMessageId(msg: StoredMessage): string {
53
+ if (msg.type === "tool") {
54
+ return msg.data.tool_call_id ?? "";
55
+ }
56
+
53
57
  return msg.data.id ?? "";
54
58
  }
55
59
 
@@ -59,7 +63,7 @@ function storedMessageId(msg: StoredMessage): string {
59
63
  * appending typed LangChain messages.
60
64
  */
61
65
  export function createLangChainThreadManager(
62
- config: LangChainThreadManagerConfig,
66
+ config: LangChainThreadManagerConfig
63
67
  ): LangChainThreadManager {
64
68
  const baseConfig: ThreadManagerConfig<StoredMessage> = {
65
69
  redis: config.redis,
@@ -87,7 +91,7 @@ export function createLangChainThreadManager(
87
91
 
88
92
  createAIMessage(
89
93
  content: string,
90
- kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },
94
+ kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
91
95
  ): StoredMessage {
92
96
  return new AIMessage({
93
97
  id: uuidv4(),
@@ -104,7 +108,7 @@ export function createLangChainThreadManager(
104
108
 
105
109
  createToolMessage(
106
110
  content: LangChainToolMessageContent,
107
- toolCallId: string,
111
+ toolCallId: string
108
112
  ): StoredMessage {
109
113
  return new ToolMessage({
110
114
  id: uuidv4(),
@@ -120,7 +124,7 @@ export function createLangChainThreadManager(
120
124
 
121
125
  async appendToolMessage(
122
126
  content: LangChainToolMessageContent,
123
- toolCallId: string,
127
+ toolCallId: string
124
128
  ): Promise<void> {
125
129
  const message = helpers.createToolMessage(content, toolCallId);
126
130
  await base.append([message]);
@@ -5,6 +5,7 @@ import type { ThreadOps } from "./types";
5
5
  import type { RunAgentActivity } from "../model/types";
6
6
  import type { RawToolCall } from "../tool-router/types";
7
7
  import type { SandboxOps } from "../sandbox/types";
8
+ import type { ActivityInterfaceFor } from "@temporalio/workflow";
8
9
 
9
10
  let idCounter = 0;
10
11
 
@@ -51,9 +52,26 @@ type TurnScript = {
51
52
  usage?: TokenUsage;
52
53
  };
53
54
 
55
+ /**
56
+ * Wraps every method on a ThreadOps object so it also has `.executeWithOptions()`,
57
+ * matching Temporal's `ActivityInterfaceFor<ThreadOps>` shape.
58
+ */
59
+ function toActivityInterface(
60
+ raw: ThreadOps,
61
+ ): ActivityInterfaceFor<ThreadOps> {
62
+ const result = {} as Record<string, unknown>;
63
+ for (const [key, fn] of Object.entries(raw)) {
64
+ const wrapped = (...args: unknown[]) => (fn as (...a: unknown[]) => unknown)(...args);
65
+ wrapped.executeWithOptions = (_opts: unknown, args: unknown[]) =>
66
+ (fn as (...a: unknown[]) => unknown)(...args);
67
+ result[key] = wrapped;
68
+ }
69
+ return result as ActivityInterfaceFor<ThreadOps>;
70
+ }
71
+
54
72
  function createMockThreadOps() {
55
73
  const log: { op: string; args: unknown[] }[] = [];
56
- const ops: ThreadOps = {
74
+ const ops = toActivityInterface({
57
75
  initializeThread: async (threadId) => {
58
76
  log.push({ op: "initializeThread", args: [threadId] });
59
77
  },
@@ -69,7 +87,7 @@ function createMockThreadOps() {
69
87
  forkThread: async (source, target) => {
70
88
  log.push({ op: "forkThread", args: [source, target] });
71
89
  },
72
- };
90
+ });
73
91
  return { ops, log };
74
92
  }
75
93
 
@@ -5,6 +5,7 @@ import type { ThreadOps } from "./types";
5
5
  import type { RunAgentActivity } from "../model/types";
6
6
  import type { RawToolCall } from "../tool-router/types";
7
7
  import type { SandboxOps } from "../sandbox/types";
8
+ import type { ActivityInterfaceFor } from "@temporalio/workflow";
8
9
 
9
10
  // ---------------------------------------------------------------------------
10
11
  // Mock @temporalio/workflow
@@ -58,10 +59,23 @@ function at<T>(arr: T[], index: number): T {
58
59
  return val;
59
60
  }
60
61
 
62
+ function toActivityInterface(
63
+ raw: ThreadOps,
64
+ ): ActivityInterfaceFor<ThreadOps> {
65
+ const result = {} as Record<string, unknown>;
66
+ for (const [key, fn] of Object.entries(raw)) {
67
+ const wrapped = (...args: unknown[]) => (fn as (...a: unknown[]) => unknown)(...args);
68
+ wrapped.executeWithOptions = (_opts: unknown, args: unknown[]) =>
69
+ (fn as (...a: unknown[]) => unknown)(...args);
70
+ result[key] = wrapped;
71
+ }
72
+ return result as ActivityInterfaceFor<ThreadOps>;
73
+ }
74
+
61
75
  function createMockThreadOps() {
62
76
  const log: { op: string; args: unknown[] }[] = [];
63
77
 
64
- const ops: ThreadOps = {
78
+ const ops = toActivityInterface({
65
79
  initializeThread: async (threadId) => {
66
80
  log.push({ op: "initializeThread", args: [threadId] });
67
81
  },
@@ -77,7 +91,7 @@ function createMockThreadOps() {
77
91
  forkThread: async (source, target) => {
78
92
  log.push({ op: "forkThread", args: [source, target] });
79
93
  },
80
- };
94
+ });
81
95
 
82
96
  return { ops, log };
83
97
  }
@@ -4,6 +4,7 @@ import {
4
4
  defineUpdate,
5
5
  setHandler,
6
6
  ApplicationFailure,
7
+ type ActivityInterfaceFor,
7
8
  } from "@temporalio/workflow";
8
9
  import type { SessionExitReason, MessageContent } from "../types";
9
10
  import type { ThreadOps, SessionConfig, ZeitlichSession } from "./types";
@@ -299,7 +300,7 @@ export const createSession = async <T extends ToolMap, M = unknown>({
299
300
  */
300
301
  export function proxyDefaultThreadOps(
301
302
  options?: Parameters<typeof proxyActivities>[0]
302
- ): ThreadOps {
303
+ ): ActivityInterfaceFor<ThreadOps> {
303
304
  return proxyActivities<ThreadOps>(
304
305
  options ?? {
305
306
  startToCloseTimeout: "10s",
@@ -15,6 +15,7 @@ import type { Skill } from "../skills/types";
15
15
  import type { SandboxOps } from "../sandbox/types";
16
16
  import type { RunAgentActivity } from "../model/types";
17
17
  import type { AgentStateManager, JsonSerializable } from "../state/types";
18
+ import type { ActivityInterfaceFor } from "@temporalio/workflow";
18
19
 
19
20
  /**
20
21
  * Thread operations required by a session.
@@ -53,7 +54,7 @@ export interface SessionConfig<T extends ToolMap, M = unknown> {
53
54
  /** Workflow-specific runAgent activity (with tools pre-bound) */
54
55
  runAgent: RunAgentActivity<M>;
55
56
  /** Thread operations (initialize, append messages, parse tool calls) */
56
- threadOps?: ThreadOps;
57
+ threadOps?: ActivityInterfaceFor<ThreadOps>;
57
58
  /** Tool router for processing tool calls (optional if agent has no tools) */
58
59
  tools?: T;
59
60
  /** Subagent configurations */
@@ -89,7 +89,7 @@ export function createSubagentHandler<
89
89
  if (config.allowThreadContinuation && childThreadId) {
90
90
  finalToolResponse =
91
91
  typeof toolResponse === "string"
92
- ? `${toolResponse}\n\n[Thread ID: ${childThreadId}]`
92
+ ? `${toolResponse}\n\n[${config.agentName} Thread ID: ${childThreadId}]`
93
93
  : toolResponse;
94
94
  }
95
95
 
@@ -39,15 +39,9 @@ export function buildSubagentRegistration(
39
39
 
40
40
  return {
41
41
  name: SUBAGENT_TOOL_NAME,
42
- get enabled(): boolean {
43
- return getEnabled().length > 0;
44
- },
45
- get description(): string {
46
- return createSubagentTool(getEnabled()).description;
47
- },
48
- get schema(): z.ZodObject<z.ZodRawShape> {
49
- return createSubagentTool(getEnabled()).schema;
50
- },
42
+ enabled: (): boolean => getEnabled().length > 0,
43
+ description: (): string => createSubagentTool(getEnabled()).description,
44
+ schema: (): z.ZodObject<z.ZodRawShape> => createSubagentTool(getEnabled()).schema,
51
45
  handler: createSubagentHandler(subagents),
52
46
  ...(subagentHooksMap.size > 0 && {
53
47
  hooks: {
@@ -397,10 +397,10 @@ describe("buildSubagentRegistration", () => {
397
397
 
398
398
  expect(reg).toBeDefined();
399
399
  if (!reg) return;
400
- expect(reg.enabled).toBe(true);
400
+ expect((reg.enabled as () => boolean)()).toBe(true);
401
401
 
402
402
  flag = false;
403
- expect(reg.enabled).toBe(false);
403
+ expect((reg.enabled as () => boolean)()).toBe(false);
404
404
  });
405
405
 
406
406
  it("disabled when all subagents are disabled", () => {
@@ -415,7 +415,7 @@ describe("buildSubagentRegistration", () => {
415
415
 
416
416
  expect(reg).toBeDefined();
417
417
  if (reg) {
418
- expect(reg.enabled).toBe(false);
418
+ expect((reg.enabled as () => boolean)()).toBe(false);
419
419
  }
420
420
  });
421
421
 
@@ -476,13 +476,14 @@ describe("buildSubagentRegistration", () => {
476
476
 
477
477
  expect(reg).toBeDefined();
478
478
  if (reg) {
479
- expect(reg.description).toContain("Agent A");
480
- expect(reg.description).toContain("Agent B");
479
+ const desc = reg.description as () => string;
480
+ expect(desc()).toContain("Agent A");
481
+ expect(desc()).toContain("Agent B");
481
482
 
482
483
  bEnabled = false;
483
484
 
484
- expect(reg.description).toContain("Agent A");
485
- expect(reg.description).not.toContain("Agent B");
485
+ expect(desc()).toContain("Agent A");
486
+ expect(desc()).not.toContain("Agent B");
486
487
  }
487
488
  });
488
489
  });
@@ -519,10 +520,10 @@ describe("defineSubagent", () => {
519
520
  const reg = buildSubagentRegistration([config]);
520
521
  expect(reg).toBeDefined();
521
522
  if (!reg) return;
522
- expect(reg.enabled).toBe(true);
523
+ expect((reg.enabled as () => boolean)()).toBe(true);
523
524
 
524
525
  flag = false;
525
- expect(reg.enabled).toBe(false);
526
+ expect((reg.enabled as () => boolean)()).toBe(false);
526
527
  });
527
528
  });
528
529
 
@@ -57,7 +57,7 @@ export function createSubagentTool<T extends SubagentConfig[]>(
57
57
  .string()
58
58
  .nullable()
59
59
  .describe(
60
- "Thread ID to continue an existing conversation, or null to start a new one"
60
+ "Thread ID to continue an existing conversation from the same subagent, or null to start a new one"
61
61
  ),
62
62
  })
63
63
  : z.object(baseFields);
@@ -1,5 +1,4 @@
1
1
  export { createThreadManager } from "./manager";
2
- export { getShortId } from "./id";
3
2
 
4
3
  export type {
5
4
  ThreadManagerConfig,
@@ -38,9 +38,14 @@ import type { ToolResultConfig } from "../types";
38
38
 
39
39
  function createAppendSpy() {
40
40
  const calls: ToolResultConfig[] = [];
41
- const fn: AppendToolResultFn = async (config) => {
42
- calls.push(config);
43
- };
41
+ const fn = Object.assign(
42
+ async (config: ToolResultConfig) => { calls.push(config); },
43
+ { executeWithOptions: (_opts: unknown, [config]: [ToolResultConfig]) => {
44
+ calls.push(config);
45
+ return Promise.resolve();
46
+ },
47
+ },
48
+ ) as AppendToolResultFn;
44
49
  return { fn, calls };
45
50
  }
46
51
 
@@ -86,9 +86,14 @@ function createTools() {
86
86
 
87
87
  function createAppendSpy() {
88
88
  const calls: ToolResultConfig[] = [];
89
- const fn: AppendToolResultFn = async (config) => {
90
- calls.push(config);
91
- };
89
+ const fn = Object.assign(
90
+ async (config: ToolResultConfig) => { calls.push(config); },
91
+ { executeWithOptions: (_opts: unknown, [config]: [ToolResultConfig]) => {
92
+ calls.push(config);
93
+ return Promise.resolve();
94
+ },
95
+ },
96
+ ) as AppendToolResultFn;
92
97
  return { fn, calls };
93
98
  }
94
99