zeitlich 0.2.20 → 0.2.22

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 (126) hide show
  1. package/README.md +70 -55
  2. package/dist/adapters/sandbox/daytona/index.cjs +3 -0
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  4. package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
  5. package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
  6. package/dist/adapters/sandbox/daytona/index.js +3 -0
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  8. package/dist/adapters/sandbox/daytona/workflow.cjs +32 -0
  9. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -0
  10. package/dist/adapters/sandbox/daytona/workflow.d.cts +27 -0
  11. package/dist/adapters/sandbox/daytona/workflow.d.ts +27 -0
  12. package/dist/adapters/sandbox/daytona/workflow.js +30 -0
  13. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -0
  14. package/dist/adapters/sandbox/inmemory/index.cjs +4 -1
  15. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  16. package/dist/adapters/sandbox/inmemory/index.d.cts +3 -2
  17. package/dist/adapters/sandbox/inmemory/index.d.ts +3 -2
  18. package/dist/adapters/sandbox/inmemory/index.js +4 -1
  19. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  20. package/dist/adapters/sandbox/inmemory/workflow.cjs +32 -0
  21. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -0
  22. package/dist/adapters/sandbox/inmemory/workflow.d.cts +25 -0
  23. package/dist/adapters/sandbox/inmemory/workflow.d.ts +25 -0
  24. package/dist/adapters/sandbox/inmemory/workflow.js +30 -0
  25. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -0
  26. package/dist/adapters/sandbox/virtual/index.cjs +3 -0
  27. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/virtual/index.d.cts +7 -5
  29. package/dist/adapters/sandbox/virtual/index.d.ts +7 -5
  30. package/dist/adapters/sandbox/virtual/index.js +3 -0
  31. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  32. package/dist/adapters/sandbox/virtual/workflow.cjs +32 -0
  33. package/dist/adapters/sandbox/virtual/workflow.cjs.map +1 -0
  34. package/dist/adapters/sandbox/virtual/workflow.d.cts +27 -0
  35. package/dist/adapters/sandbox/virtual/workflow.d.ts +27 -0
  36. package/dist/adapters/sandbox/virtual/workflow.js +30 -0
  37. package/dist/adapters/sandbox/virtual/workflow.js.map +1 -0
  38. package/dist/adapters/thread/google-genai/index.cjs +79 -24
  39. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  40. package/dist/adapters/thread/google-genai/index.d.cts +39 -27
  41. package/dist/adapters/thread/google-genai/index.d.ts +39 -27
  42. package/dist/adapters/thread/google-genai/index.js +79 -24
  43. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  44. package/dist/adapters/thread/google-genai/workflow.cjs +33 -0
  45. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -0
  46. package/dist/adapters/thread/google-genai/workflow.d.cts +32 -0
  47. package/dist/adapters/thread/google-genai/workflow.d.ts +32 -0
  48. package/dist/adapters/thread/google-genai/workflow.js +31 -0
  49. package/dist/adapters/thread/google-genai/workflow.js.map +1 -0
  50. package/dist/adapters/thread/langchain/index.cjs +84 -71
  51. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  52. package/dist/adapters/thread/langchain/index.d.cts +35 -24
  53. package/dist/adapters/thread/langchain/index.d.ts +35 -24
  54. package/dist/adapters/thread/langchain/index.js +84 -71
  55. package/dist/adapters/thread/langchain/index.js.map +1 -1
  56. package/dist/adapters/thread/langchain/workflow.cjs +33 -0
  57. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -0
  58. package/dist/adapters/thread/langchain/workflow.d.cts +32 -0
  59. package/dist/adapters/thread/langchain/workflow.d.ts +32 -0
  60. package/dist/adapters/thread/langchain/workflow.js +31 -0
  61. package/dist/adapters/thread/langchain/workflow.js.map +1 -0
  62. package/dist/index.cjs +90 -43
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +38 -17
  65. package/dist/index.d.ts +38 -17
  66. package/dist/index.js +92 -43
  67. package/dist/index.js.map +1 -1
  68. package/dist/queries-Bw6WEPMw.d.cts +44 -0
  69. package/dist/queries-C27raDaB.d.ts +44 -0
  70. package/dist/{queries-KHj5Otv7.d.ts → types-BJ8itUAl.d.cts} +3 -44
  71. package/dist/{types-By80IE1x.d.ts → types-C5bkx6kQ.d.ts} +37 -9
  72. package/dist/{types-DZ7BkA3-.d.cts → types-ClsHhtwL.d.cts} +37 -9
  73. package/dist/{queries-nIdzTCDS.d.cts → types-ENYCKFBk.d.ts} +3 -44
  74. package/dist/{types-BMRzfELQ.d.cts → types-HBosetv3.d.cts} +15 -1
  75. package/dist/{types-BMRzfELQ.d.ts → types-HBosetv3.d.ts} +15 -1
  76. package/dist/{types-Ct2igz9y.d.cts → types-YbL7JpEA.d.cts} +1 -1
  77. package/dist/{types-Ct2igz9y.d.ts → types-YbL7JpEA.d.ts} +1 -1
  78. package/dist/workflow.cjs +11 -38
  79. package/dist/workflow.cjs.map +1 -1
  80. package/dist/workflow.d.cts +15 -43
  81. package/dist/workflow.d.ts +15 -43
  82. package/dist/workflow.js +13 -38
  83. package/dist/workflow.js.map +1 -1
  84. package/package.json +53 -1
  85. package/src/adapters/sandbox/daytona/index.ts +4 -0
  86. package/src/adapters/sandbox/daytona/proxy.ts +55 -0
  87. package/src/adapters/sandbox/e2b/filesystem.ts +147 -0
  88. package/src/adapters/sandbox/e2b/index.ts +159 -0
  89. package/src/adapters/sandbox/e2b/types.ts +23 -0
  90. package/src/adapters/sandbox/inmemory/index.ts +5 -1
  91. package/src/adapters/sandbox/inmemory/proxy.ts +53 -0
  92. package/src/adapters/sandbox/virtual/provider.ts +5 -1
  93. package/src/adapters/sandbox/virtual/proxy.ts +52 -0
  94. package/src/adapters/thread/google-genai/activities.ts +62 -26
  95. package/src/adapters/thread/google-genai/index.ts +1 -0
  96. package/src/adapters/thread/google-genai/model-invoker.ts +6 -11
  97. package/src/adapters/thread/google-genai/proxy.ts +61 -0
  98. package/src/adapters/thread/google-genai/thread-manager.ts +44 -29
  99. package/src/adapters/thread/langchain/activities.ts +53 -18
  100. package/src/adapters/thread/langchain/index.ts +1 -0
  101. package/src/adapters/thread/langchain/proxy.ts +61 -0
  102. package/src/adapters/thread/langchain/thread-manager.ts +46 -22
  103. package/src/lib/sandbox/manager.ts +40 -6
  104. package/src/lib/sandbox/sandbox.test.ts +12 -11
  105. package/src/lib/sandbox/types.ts +18 -0
  106. package/src/lib/session/index.ts +3 -5
  107. package/src/lib/session/session-edge-cases.integration.test.ts +45 -34
  108. package/src/lib/session/session.integration.test.ts +40 -48
  109. package/src/lib/session/session.ts +9 -70
  110. package/src/lib/session/types.ts +39 -3
  111. package/src/lib/subagent/define.ts +1 -1
  112. package/src/lib/subagent/handler.ts +9 -2
  113. package/src/lib/subagent/index.ts +1 -0
  114. package/src/lib/subagent/subagent.integration.test.ts +62 -0
  115. package/src/lib/subagent/types.ts +7 -2
  116. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +20 -21
  117. package/src/lib/tool-router/auto-append.ts +3 -2
  118. package/src/lib/tool-router/router-edge-cases.integration.test.ts +63 -24
  119. package/src/lib/tool-router/router.integration.test.ts +59 -24
  120. package/src/lib/tool-router/router.ts +4 -3
  121. package/src/lib/tool-router/types.ts +1 -1
  122. package/src/lib/workflow.test.ts +19 -10
  123. package/src/lib/workflow.ts +4 -1
  124. package/src/tools/bash/bash.test.ts +16 -7
  125. package/src/workflow.ts +6 -14
  126. package/tsup.config.ts +6 -0
