zeitlich 0.2.33 → 0.2.35
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 +17 -6
- package/dist/{activities-YBD5BaHh.d.ts → activities-BVI2lTwr.d.ts} +6 -4
- package/dist/{activities-fnX8-vhR.d.cts → activities-hd4aNnZE.d.cts} +6 -4
- package/dist/adapters/sandbox/bedrock/index.cjs +2 -0
- package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
- package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
- package/dist/adapters/sandbox/bedrock/index.js +2 -0
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.cjs +1 -0
- package/dist/adapters/sandbox/bedrock/workflow.cjs.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/bedrock/workflow.js +1 -0
- package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
- package/dist/adapters/sandbox/daytona/index.cjs +2 -0
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
- package/dist/adapters/sandbox/daytona/index.js +2 -0
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +1 -0
- package/dist/adapters/sandbox/daytona/workflow.cjs.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/daytona/workflow.js +1 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
- package/dist/adapters/sandbox/e2b/index.cjs +3 -0
- package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +2 -1
- package/dist/adapters/sandbox/e2b/index.d.ts +2 -1
- package/dist/adapters/sandbox/e2b/index.js +3 -0
- package/dist/adapters/sandbox/e2b/index.js.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.cjs +1 -0
- package/dist/adapters/sandbox/e2b/workflow.cjs.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/e2b/workflow.js +1 -0
- package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs +2 -0
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
- package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
- package/dist/adapters/sandbox/inmemory/index.js +2 -0
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +1 -0
- package/dist/adapters/sandbox/inmemory/workflow.cjs.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/sandbox/inmemory/workflow.js +1 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +7 -2
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +5 -5
- package/dist/adapters/thread/anthropic/index.d.ts +5 -5
- package/dist/adapters/thread/anthropic/index.js +7 -2
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
- package/dist/adapters/thread/google-genai/index.cjs +4 -3
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +5 -5
- package/dist/adapters/thread/google-genai/index.d.ts +5 -5
- package/dist/adapters/thread/google-genai/index.js +4 -3
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
- package/dist/adapters/thread/langchain/index.cjs +4 -1
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +5 -5
- package/dist/adapters/thread/langchain/index.d.ts +5 -5
- package/dist/adapters/thread/langchain/index.js +4 -1
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
- package/dist/index.cjs +115 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -9
- package/dist/index.d.ts +10 -9
- package/dist/index.js +115 -31
- package/dist/index.js.map +1 -1
- package/dist/{proxy-CTCYWjkr.d.cts → proxy-7RnVaPdJ.d.cts} +1 -1
- package/dist/{proxy-Br4unLTC.d.ts → proxy-BjdFGPTm.d.ts} +1 -1
- package/dist/{thread-manager-DKWxHUzD.d.ts → thread-manager-BBzNgQWH.d.cts} +5 -2
- package/dist/{thread-manager-CUubPYPH.d.cts → thread-manager-CbpiGq1L.d.ts} +6 -3
- package/dist/{thread-manager-Cv_BR28i.d.cts → thread-manager-DjN5JYul.d.ts} +5 -2
- package/dist/{thread-manager-YJLoc1vH.d.ts → thread-manager-DzXm9eeI.d.cts} +6 -3
- package/dist/{types-Bpq5fDI5.d.cts → types-CADc5V_P.d.ts} +39 -24
- package/dist/{types-DUvEZSDe.d.cts → types-CBH54cwr.d.cts} +1 -1
- package/dist/{types-CheCTLeV.d.ts → types-DQ1l_gXL.d.cts} +39 -24
- package/dist/{types-AujBIMMn.d.cts → types-DxCpFNv_.d.cts} +4 -0
- package/dist/{types-AujBIMMn.d.ts → types-DxCpFNv_.d.ts} +4 -0
- package/dist/{types-NJDyMyUx.d.cts → types-Mc_4BCfT.d.cts} +3 -3
- package/dist/{types-DBk-C8zM.d.ts → types-wiGLvxWf.d.ts} +1 -1
- package/dist/{types-BxiT8w9d.d.ts → types-yiXmqedU.d.ts} +3 -3
- package/dist/{workflow-Od9vx5Jk.d.cts → workflow-DhtWRovz.d.cts} +3 -3
- package/dist/{workflow-D9nNERvs.d.ts → workflow-P2pTSfKu.d.ts} +3 -3
- package/dist/workflow.cjs +109 -31
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +3 -3
- package/dist/workflow.d.ts +3 -3
- package/dist/workflow.js +109 -31
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/sandbox/bedrock/index.ts +4 -0
- package/src/adapters/sandbox/bedrock/proxy.ts +1 -0
- package/src/adapters/sandbox/daytona/index.ts +4 -0
- package/src/adapters/sandbox/daytona/proxy.ts +1 -0
- package/src/adapters/sandbox/e2b/index.ts +4 -0
- package/src/adapters/sandbox/e2b/proxy.ts +1 -0
- package/src/adapters/sandbox/inmemory/index.ts +4 -0
- package/src/adapters/sandbox/inmemory/proxy.ts +1 -0
- package/src/adapters/thread/anthropic/activities.ts +2 -1
- package/src/adapters/thread/anthropic/thread-manager.ts +21 -9
- package/src/adapters/thread/google-genai/activities.ts +2 -1
- package/src/adapters/thread/google-genai/thread-manager.test.ts +1 -1
- package/src/adapters/thread/google-genai/thread-manager.ts +15 -7
- package/src/adapters/thread/langchain/activities.ts +2 -1
- package/src/adapters/thread/langchain/thread-manager.ts +12 -3
- package/src/lib/lifecycle.ts +7 -3
- package/src/lib/sandbox/manager.ts +7 -0
- package/src/lib/sandbox/types.ts +4 -0
- package/src/lib/session/session-edge-cases.integration.test.ts +194 -0
- package/src/lib/session/session.integration.test.ts +5 -0
- package/src/lib/session/session.ts +13 -1
- package/src/lib/session/types.ts +10 -2
- package/src/lib/state/manager.ts +2 -2
- package/src/lib/state/types.ts +2 -2
- package/src/lib/subagent/define.ts +1 -1
- package/src/lib/subagent/handler.ts +142 -32
- package/src/lib/subagent/index.ts +5 -1
- package/src/lib/subagent/signals.ts +8 -1
- package/src/lib/subagent/subagent.integration.test.ts +532 -25
- package/src/lib/subagent/types.ts +32 -15
- package/src/lib/subagent/workflow.ts +26 -13
- package/src/lib/thread/types.ts +2 -2
- package/src/lib/types.ts +1 -1
- package/src/lib/virtual-fs/manager.ts +1 -1
- package/src/lib/virtual-fs/types.ts +2 -2
- package/src/lib/virtual-fs/virtual-fs.test.ts +2 -2
- package/src/workflow.ts +0 -1
package/package.json
CHANGED
|
@@ -237,6 +237,10 @@ export class BedrockSandboxProvider
|
|
|
237
237
|
throw new SandboxNotSupportedError("pause");
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
async resume(_sandboxId: string): Promise<void> {
|
|
241
|
+
// Bedrock sandboxes don't support pause, so resume is a no-op
|
|
242
|
+
}
|
|
243
|
+
|
|
240
244
|
async snapshot(_sandboxId: string): Promise<SandboxSnapshot> {
|
|
241
245
|
throw new SandboxNotSupportedError("snapshot");
|
|
242
246
|
}
|
|
@@ -50,6 +50,7 @@ export function proxyBedrockSandboxOps(
|
|
|
50
50
|
createSandbox: acts[p("createSandbox")],
|
|
51
51
|
destroySandbox: acts[p("destroySandbox")],
|
|
52
52
|
pauseSandbox: acts[p("pauseSandbox")],
|
|
53
|
+
resumeSandbox: acts[p("resumeSandbox")],
|
|
53
54
|
snapshotSandbox: acts[p("snapshotSandbox")],
|
|
54
55
|
forkSandbox: acts[p("forkSandbox")],
|
|
55
56
|
} as SandboxOps<BedrockSandboxCreateOptions>;
|
|
@@ -145,6 +145,10 @@ export class DaytonaSandboxProvider implements SandboxProvider<
|
|
|
145
145
|
throw new SandboxNotSupportedError("pause");
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
async resume(_sandboxId: string): Promise<void> {
|
|
149
|
+
// Daytona sandboxes don't support pause, so resume is a no-op
|
|
150
|
+
}
|
|
151
|
+
|
|
148
152
|
async fork(_sandboxId: string): Promise<Sandbox> {
|
|
149
153
|
throw new Error("Not implemented");
|
|
150
154
|
}
|
|
@@ -50,6 +50,7 @@ export function proxyDaytonaSandboxOps(
|
|
|
50
50
|
createSandbox: acts[p("createSandbox")],
|
|
51
51
|
destroySandbox: acts[p("destroySandbox")],
|
|
52
52
|
pauseSandbox: acts[p("pauseSandbox")],
|
|
53
|
+
resumeSandbox: acts[p("resumeSandbox")],
|
|
53
54
|
snapshotSandbox: acts[p("snapshotSandbox")],
|
|
54
55
|
forkSandbox: acts[p("forkSandbox")],
|
|
55
56
|
} as SandboxOps<DaytonaSandboxCreateOptions>;
|
|
@@ -136,6 +136,10 @@ export class E2bSandboxProvider
|
|
|
136
136
|
await sdkSandbox.pause();
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
async resume(sandboxId: string): Promise<void> {
|
|
140
|
+
await E2bSdkSandbox.connect(sandboxId);
|
|
141
|
+
}
|
|
142
|
+
|
|
139
143
|
async snapshot(_sandboxId: string): Promise<SandboxSnapshot> {
|
|
140
144
|
throw new SandboxNotSupportedError("snapshot");
|
|
141
145
|
}
|
|
@@ -50,6 +50,7 @@ export function proxyE2bSandboxOps(
|
|
|
50
50
|
createSandbox: acts[p("createSandbox")],
|
|
51
51
|
destroySandbox: acts[p("destroySandbox")],
|
|
52
52
|
pauseSandbox: acts[p("pauseSandbox")],
|
|
53
|
+
resumeSandbox: acts[p("resumeSandbox")],
|
|
53
54
|
snapshotSandbox: acts[p("snapshotSandbox")],
|
|
54
55
|
forkSandbox: acts[p("forkSandbox")],
|
|
55
56
|
} as SandboxOps<E2bSandboxCreateOptions>;
|
|
@@ -163,6 +163,10 @@ export class InMemorySandboxProvider implements SandboxProvider {
|
|
|
163
163
|
// In-memory: nothing to pause
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
async resume(_sandboxId: string): Promise<void> {
|
|
167
|
+
// In-memory: nothing to resume
|
|
168
|
+
}
|
|
169
|
+
|
|
166
170
|
async create(options?: SandboxCreateOptions): Promise<SandboxCreateResult> {
|
|
167
171
|
const id = options?.id ?? getShortId();
|
|
168
172
|
const initialFiles: InitialFiles = {};
|
|
@@ -47,6 +47,7 @@ export function proxyInMemorySandboxOps(
|
|
|
47
47
|
createSandbox: acts[p("createSandbox")],
|
|
48
48
|
destroySandbox: acts[p("destroySandbox")],
|
|
49
49
|
pauseSandbox: acts[p("pauseSandbox")],
|
|
50
|
+
resumeSandbox: acts[p("resumeSandbox")],
|
|
50
51
|
snapshotSandbox: acts[p("snapshotSandbox")],
|
|
51
52
|
forkSandbox: acts[p("forkSandbox")],
|
|
52
53
|
} as SandboxOps;
|
|
@@ -15,6 +15,7 @@ import type { ModelInvoker } from "../../../lib/model";
|
|
|
15
15
|
import {
|
|
16
16
|
createAnthropicThreadManager,
|
|
17
17
|
type AnthropicContent,
|
|
18
|
+
type AnthropicSystemContent,
|
|
18
19
|
type AnthropicThreadManagerHooks,
|
|
19
20
|
} from "./thread-manager";
|
|
20
21
|
import {
|
|
@@ -152,7 +153,7 @@ export function createAnthropicAdapter(
|
|
|
152
153
|
async appendSystemMessage(
|
|
153
154
|
threadId: string,
|
|
154
155
|
id: string,
|
|
155
|
-
content:
|
|
156
|
+
content: AnthropicSystemContent,
|
|
156
157
|
threadKey?: string,
|
|
157
158
|
): Promise<void> {
|
|
158
159
|
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
|
|
@@ -13,6 +13,11 @@ export type AnthropicContent =
|
|
|
13
13
|
| string
|
|
14
14
|
| Anthropic.Messages.ContentBlockParam[];
|
|
15
15
|
|
|
16
|
+
/** SDK-native content type for Anthropic system prompts (supports cache_control blocks) */
|
|
17
|
+
export type AnthropicSystemContent =
|
|
18
|
+
| string
|
|
19
|
+
| Anthropic.Messages.TextBlockParam[];
|
|
20
|
+
|
|
16
21
|
/** A MessageParam with a unique ID for idempotent Redis storage */
|
|
17
22
|
export interface StoredMessage {
|
|
18
23
|
id: string;
|
|
@@ -34,12 +39,12 @@ export interface AnthropicThreadManagerConfig {
|
|
|
34
39
|
/** Prepared payload ready to send to the Anthropic API */
|
|
35
40
|
export interface AnthropicInvocationPayload {
|
|
36
41
|
messages: Anthropic.Messages.MessageParam[];
|
|
37
|
-
system?: string;
|
|
42
|
+
system?: string | Anthropic.Messages.TextBlockParam[];
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/** Thread manager with Anthropic MessageParam convenience helpers */
|
|
41
46
|
export interface AnthropicThreadManager
|
|
42
|
-
extends ProviderThreadManager<StoredMessage, AnthropicContent> {
|
|
47
|
+
extends ProviderThreadManager<StoredMessage, AnthropicContent, JsonValue, AnthropicSystemContent> {
|
|
43
48
|
appendAssistantMessage(
|
|
44
49
|
id: string,
|
|
45
50
|
content: Anthropic.Messages.ContentBlock[],
|
|
@@ -120,11 +125,19 @@ export function createAnthropicThreadManager(
|
|
|
120
125
|
}]);
|
|
121
126
|
},
|
|
122
127
|
|
|
123
|
-
async appendSystemMessage(
|
|
128
|
+
async appendSystemMessage(
|
|
129
|
+
id: string,
|
|
130
|
+
content: AnthropicSystemContent,
|
|
131
|
+
): Promise<void> {
|
|
124
132
|
await base.initialize();
|
|
125
133
|
await base.append([{
|
|
126
134
|
id,
|
|
127
|
-
|
|
135
|
+
// Stored under a user-role placeholder to satisfy the MessageParam
|
|
136
|
+
// shape; the `isSystem` flag steers extraction in prepareForInvocation.
|
|
137
|
+
message: {
|
|
138
|
+
role: "user",
|
|
139
|
+
content: content as Anthropic.Messages.MessageParam["content"],
|
|
140
|
+
},
|
|
128
141
|
isSystem: true,
|
|
129
142
|
}]);
|
|
130
143
|
},
|
|
@@ -174,15 +187,14 @@ export function createAnthropicThreadManager(
|
|
|
174
187
|
? stored.map((msg, i) => onPrepareMessage(msg, i, stored))
|
|
175
188
|
: stored;
|
|
176
189
|
|
|
177
|
-
let system: string | undefined;
|
|
190
|
+
let system: string | Anthropic.Messages.TextBlockParam[] | undefined;
|
|
178
191
|
const conversationMessages: Anthropic.Messages.MessageParam[] = [];
|
|
179
192
|
|
|
180
193
|
for (const item of mapped) {
|
|
181
194
|
if (item.isSystem) {
|
|
182
|
-
system =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
: undefined;
|
|
195
|
+
system = item.message.content as
|
|
196
|
+
| string
|
|
197
|
+
| Anthropic.Messages.TextBlockParam[];
|
|
186
198
|
} else {
|
|
187
199
|
conversationMessages.push(item.message);
|
|
188
200
|
}
|
|
@@ -15,6 +15,7 @@ import type { ModelInvoker } from "../../../lib/model";
|
|
|
15
15
|
import {
|
|
16
16
|
createGoogleGenAIThreadManager,
|
|
17
17
|
type GoogleGenAIContent,
|
|
18
|
+
type GoogleGenAISystemContent,
|
|
18
19
|
type GoogleGenAIThreadManagerHooks,
|
|
19
20
|
} from "./thread-manager";
|
|
20
21
|
import { createGoogleGenAIModelInvoker } from "./model-invoker";
|
|
@@ -169,7 +170,7 @@ export function createGoogleGenAIAdapter(
|
|
|
169
170
|
async appendSystemMessage(
|
|
170
171
|
threadId: string,
|
|
171
172
|
id: string,
|
|
172
|
-
content:
|
|
173
|
+
content: GoogleGenAISystemContent,
|
|
173
174
|
threadKey?: string
|
|
174
175
|
): Promise<void> {
|
|
175
176
|
const thread = createGoogleGenAIThreadManager({
|
|
@@ -55,7 +55,7 @@ describe("Google GenAI thread manager hooks", () => {
|
|
|
55
55
|
|
|
56
56
|
expect(hook).toHaveBeenCalledTimes(3);
|
|
57
57
|
expect(hook).toHaveBeenCalledWith(systemContent, 0, [systemContent, userContent, modelContent]);
|
|
58
|
-
expect(systemInstruction).
|
|
58
|
+
expect(systemInstruction).toEqual([{ text: "You are helpful." }]);
|
|
59
59
|
expect(contents[0]?.parts?.[0]?.text).toBe("[modified] Hello");
|
|
60
60
|
expect(contents[1]?.parts?.[0]?.text).toBe("[modified] Hi there!");
|
|
61
61
|
});
|
|
@@ -11,6 +11,9 @@ import type { GoogleGenAIToolResponse } from "./activities";
|
|
|
11
11
|
/** SDK-native content type for Google GenAI human messages */
|
|
12
12
|
export type GoogleGenAIContent = string | Part[];
|
|
13
13
|
|
|
14
|
+
/** SDK-native content type for Google GenAI system instructions */
|
|
15
|
+
export type GoogleGenAISystemContent = string | Part[];
|
|
16
|
+
|
|
14
17
|
/** A Content with a unique ID for idempotent Redis storage */
|
|
15
18
|
export interface StoredContent {
|
|
16
19
|
id: string;
|
|
@@ -30,12 +33,12 @@ export interface GoogleGenAIThreadManagerConfig {
|
|
|
30
33
|
/** Prepared payload ready to send to the Google GenAI API */
|
|
31
34
|
export interface GoogleGenAIInvocationPayload {
|
|
32
35
|
contents: Content[];
|
|
33
|
-
systemInstruction?:
|
|
36
|
+
systemInstruction?: Part[];
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/** Thread manager with Google GenAI Content convenience helpers */
|
|
37
40
|
export interface GoogleGenAIThreadManager
|
|
38
|
-
extends ProviderThreadManager<StoredContent, GoogleGenAIContent, GoogleGenAIToolResponse> {
|
|
41
|
+
extends ProviderThreadManager<StoredContent, GoogleGenAIContent, GoogleGenAIToolResponse, GoogleGenAISystemContent> {
|
|
39
42
|
appendModelContent(id: string, parts: Part[]): Promise<void>;
|
|
40
43
|
prepareForInvocation(): Promise<GoogleGenAIInvocationPayload>;
|
|
41
44
|
}
|
|
@@ -106,11 +109,16 @@ export function createGoogleGenAIThreadManager(
|
|
|
106
109
|
}]);
|
|
107
110
|
},
|
|
108
111
|
|
|
109
|
-
async appendSystemMessage(
|
|
112
|
+
async appendSystemMessage(
|
|
113
|
+
id: string,
|
|
114
|
+
content: GoogleGenAISystemContent,
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
const parts: Part[] =
|
|
117
|
+
typeof content === "string" ? [{ text: content }] : content;
|
|
110
118
|
await base.initialize();
|
|
111
119
|
await base.append([{
|
|
112
120
|
id,
|
|
113
|
-
content: { role: "system", parts
|
|
121
|
+
content: { role: "system", parts },
|
|
114
122
|
}]);
|
|
115
123
|
},
|
|
116
124
|
|
|
@@ -150,12 +158,12 @@ export function createGoogleGenAIThreadManager(
|
|
|
150
158
|
? stored.map((msg, i) => onPrepareMessage(msg, i, stored))
|
|
151
159
|
: stored;
|
|
152
160
|
|
|
153
|
-
let systemInstruction:
|
|
161
|
+
let systemInstruction: Part[] | undefined;
|
|
154
162
|
const conversationContents: Content[] = [];
|
|
155
163
|
|
|
156
164
|
for (const item of mapped) {
|
|
157
165
|
if (item.content.role === "system") {
|
|
158
|
-
systemInstruction = item.content.parts
|
|
166
|
+
systemInstruction = item.content.parts ?? [];
|
|
159
167
|
} else {
|
|
160
168
|
conversationContents.push(item.content);
|
|
161
169
|
}
|
|
@@ -166,7 +174,7 @@ export function createGoogleGenAIThreadManager(
|
|
|
166
174
|
contents: onPreparedMessage
|
|
167
175
|
? contents.map((msg, i) => onPreparedMessage(msg, i, contents))
|
|
168
176
|
: contents,
|
|
169
|
-
...(systemInstruction ? { systemInstruction } : {}),
|
|
177
|
+
...(systemInstruction && systemInstruction.length > 0 ? { systemInstruction } : {}),
|
|
170
178
|
};
|
|
171
179
|
},
|
|
172
180
|
};
|
|
@@ -17,6 +17,7 @@ import type { BaseChatModel } from "@langchain/core/language_models/chat_models"
|
|
|
17
17
|
import {
|
|
18
18
|
createLangChainThreadManager,
|
|
19
19
|
type LangChainContent,
|
|
20
|
+
type LangChainSystemContent,
|
|
20
21
|
type LangChainThreadManagerHooks,
|
|
21
22
|
} from "./thread-manager";
|
|
22
23
|
import { createLangChainModelInvoker } from "./model-invoker";
|
|
@@ -135,7 +136,7 @@ export function createLangChainAdapter(
|
|
|
135
136
|
async appendSystemMessage(
|
|
136
137
|
threadId: string,
|
|
137
138
|
id: string,
|
|
138
|
-
content:
|
|
139
|
+
content: LangChainSystemContent,
|
|
139
140
|
threadKey?: string,
|
|
140
141
|
): Promise<void> {
|
|
141
142
|
const thread = createLangChainThreadManager({ redis, threadId, key: threadKey });
|
|
@@ -20,6 +20,9 @@ import type {
|
|
|
20
20
|
/** SDK-native content type for LangChain human messages */
|
|
21
21
|
export type LangChainContent = string | MessageContent;
|
|
22
22
|
|
|
23
|
+
/** SDK-native content type for LangChain system messages */
|
|
24
|
+
export type LangChainSystemContent = string | MessageContent;
|
|
25
|
+
|
|
23
26
|
export type LangChainThreadManagerHooks = ThreadManagerHooks<StoredMessage, BaseMessage>;
|
|
24
27
|
|
|
25
28
|
export interface LangChainThreadManagerConfig {
|
|
@@ -37,7 +40,7 @@ export interface LangChainInvocationPayload {
|
|
|
37
40
|
|
|
38
41
|
/** Thread manager with LangChain StoredMessage convenience helpers */
|
|
39
42
|
export interface LangChainThreadManager
|
|
40
|
-
extends ProviderThreadManager<StoredMessage, LangChainContent> {
|
|
43
|
+
extends ProviderThreadManager<StoredMessage, LangChainContent, JsonValue, LangChainSystemContent> {
|
|
41
44
|
appendAIMessage(id: string, content: string | MessageContent): Promise<void>;
|
|
42
45
|
prepareForInvocation(): Promise<LangChainInvocationPayload>;
|
|
43
46
|
}
|
|
@@ -81,10 +84,16 @@ export function createLangChainThreadManager(
|
|
|
81
84
|
]);
|
|
82
85
|
},
|
|
83
86
|
|
|
84
|
-
async appendSystemMessage(
|
|
87
|
+
async appendSystemMessage(
|
|
88
|
+
id: string,
|
|
89
|
+
content: LangChainSystemContent,
|
|
90
|
+
): Promise<void> {
|
|
85
91
|
await base.initialize();
|
|
86
92
|
await base.append([
|
|
87
|
-
new SystemMessage({
|
|
93
|
+
new SystemMessage({
|
|
94
|
+
id,
|
|
95
|
+
content: content as MessageContent,
|
|
96
|
+
}).toDict(),
|
|
88
97
|
]);
|
|
89
98
|
},
|
|
90
99
|
|
package/src/lib/lifecycle.ts
CHANGED
|
@@ -25,8 +25,9 @@ export type ThreadInit =
|
|
|
25
25
|
* - `"new"` — create a fresh sandbox. Optionally pass `ctx` to
|
|
26
26
|
* have the {@link SandboxManager}'s resolver produce creation options
|
|
27
27
|
* (e.g. initial files) from workflow arguments.
|
|
28
|
-
* - `"continue"` —
|
|
29
|
-
*
|
|
28
|
+
* - `"continue"` — take ownership of an existing sandbox (paused or running).
|
|
29
|
+
* Paused sandboxes are automatically resumed. The shutdown policy applies
|
|
30
|
+
* on exit.
|
|
30
31
|
* - `"fork"` — fork from an existing (or paused) sandbox; a new sandbox is
|
|
31
32
|
* created and owned by this session.
|
|
32
33
|
* - `"inherit"` — use a sandbox owned by someone else (e.g. a parent agent).
|
|
@@ -56,7 +57,10 @@ export type SandboxShutdown = "destroy" | "pause" | "keep";
|
|
|
56
57
|
* Includes all base {@link SandboxShutdown} values plus:
|
|
57
58
|
* - `"pause-until-parent-close"` — pause the sandbox on exit, then wait for
|
|
58
59
|
* the parent workflow to signal when to destroy it.
|
|
60
|
+
* - `"keep-until-parent-close"` — leave the sandbox running on exit, then
|
|
61
|
+
* wait for the parent workflow to signal when to destroy it.
|
|
59
62
|
*/
|
|
60
63
|
export type SubagentSandboxShutdown =
|
|
61
64
|
| SandboxShutdown
|
|
62
|
-
| "pause-until-parent-close"
|
|
65
|
+
| "pause-until-parent-close"
|
|
66
|
+
| "keep-until-parent-close";
|
|
@@ -166,6 +166,10 @@ export class SandboxManager<
|
|
|
166
166
|
await this.provider.pause(id, ttlSeconds);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
async resume(id: string): Promise<void> {
|
|
170
|
+
await this.provider.resume(id);
|
|
171
|
+
}
|
|
172
|
+
|
|
169
173
|
async snapshot(id: string): Promise<SandboxSnapshot> {
|
|
170
174
|
return this.provider.snapshot(id);
|
|
171
175
|
}
|
|
@@ -222,6 +226,9 @@ export class SandboxManager<
|
|
|
222
226
|
): Promise<void> => {
|
|
223
227
|
await this.pause(sandboxId, ttlSeconds);
|
|
224
228
|
},
|
|
229
|
+
resumeSandbox: async (sandboxId: string): Promise<void> => {
|
|
230
|
+
await this.resume(sandboxId);
|
|
231
|
+
},
|
|
225
232
|
snapshotSandbox: async (sandboxId: string): Promise<SandboxSnapshot> => {
|
|
226
233
|
return this.snapshot(sandboxId);
|
|
227
234
|
},
|
package/src/lib/sandbox/types.ts
CHANGED
|
@@ -131,6 +131,8 @@ export interface SandboxProvider<
|
|
|
131
131
|
get(sandboxId: string): Promise<TSandbox>;
|
|
132
132
|
destroy(sandboxId: string): Promise<void>;
|
|
133
133
|
pause(sandboxId: string, ttlSeconds?: number): Promise<void>;
|
|
134
|
+
/** Resume a paused sandbox. No-op if already running. */
|
|
135
|
+
resume(sandboxId: string): Promise<void>;
|
|
134
136
|
snapshot(sandboxId: string): Promise<SandboxSnapshot>;
|
|
135
137
|
restore(snapshot: SandboxSnapshot): Promise<Sandbox>;
|
|
136
138
|
fork(sandboxId: string): Promise<Sandbox>;
|
|
@@ -150,6 +152,8 @@ export interface SandboxOps<
|
|
|
150
152
|
): Promise<{ sandboxId: string } | null>;
|
|
151
153
|
destroySandbox(sandboxId: string): Promise<void>;
|
|
152
154
|
pauseSandbox(sandboxId: string): Promise<void>;
|
|
155
|
+
/** Resume a paused sandbox. No-op if already running. */
|
|
156
|
+
resumeSandbox(sandboxId: string): Promise<void>;
|
|
153
157
|
snapshotSandbox(sandboxId: string): Promise<SandboxSnapshot>;
|
|
154
158
|
forkSandbox(sandboxId: string): Promise<string>;
|
|
155
159
|
}
|
|
@@ -434,6 +434,7 @@ describe("createSession edge cases", () => {
|
|
|
434
434
|
}),
|
|
435
435
|
forkSandbox: async () => "forked-sandbox-id",
|
|
436
436
|
pauseSandbox: async () => {},
|
|
437
|
+
resumeSandbox: async () => {},
|
|
437
438
|
};
|
|
438
439
|
|
|
439
440
|
const session = await createSession({
|
|
@@ -476,6 +477,7 @@ describe("createSession edge cases", () => {
|
|
|
476
477
|
}),
|
|
477
478
|
forkSandbox: async () => "forked-sandbox-id",
|
|
478
479
|
pauseSandbox: async () => {},
|
|
480
|
+
resumeSandbox: async () => {},
|
|
479
481
|
};
|
|
480
482
|
|
|
481
483
|
const session = await createSession({
|
|
@@ -939,6 +941,7 @@ describe("createSession edge cases", () => {
|
|
|
939
941
|
snapshotSandbox: snapshotSpy,
|
|
940
942
|
forkSandbox: async () => "forked-sandbox-id",
|
|
941
943
|
pauseSandbox: async () => {},
|
|
944
|
+
resumeSandbox: async () => {},
|
|
942
945
|
};
|
|
943
946
|
|
|
944
947
|
const session = await createSession({
|
|
@@ -1029,6 +1032,7 @@ describe("createSession edge cases", () => {
|
|
|
1029
1032
|
createSandbox: async () => ({ sandboxId: "sb-created" }),
|
|
1030
1033
|
destroySandbox: async () => {},
|
|
1031
1034
|
pauseSandbox: async () => {},
|
|
1035
|
+
resumeSandbox: async () => {},
|
|
1032
1036
|
snapshotSandbox: async () => ({
|
|
1033
1037
|
sandboxId: "sb-1",
|
|
1034
1038
|
providerId: "test",
|
|
@@ -1062,6 +1066,7 @@ describe("createSession edge cases", () => {
|
|
|
1062
1066
|
createSandbox: async () => ({ sandboxId: "sb" }),
|
|
1063
1067
|
destroySandbox: async () => {},
|
|
1064
1068
|
pauseSandbox: async () => {},
|
|
1069
|
+
resumeSandbox: async () => {},
|
|
1065
1070
|
snapshotSandbox: async () => ({
|
|
1066
1071
|
sandboxId: "sb",
|
|
1067
1072
|
providerId: "test",
|
|
@@ -1106,6 +1111,7 @@ describe("createSession edge cases", () => {
|
|
|
1106
1111
|
pauseSandbox: async (id: string) => {
|
|
1107
1112
|
sandboxLog.push(`pause:${id}`);
|
|
1108
1113
|
},
|
|
1114
|
+
resumeSandbox: async () => {},
|
|
1109
1115
|
snapshotSandbox: async () => ({
|
|
1110
1116
|
sandboxId: "sb-1",
|
|
1111
1117
|
providerId: "test",
|
|
@@ -1149,6 +1155,7 @@ describe("createSession edge cases", () => {
|
|
|
1149
1155
|
sandboxLog.push(`destroy:${id}`);
|
|
1150
1156
|
},
|
|
1151
1157
|
pauseSandbox: async () => {},
|
|
1158
|
+
resumeSandbox: async () => {},
|
|
1152
1159
|
snapshotSandbox: async () => ({
|
|
1153
1160
|
sandboxId: "sb-1",
|
|
1154
1161
|
providerId: "test",
|
|
@@ -1195,6 +1202,7 @@ describe("createSession edge cases", () => {
|
|
|
1195
1202
|
sandboxLog.push(`destroy:${id}`);
|
|
1196
1203
|
},
|
|
1197
1204
|
pauseSandbox: async () => {},
|
|
1205
|
+
resumeSandbox: async () => {},
|
|
1198
1206
|
snapshotSandbox: async () => ({
|
|
1199
1207
|
sandboxId: "sb-1",
|
|
1200
1208
|
providerId: "test",
|
|
@@ -1237,6 +1245,7 @@ describe("createSession edge cases", () => {
|
|
|
1237
1245
|
pauseSandbox: async (id: string) => {
|
|
1238
1246
|
sandboxLog.push(`pause:${id}`);
|
|
1239
1247
|
},
|
|
1248
|
+
resumeSandbox: async () => {},
|
|
1240
1249
|
snapshotSandbox: async () => ({
|
|
1241
1250
|
sandboxId: "sb-1",
|
|
1242
1251
|
providerId: "test",
|
|
@@ -1280,6 +1289,7 @@ describe("createSession edge cases", () => {
|
|
|
1280
1289
|
pauseSandbox: async (id: string) => {
|
|
1281
1290
|
sandboxLog.push(`pause:${id}`);
|
|
1282
1291
|
},
|
|
1292
|
+
resumeSandbox: async () => {},
|
|
1283
1293
|
snapshotSandbox: async () => ({
|
|
1284
1294
|
sandboxId: "sb-1",
|
|
1285
1295
|
providerId: "test",
|
|
@@ -1395,6 +1405,7 @@ describe("createSession edge cases", () => {
|
|
|
1395
1405
|
pauseSandbox: async (id: string) => {
|
|
1396
1406
|
sandboxLog.push(`pause:${id}`);
|
|
1397
1407
|
},
|
|
1408
|
+
resumeSandbox: async () => {},
|
|
1398
1409
|
snapshotSandbox: async () => ({
|
|
1399
1410
|
sandboxId: "sb-1",
|
|
1400
1411
|
providerId: "test",
|
|
@@ -1427,4 +1438,187 @@ describe("createSession edge cases", () => {
|
|
|
1427
1438
|
expect(sandboxLog).toContain("pause:sb-err");
|
|
1428
1439
|
expect(sandboxLog).not.toContain("destroy:sb-err");
|
|
1429
1440
|
});
|
|
1441
|
+
|
|
1442
|
+
// --- sandboxShutdown: "keep-until-parent-close" ---
|
|
1443
|
+
|
|
1444
|
+
it("keeps sandbox running on exit when sandboxShutdown is keep-until-parent-close", async () => {
|
|
1445
|
+
const { ops } = createMockThreadOps();
|
|
1446
|
+
const sandboxLog: string[] = [];
|
|
1447
|
+
|
|
1448
|
+
const sandboxOps: SandboxOps = {
|
|
1449
|
+
createSandbox: async () => ({ sandboxId: "sb-keep-parent" }),
|
|
1450
|
+
destroySandbox: async (id: string) => {
|
|
1451
|
+
sandboxLog.push(`destroy:${id}`);
|
|
1452
|
+
},
|
|
1453
|
+
pauseSandbox: async (id: string) => {
|
|
1454
|
+
sandboxLog.push(`pause:${id}`);
|
|
1455
|
+
},
|
|
1456
|
+
resumeSandbox: async () => {},
|
|
1457
|
+
snapshotSandbox: async () => ({
|
|
1458
|
+
sandboxId: "sb-1",
|
|
1459
|
+
providerId: "test",
|
|
1460
|
+
data: null,
|
|
1461
|
+
createdAt: new Date().toISOString(),
|
|
1462
|
+
}),
|
|
1463
|
+
forkSandbox: async () => "forked-sb",
|
|
1464
|
+
};
|
|
1465
|
+
|
|
1466
|
+
const session = await createSession({
|
|
1467
|
+
agentName: "TestAgent",
|
|
1468
|
+
thread: { mode: "new", threadId: "thread-1" },
|
|
1469
|
+
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
1470
|
+
threadOps: ops,
|
|
1471
|
+
buildContextMessage: () => "go",
|
|
1472
|
+
sandboxOps,
|
|
1473
|
+
sandboxShutdown: "keep-until-parent-close",
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1476
|
+
const stateManager = createAgentStateManager({
|
|
1477
|
+
initialState: { systemPrompt: "test" },
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
await session.runSession({ stateManager });
|
|
1481
|
+
|
|
1482
|
+
expect(sandboxLog).not.toContain("pause:sb-keep-parent");
|
|
1483
|
+
expect(sandboxLog).not.toContain("destroy:sb-keep-parent");
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
it("keeps sandbox running on error when sandboxShutdown is keep-until-parent-close", async () => {
|
|
1487
|
+
const { ops } = createMockThreadOps();
|
|
1488
|
+
const sandboxLog: string[] = [];
|
|
1489
|
+
|
|
1490
|
+
const sandboxOps: SandboxOps = {
|
|
1491
|
+
createSandbox: async () => ({ sandboxId: "sb-keep-err" }),
|
|
1492
|
+
destroySandbox: async (id: string) => {
|
|
1493
|
+
sandboxLog.push(`destroy:${id}`);
|
|
1494
|
+
},
|
|
1495
|
+
pauseSandbox: async (id: string) => {
|
|
1496
|
+
sandboxLog.push(`pause:${id}`);
|
|
1497
|
+
},
|
|
1498
|
+
resumeSandbox: async () => {},
|
|
1499
|
+
snapshotSandbox: async () => ({
|
|
1500
|
+
sandboxId: "sb-1",
|
|
1501
|
+
providerId: "test",
|
|
1502
|
+
data: null,
|
|
1503
|
+
createdAt: new Date().toISOString(),
|
|
1504
|
+
}),
|
|
1505
|
+
forkSandbox: async () => "forked-sb",
|
|
1506
|
+
};
|
|
1507
|
+
|
|
1508
|
+
const session = await createSession({
|
|
1509
|
+
agentName: "TestAgent",
|
|
1510
|
+
thread: { mode: "new", threadId: "thread-1" },
|
|
1511
|
+
runAgent: async () => {
|
|
1512
|
+
throw new Error("crash");
|
|
1513
|
+
},
|
|
1514
|
+
threadOps: ops,
|
|
1515
|
+
buildContextMessage: () => "go",
|
|
1516
|
+
sandboxOps,
|
|
1517
|
+
sandboxShutdown: "keep-until-parent-close",
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
const stateManager = createAgentStateManager({
|
|
1521
|
+
initialState: { systemPrompt: "test" },
|
|
1522
|
+
});
|
|
1523
|
+
|
|
1524
|
+
await expect(session.runSession({ stateManager })).rejects.toThrow(
|
|
1525
|
+
"crash"
|
|
1526
|
+
);
|
|
1527
|
+
|
|
1528
|
+
expect(sandboxLog).not.toContain("pause:sb-keep-err");
|
|
1529
|
+
expect(sandboxLog).not.toContain("destroy:sb-keep-err");
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
// --- sandbox continue calls resumeSandbox only for pause-until-parent-close ---
|
|
1533
|
+
|
|
1534
|
+
it("calls resumeSandbox when sandbox mode is continue with pause-until-parent-close", async () => {
|
|
1535
|
+
const { ops } = createMockThreadOps();
|
|
1536
|
+
const sandboxLog: string[] = [];
|
|
1537
|
+
|
|
1538
|
+
const sandboxOps: SandboxOps = {
|
|
1539
|
+
createSandbox: async () => ({ sandboxId: "new-sb" }),
|
|
1540
|
+
destroySandbox: async (id: string) => {
|
|
1541
|
+
sandboxLog.push(`destroy:${id}`);
|
|
1542
|
+
},
|
|
1543
|
+
pauseSandbox: async (id: string) => {
|
|
1544
|
+
sandboxLog.push(`pause:${id}`);
|
|
1545
|
+
},
|
|
1546
|
+
resumeSandbox: async (id: string) => {
|
|
1547
|
+
sandboxLog.push(`resume:${id}`);
|
|
1548
|
+
},
|
|
1549
|
+
snapshotSandbox: async () => ({
|
|
1550
|
+
sandboxId: "sb-1",
|
|
1551
|
+
providerId: "test",
|
|
1552
|
+
data: null,
|
|
1553
|
+
createdAt: new Date().toISOString(),
|
|
1554
|
+
}),
|
|
1555
|
+
forkSandbox: async () => "forked-sb",
|
|
1556
|
+
};
|
|
1557
|
+
|
|
1558
|
+
const session = await createSession({
|
|
1559
|
+
agentName: "TestAgent",
|
|
1560
|
+
thread: { mode: "new", threadId: "thread-1" },
|
|
1561
|
+
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
1562
|
+
threadOps: ops,
|
|
1563
|
+
buildContextMessage: () => "go",
|
|
1564
|
+
sandboxOps,
|
|
1565
|
+
sandbox: { mode: "continue", sandboxId: "paused-sb" },
|
|
1566
|
+
sandboxShutdown: "pause-until-parent-close",
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
const stateManager = createAgentStateManager({
|
|
1570
|
+
initialState: { systemPrompt: "test" },
|
|
1571
|
+
});
|
|
1572
|
+
|
|
1573
|
+
await session.runSession({ stateManager });
|
|
1574
|
+
|
|
1575
|
+
expect(sandboxLog).toContain("resume:paused-sb");
|
|
1576
|
+
expect(sandboxLog).toContain("pause:paused-sb");
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
it("skips resumeSandbox when sandbox mode is continue with keep-until-parent-close", async () => {
|
|
1580
|
+
const { ops } = createMockThreadOps();
|
|
1581
|
+
const sandboxLog: string[] = [];
|
|
1582
|
+
|
|
1583
|
+
const sandboxOps: SandboxOps = {
|
|
1584
|
+
createSandbox: async () => ({ sandboxId: "new-sb" }),
|
|
1585
|
+
destroySandbox: async (id: string) => {
|
|
1586
|
+
sandboxLog.push(`destroy:${id}`);
|
|
1587
|
+
},
|
|
1588
|
+
pauseSandbox: async (id: string) => {
|
|
1589
|
+
sandboxLog.push(`pause:${id}`);
|
|
1590
|
+
},
|
|
1591
|
+
resumeSandbox: async (id: string) => {
|
|
1592
|
+
sandboxLog.push(`resume:${id}`);
|
|
1593
|
+
},
|
|
1594
|
+
snapshotSandbox: async () => ({
|
|
1595
|
+
sandboxId: "sb-1",
|
|
1596
|
+
providerId: "test",
|
|
1597
|
+
data: null,
|
|
1598
|
+
createdAt: new Date().toISOString(),
|
|
1599
|
+
}),
|
|
1600
|
+
forkSandbox: async () => "forked-sb",
|
|
1601
|
+
};
|
|
1602
|
+
|
|
1603
|
+
const session = await createSession({
|
|
1604
|
+
agentName: "TestAgent",
|
|
1605
|
+
thread: { mode: "new", threadId: "thread-1" },
|
|
1606
|
+
runAgent: createScriptedRunAgent([{ message: "done", toolCalls: [] }]),
|
|
1607
|
+
threadOps: ops,
|
|
1608
|
+
buildContextMessage: () => "go",
|
|
1609
|
+
sandboxOps,
|
|
1610
|
+
sandbox: { mode: "continue", sandboxId: "kept-sb" },
|
|
1611
|
+
sandboxShutdown: "keep-until-parent-close",
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
const stateManager = createAgentStateManager({
|
|
1615
|
+
initialState: { systemPrompt: "test" },
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
await session.runSession({ stateManager });
|
|
1619
|
+
|
|
1620
|
+
expect(sandboxLog).not.toContain("resume:kept-sb");
|
|
1621
|
+
expect(sandboxLog).not.toContain("pause:kept-sb");
|
|
1622
|
+
expect(sandboxLog).not.toContain("destroy:kept-sb");
|
|
1623
|
+
});
|
|
1430
1624
|
});
|