@@ -9,7 +9,6 @@ import {
9
9
  SystemMessage,
10
10
  ToolMessage,
11
11
  } from "@langchain/core/messages";
12
- import { v4 as uuidv4 } from "uuid";
13
12
  import {
14
13
  createThreadManager,
15
14
  type BaseThreadManager,
@@ -30,31 +29,44 @@ export interface LangChainThreadManagerConfig {
30
29
 
31
30
  /** Thread manager with LangChain StoredMessage convenience helpers */
32
31
  export interface LangChainThreadManager extends BaseThreadManager<StoredMessage> {
33
- createHumanMessage(content: string | MessageContent): StoredMessage;
34
- createSystemMessage(content: string): StoredMessage;
32
+ createHumanMessage(
33
+ id: string,
34
+ content: string | MessageContent
35
+ ): StoredMessage;
36
+ createSystemMessage(id: string, content: string): StoredMessage;
35
37
  createAIMessage(
38
+ id: string,
36
39
  content: string | MessageContent,
37
40
  kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
38
41
  ): StoredMessage;
39
42
  createToolMessage(
43
+ id: string,
40
44
  content: LangChainToolMessageContent,
41
45
  toolCallId: string
42
46
  ): StoredMessage;
43
- appendHumanMessage(content: string | MessageContent): Promise<void>;
44
- appendSystemMessage(content: string): Promise<void>;
47
+ appendHumanMessage(
48
+ id: string,
49
+ content: string | MessageContent
50
+ ): Promise<void>;
51
+ appendSystemMessage(id: string, content: string): Promise<void>;
45
52
  appendToolMessage(
53
+ id: string,
46
54
  content: LangChainToolMessageContent,
47
55
  toolCallId: string
48
56
  ): Promise<void>;
49
- appendAIMessage(content: string | MessageContent): Promise<void>;
57
+ appendAIMessage(id: string, content: string | MessageContent): Promise<void>;
50
58
  }
51
59
 
52
60
  function storedMessageId(msg: StoredMessage): string {
53
- if (msg.type === "tool") {
54
- return msg.data.tool_call_id ?? "";
61
+ if (msg.type === "tool" && msg.data.tool_call_id) {
62
+ return msg.data.tool_call_id;
63
+ }
64
+
65
+ if (msg.data.id) {
66
+ return msg.data.id;
55
67
  }
56
68
 
57
- return msg.data.id ?? "";
69
+ throw new Error("No id found for message");
58
70
  }
59
71
 
60
72
  /**
@@ -75,26 +87,30 @@ export function createLangChainThreadManager(
75
87
  const base = createThreadManager(baseConfig);
76
88
 
77
89
  const helpers = {
78
- createHumanMessage(content: string | MessageContent): StoredMessage {
90
+ createHumanMessage(
91
+ id: string,
92
+ content: string | MessageContent
93
+ ): StoredMessage {
79
94
  return new HumanMessage({
80
- id: uuidv4(),
95
+ id,
81
96
  content: content as string,
82
97
  }).toDict();
83
98
  },
84
99
 
85
- createSystemMessage(content: string): StoredMessage {
100
+ createSystemMessage(id: string, content: string): StoredMessage {
86
101
  return new SystemMessage({
87
- id: uuidv4(),
102
+ id,
88
103
  content: content as string,
89
104
  }).toDict();
90
105
  },
91
106
 
92
107
  createAIMessage(
108
+ id: string,
93
109
  content: string,
94
110
  kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
95
111
  ): StoredMessage {
96
112
  return new AIMessage({
97
- id: uuidv4(),
113
+ id,
98
114
  content,
99
115
  additional_kwargs: kwargs
100
116
  ? {
@@ -107,36 +123,44 @@ export function createLangChainThreadManager(
107
123
  },
108
124
 
109
125
  createToolMessage(
126
+ id: string,
110
127
  content: LangChainToolMessageContent,
111
128
  toolCallId: string
112
129
  ): StoredMessage {
113
130
  return new ToolMessage({
114
- id: uuidv4(),
131
+ id,
115
132
  content: content as MessageContent,
116
133
  tool_call_id: toolCallId,
117
134
  }).toDict();
118
135
  },
119
136
 
120
- async appendHumanMessage(content: string | MessageContent): Promise<void> {
121
- const message = helpers.createHumanMessage(content);
137
+ async appendHumanMessage(
138
+ id: string,
139
+ content: string | MessageContent
140
+ ): Promise<void> {
141
+ const message = helpers.createHumanMessage(id, content);
122
142
  await base.append([message]);
123
143
  },
124
144
 
125
145
  async appendToolMessage(
146
+ id: string,
126
147
  content: LangChainToolMessageContent,
127
148
  toolCallId: string
128
149
  ): Promise<void> {
129
- const message = helpers.createToolMessage(content, toolCallId);
150
+ const message = helpers.createToolMessage(id, content, toolCallId);
130
151
  await base.append([message]);
131
152
  },
132
153
 
133
- async appendAIMessage(content: string | MessageContent): Promise<void> {
134
- const message = helpers.createAIMessage(content as string);
154
+ async appendAIMessage(
155
+ id: string,
156
+ content: string | MessageContent
157
+ ): Promise<void> {
158
+ const message = helpers.createAIMessage(id, content as string);
135
159
  await base.append([message]);
136
160
  },
137
161
 
138
- async appendSystemMessage(content: string): Promise<void> {
139
- const message = helpers.createSystemMessage(content);
162
+ async appendSystemMessage(id: string, content: string): Promise<void> {
163
+ const message = helpers.createSystemMessage(id, content);
140
164
  await base.initialize();
141
165
  await base.append([message]);
142
166
  },
@@ -2,6 +2,7 @@ import type {
2
2
  Sandbox,
3
3
  SandboxCreateOptions,
4
4
  SandboxOps,
5
+ PrefixedSandboxOps,
5
6
  SandboxProvider,
6
7
  SandboxSnapshot,
7
8
  } from "./types";
@@ -16,16 +17,18 @@ import type {
16
17
  * ```typescript
17
18
  * const manager = new SandboxManager(new InMemorySandboxProvider());
18
19
  * const activities = {
19
- * ...manager.createActivities(),
20
+ * ...manager.createActivities("CodingAgent"),
20
21
  * bashHandler: withSandbox(manager, bashHandler),
21
22
  * };
23
+ * // registers: inMemoryCodingAgentCreateSandbox, …
22
24
  * ```
23
25
  */
24
26
  export class SandboxManager<
25
27
  TOptions extends SandboxCreateOptions = SandboxCreateOptions,
26
28
  TSandbox extends Sandbox = Sandbox,
29
+ TId extends string = string,
27
30
  > {
28
- constructor(private provider: SandboxProvider<TOptions, TSandbox>) {}
31
+ constructor(private provider: SandboxProvider<TOptions, TSandbox> & { readonly id: TId }) {}
29
32
 
30
33
  async create(
31
34
  options?: TOptions
@@ -51,12 +54,36 @@ export class SandboxManager<
51
54
  return sandbox.id;
52
55
  }
53
56
 
57
+ async fork(sandboxId: string): Promise<string> {
58
+ const sandbox = await this.provider.fork(sandboxId);
59
+ return sandbox.id;
60
+ }
61
+
54
62
  /**
55
- * Returns Temporal activity functions matching {@link SandboxOps}.
56
- * Spread these into your worker's activity map.
63
+ * Returns Temporal activity functions with prefixed names.
64
+ *
65
+ * The provider's `id` is automatically prepended, so you only need
66
+ * to pass the workflow/scope name. Use the matching `proxy*SandboxOps()`
67
+ * helper from the adapter's `/workflow` entrypoint on the workflow side.
68
+ *
69
+ * @param scope - Workflow name (appended to the provider id)
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const manager = new SandboxManager(new InMemorySandboxProvider());
74
+ * manager.createActivities("CodingAgent");
75
+ * // registers: inMemoryCodingAgentCreateSandbox, inMemoryCodingAgentDestroySandbox, …
76
+ *
77
+ * const vmgr = new SandboxManager(new VirtualSandboxProvider(resolver));
78
+ * vmgr.createActivities("CodingAgent");
79
+ * // registers: virtualCodingAgentCreateSandbox, …
80
+ * ```
57
81
  */
58
- createActivities(): SandboxOps<TOptions> {
59
- return {
82
+ createActivities<S extends string>(
83
+ scope: S
84
+ ): PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions> {
85
+ const prefix = `${this.provider.id}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
86
+ const ops: SandboxOps<TOptions> = {
60
87
  createSandbox: async (
61
88
  options?: TOptions
62
89
  ): Promise<{
@@ -71,6 +98,13 @@ export class SandboxManager<
71
98
  snapshotSandbox: async (sandboxId: string): Promise<SandboxSnapshot> => {
72
99
  return this.snapshot(sandboxId);
73
100
  },
101
+ forkSandbox: async (sandboxId: string): Promise<string> => {
102
+ return this.fork(sandboxId);
103
+ },
74
104
  };
105
+ const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
106
+ return Object.fromEntries(
107
+ Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
108
+ ) as PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions>;
75
109
  }
76
110
  }
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, it, beforeEach } from "vitest";
2
2
  import { SandboxManager } from "./manager";
3
3
  import { InMemorySandboxProvider } from "../../adapters/sandbox/inmemory/index";
4
- import { SandboxNotFoundError } from "./types";
4
+ import { SandboxNotFoundError, type Sandbox, type SandboxCreateOptions } from "./types";
5
5
 
6
6
  describe("SandboxManager", () => {
7
- let manager: SandboxManager;
7
+ let manager: SandboxManager<SandboxCreateOptions, Sandbox, "inMemory">;
8
8
 
9
9
  beforeEach(() => {
10
10
  manager = new SandboxManager(new InMemorySandboxProvider());
@@ -54,7 +54,7 @@ describe("SandboxManager", () => {
54
54
 
55
55
  const snapshot = await manager.snapshot(sandboxId);
56
56
  expect(snapshot.sandboxId).toBe(sandboxId);
57
- expect(snapshot.providerId).toBe("inmemory");
57
+ expect(snapshot.providerId).toBe("inMemory");
58
58
 
59
59
  await manager.destroy(sandboxId);
60
60
  await expect(manager.getSandbox(sandboxId)).rejects.toThrow(SandboxNotFoundError);
@@ -68,16 +68,17 @@ describe("SandboxManager", () => {
68
68
  expect(extra).toBe("world");
69
69
  });
70
70
 
71
- it("createActivities returns SandboxOps-shaped object", async () => {
72
- const activities = manager.createActivities();
73
- expect(activities.createSandbox).toBeTypeOf("function");
74
- expect(activities.destroySandbox).toBeTypeOf("function");
75
- expect(activities.snapshotSandbox).toBeTypeOf("function");
71
+ it("createActivities returns prefixed SandboxOps-shaped object", async () => {
72
+ // provider.id is "inMemory", scope is "Test" → prefix "inMemoryTest"
73
+ const activities = manager.createActivities("Test");
74
+ expect(activities.inMemoryTestCreateSandbox).toBeTypeOf("function");
75
+ expect(activities.inMemoryTestDestroySandbox).toBeTypeOf("function");
76
+ expect(activities.inMemoryTestSnapshotSandbox).toBeTypeOf("function");
76
77
 
77
- const { sandboxId } = await activities.createSandbox();
78
+ const { sandboxId } = await activities.inMemoryTestCreateSandbox();
78
79
  await expect(manager.getSandbox(sandboxId)).resolves.toBeTruthy();
79
80
 
80
- await activities.destroySandbox(sandboxId);
81
+ await activities.inMemoryTestDestroySandbox(sandboxId);
81
82
  await expect(manager.getSandbox(sandboxId)).rejects.toThrow(
82
83
  SandboxNotFoundError,
83
84
  );
@@ -85,7 +86,7 @@ describe("SandboxManager", () => {
85
86
  });
86
87
 
87
88
  describe("InMemorySandboxProvider", () => {
88
- let manager: SandboxManager;
89
+ let manager: SandboxManager<SandboxCreateOptions, Sandbox, "inMemory">;
89
90
 
90
91
  beforeEach(() => {
91
92
  manager = new SandboxManager(new InMemorySandboxProvider());
@@ -127,6 +127,7 @@ export interface SandboxProvider<
127
127
  destroy(sandboxId: string): Promise<void>;
128
128
  snapshot(sandboxId: string): Promise<SandboxSnapshot>;
129
129
  restore(snapshot: SandboxSnapshot): Promise<Sandbox>;
130
+ fork(sandboxId: string): Promise<Sandbox>;
130
131
  }
131
132
 
132
133
  // ============================================================================
@@ -141,8 +142,25 @@ export interface SandboxOps<
141
142
  ): Promise<{ sandboxId: string; stateUpdate?: Record<string, unknown> }>;
142
143
  destroySandbox(sandboxId: string): Promise<void>;
143
144
  snapshotSandbox(sandboxId: string): Promise<SandboxSnapshot>;
145
+ forkSandbox(sandboxId: string): Promise<string>;
144
146
  }
145
147
 
148
+ /**
149
+ * Maps generic {@link SandboxOps} method names to adapter-prefixed names.
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * type InMemOps = PrefixedSandboxOps<"inMemory">;
154
+ * // → { inMemoryCreateSandbox, inMemoryDestroySandbox, inMemorySnapshotSandbox }
155
+ * ```
156
+ */
157
+ export type PrefixedSandboxOps<
158
+ TPrefix extends string,
159
+ TOptions extends SandboxCreateOptions = SandboxCreateOptions,
160
+ > = {
161
+ [K in keyof SandboxOps<TOptions> as `${TPrefix}${Capitalize<K & string>}`]: SandboxOps<TOptions>[K];
162
+ };
163
+
146
164
  // ============================================================================
147
165
  // Errors
148
166
  // ============================================================================
@@ -1,11 +1,9 @@
1
- export {
2
- createSession,
3
- proxyDefaultThreadOps,
4
- proxySandboxOps,
5
- } from "./session";
1
+ export { createSession } from "./session";
6
2
 
7
3
  export type {
8
4
  ThreadOps,
5
+ PrefixedThreadOps,
6
+ ScopedPrefix,
9
7
  SessionConfig,
10
8
  ZeitlichSession,
11
9
  } from "./types";
@@ -56,12 +56,11 @@ type TurnScript = {
56
56
  * Wraps every method on a ThreadOps object so it also has `.executeWithOptions()`,
57
57
  * matching Temporal's `ActivityInterfaceFor<ThreadOps>` shape.
58
58
  */
59
- function toActivityInterface(
60
- raw: ThreadOps,
61
- ): ActivityInterfaceFor<ThreadOps> {
59
+ function toActivityInterface(raw: ThreadOps): ActivityInterfaceFor<ThreadOps> {
62
60
  const result = {} as Record<string, unknown>;
63
61
  for (const [key, fn] of Object.entries(raw)) {
64
- const wrapped = (...args: unknown[]) => (fn as (...a: unknown[]) => unknown)(...args);
62
+ const wrapped = (...args: unknown[]) =>
63
+ (fn as (...a: unknown[]) => unknown)(...args);
65
64
  wrapped.executeWithOptions = (_opts: unknown, args: unknown[]) =>
66
65
  (fn as (...a: unknown[]) => unknown)(...args);
67
66
  result[key] = wrapped;
@@ -75,14 +74,14 @@ function createMockThreadOps() {
75
74
  initializeThread: async (threadId) => {
76
75
  log.push({ op: "initializeThread", args: [threadId] });
77
76
  },
78
- appendHumanMessage: async (threadId, content) => {
79
- log.push({ op: "appendHumanMessage", args: [threadId, content] });
77
+ appendHumanMessage: async (threadId, id, content) => {
78
+ log.push({ op: "appendHumanMessage", args: [threadId, id, content] });
80
79
  },
81
- appendToolResult: async (config) => {
82
- log.push({ op: "appendToolResult", args: [config] });
80
+ appendToolResult: async (id, config) => {
81
+ log.push({ op: "appendToolResult", args: [id, config] });
83
82
  },
84
- appendSystemMessage: async (threadId, content) => {
85
- log.push({ op: "appendSystemMessage", args: [threadId, content] });
83
+ appendSystemMessage: async (threadId, id, content) => {
84
+ log.push({ op: "appendSystemMessage", args: [threadId, id, content] });
86
85
  },
87
86
  forkThread: async (source, target) => {
88
87
  log.push({ op: "forkThread", args: [source, target] });
@@ -91,7 +90,9 @@ function createMockThreadOps() {
91
90
  return { ops, log };
92
91
  }
93
92
 
94
- function createScriptedRunAgent(turns: TurnScript[]): RunAgentActivity<unknown> {
93
+ function createScriptedRunAgent(
94
+ turns: TurnScript[]
95
+ ): RunAgentActivity<unknown> {
95
96
  let call = 0;
96
97
  return async () => {
97
98
  const turn = turns[call++];
@@ -113,7 +114,7 @@ function createEchoTool() {
113
114
  schema: z.object({ text: z.string() }),
114
115
  handler: async (
115
116
  args: { text: string },
116
- _ctx: RouterContext,
117
+ _ctx: RouterContext
117
118
  ): Promise<ToolHandlerResponse<{ echoed: string }>> => ({
118
119
  toolResponse: `Echo: ${args.text}`,
119
120
  data: { echoed: args.text },
@@ -216,8 +217,11 @@ describe("createSession edge cases", () => {
216
217
 
217
218
  const errorResults = log.filter((l) => {
218
219
  if (l.op !== "appendToolResult") return false;
219
- const config = l.args[0] as ToolResultConfig;
220
- return typeof config.content === "string" && config.content.includes("Invalid tool call");
220
+ const config = l.args[1] as ToolResultConfig;
221
+ return (
222
+ typeof config.content === "string" &&
223
+ config.content.includes("Invalid tool call")
224
+ );
221
225
  });
222
226
  expect(errorResults).toHaveLength(2);
223
227
  });
@@ -365,7 +369,7 @@ describe("createSession edge cases", () => {
365
369
  });
366
370
 
367
371
  await expect(session.runSession({ stateManager })).rejects.toThrow(
368
- "unrecoverable tool",
372
+ "unrecoverable tool"
369
373
  );
370
374
  expect(endReason).toBe("failed");
371
375
  });
@@ -381,9 +385,7 @@ describe("createSession edge cases", () => {
381
385
  agentName: "TestAgent",
382
386
  threadId: "thread-1",
383
387
  metadata: { env: "test", version: 42 },
384
- runAgent: createScriptedRunAgent([
385
- { message: "done", toolCalls: [] },
386
- ]),
388
+ runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
387
389
  threadOps: ops,
388
390
  buildContextMessage: () => "go",
389
391
  hooks: {
@@ -422,6 +424,7 @@ describe("createSession edge cases", () => {
422
424
  data: null,
423
425
  createdAt: new Date().toISOString(),
424
426
  }),
427
+ forkSandbox: async () => "forked-sandbox-id",
425
428
  };
426
429
 
427
430
  const session = await createSession({
@@ -438,7 +441,7 @@ describe("createSession edge cases", () => {
438
441
  });
439
442
 
440
443
  await expect(session.runSession({ stateManager })).rejects.toThrow(
441
- "sandbox creation failed",
444
+ "sandbox creation failed"
442
445
  );
443
446
  });
444
447
 
@@ -462,6 +465,7 @@ describe("createSession edge cases", () => {
462
465
  data: null,
463
466
  createdAt: new Date().toISOString(),
464
467
  }),
468
+ forkSandbox: async () => "forked-sandbox-id",
465
469
  };
466
470
 
467
471
  const session = await createSession({
@@ -480,7 +484,7 @@ describe("createSession edge cases", () => {
480
484
  });
481
485
 
482
486
  await expect(session.runSession({ stateManager })).rejects.toThrow(
483
- "LLM crash",
487
+ "LLM crash"
484
488
  );
485
489
 
486
490
  expect(sandboxLog).toContain("create");
@@ -505,7 +509,7 @@ describe("createSession edge cases", () => {
505
509
  });
506
510
 
507
511
  await expect(session.runSession({ stateManager })).rejects.toThrow(
508
- "No system prompt in state",
512
+ "No system prompt in state"
509
513
  );
510
514
  });
511
515
 
@@ -700,24 +704,27 @@ describe("createSession edge cases", () => {
700
704
 
701
705
  const toolResults = log.filter((l) => l.op === "appendToolResult");
702
706
  const echoResult = toolResults.find((l) => {
703
- const config = l.args[0] as ToolResultConfig;
707
+ const config = l.args[1] as ToolResultConfig;
704
708
  return config.toolName === "Echo";
705
709
  });
706
710
  expect(echoResult).toBeDefined();
707
711
  if (echoResult) {
708
- expect((echoResult.args[0] as ToolResultConfig).content).toBe("Echo: valid");
712
+ expect((echoResult.args[1] as ToolResultConfig).content).toBe(
713
+ "Echo: valid"
714
+ );
709
715
  }
710
716
 
711
717
  const unknownResult = toolResults.find((l) => {
712
- const config = l.args[0] as ToolResultConfig;
718
+ const config = l.args[1] as ToolResultConfig;
713
719
  return config.toolName === "Unknown";
714
720
  });
715
721
  expect(unknownResult).toBeDefined();
716
722
  const unknownContent = unknownResult
717
- ? (unknownResult.args[0] as ToolResultConfig).content
723
+ ? (unknownResult.args[1] as ToolResultConfig).content
718
724
  : undefined;
719
725
  expect(
720
- typeof unknownContent === "string" && unknownContent.includes("Invalid tool call"),
726
+ typeof unknownContent === "string" &&
727
+ unknownContent.includes("Invalid tool call")
721
728
  ).toBe(true);
722
729
  });
723
730
 
@@ -729,9 +736,7 @@ describe("createSession edge cases", () => {
729
736
  const session = await createSession({
730
737
  agentName: "TestAgent",
731
738
  threadId: "thread-1",
732
- runAgent: createScriptedRunAgent([
733
- { message: "done", toolCalls: [] },
734
- ]),
739
+ runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
735
740
  threadOps: ops,
736
741
  buildContextMessage: () => [
737
742
  { type: "text", text: "Hello" },
@@ -749,7 +754,7 @@ describe("createSession edge cases", () => {
749
754
  expect(humanOps).toHaveLength(1);
750
755
  const humanOp = humanOps[0];
751
756
  if (!humanOp) throw new Error("expected human op");
752
- const content = humanOp.args[1];
757
+ const content = humanOp.args[2];
753
758
  expect(Array.isArray(content)).toBe(true);
754
759
  const firstContent = (content as { type: string }[])[0];
755
760
  if (!firstContent) throw new Error("expected content item");
@@ -834,7 +839,7 @@ describe("createSession edge cases", () => {
834
839
 
835
840
  const toolResults = log.filter((l) => {
836
841
  if (l.op !== "appendToolResult") return false;
837
- const config = l.args[0] as ToolResultConfig;
842
+ const config = l.args[1] as ToolResultConfig;
838
843
  return config.toolName === "SelfAppend";
839
844
  });
840
845
  expect(toolResults).toHaveLength(0);
@@ -860,7 +865,10 @@ describe("createSession edge cases", () => {
860
865
  buildContextMessage: () => "go",
861
866
  hooks: {
862
867
  onPreToolUse: async ({ toolCall }) => {
863
- if (toolCall.args && (toolCall.args as { text: string }).text === "skip-me") {
868
+ if (
869
+ toolCall.args &&
870
+ (toolCall.args as { text: string }).text === "skip-me"
871
+ ) {
864
872
  return { skip: true };
865
873
  }
866
874
  return {};
@@ -878,8 +886,10 @@ describe("createSession edge cases", () => {
878
886
  expect(toolResults).toHaveLength(1);
879
887
  const toolResult = toolResults[0];
880
888
  if (!toolResult) throw new Error("expected tool result");
881
- const content = (toolResult.args[0] as ToolResultConfig).content;
882
- expect(typeof content === "string" && content.includes("Skipped")).toBe(true);
889
+ const content = (toolResult.args[1] as ToolResultConfig).content;
890
+ expect(typeof content === "string" && content.includes("Skipped")).toBe(
891
+ true
892
+ );
883
893
  });
884
894
 
885
895
  // --- Sandbox snapshot is not called on normal flow ---
@@ -897,6 +907,7 @@ describe("createSession edge cases", () => {
897
907
  createSandbox: async () => ({ sandboxId: "sb-test" }),
898
908
  destroySandbox: async () => {},
899
909
  snapshotSandbox: snapshotSpy,
910
+ forkSandbox: async () => "forked-sandbox-id",
900
911
  };
901
912
 
902
913
  const session = await createSession({