@superdurable/dex-agents 0.0.0-stage → 0.2.0
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/LICENSE +105 -0
- package/README.md +1981 -2
- package/dist/src/agent-built-in-tools.d.ts +81 -0
- package/dist/src/agent-built-in-tools.js +246 -0
- package/dist/src/agent-built-in-tools.js.map +1 -0
- package/dist/src/agent-context-compaction.d.ts +108 -0
- package/dist/src/agent-context-compaction.js +39 -0
- package/dist/src/agent-context-compaction.js.map +1 -0
- package/dist/src/agent-event-writer.d.ts +26 -0
- package/dist/src/agent-event-writer.js +110 -0
- package/dist/src/agent-event-writer.js.map +1 -0
- package/dist/src/agent-framework-runner.d.ts +808 -0
- package/dist/src/agent-framework-runner.js +82 -0
- package/dist/src/agent-framework-runner.js.map +1 -0
- package/dist/src/agent-plans.d.ts +48 -0
- package/dist/src/agent-plans.js +86 -0
- package/dist/src/agent-plans.js.map +1 -0
- package/dist/src/agent-session-cancellation-steps.d.ts +28 -0
- package/dist/src/agent-session-cancellation-steps.js +154 -0
- package/dist/src/agent-session-cancellation-steps.js.map +1 -0
- package/dist/src/agent-session-codecs.d.ts +105 -0
- package/dist/src/agent-session-codecs.js +689 -0
- package/dist/src/agent-session-codecs.js.map +1 -0
- package/dist/src/agent-session-flow.d.ts +416 -0
- package/dist/src/agent-session-flow.js +623 -0
- package/dist/src/agent-session-flow.js.map +1 -0
- package/dist/src/agent-session-persistence.d.ts +133 -0
- package/dist/src/agent-session-persistence.js +74 -0
- package/dist/src/agent-session-persistence.js.map +1 -0
- package/dist/src/agent-session-settings.d.ts +58 -0
- package/dist/src/agent-session-settings.js +81 -0
- package/dist/src/agent-session-settings.js.map +1 -0
- package/dist/src/agent-session-step-options.d.ts +27 -0
- package/dist/src/agent-session-step-options.js +39 -0
- package/dist/src/agent-session-step-options.js.map +1 -0
- package/dist/src/agent-session-step-types.d.ts +20 -0
- package/dist/src/agent-session-step-types.js +28 -0
- package/dist/src/agent-session-step-types.js.map +1 -0
- package/dist/src/agent-session-steps.d.ts +179 -0
- package/dist/src/agent-session-steps.js +1272 -0
- package/dist/src/agent-session-steps.js.map +1 -0
- package/dist/src/agent-session-tracer.d.ts +61 -0
- package/dist/src/agent-session-tracer.js +608 -0
- package/dist/src/agent-session-tracer.js.map +1 -0
- package/dist/src/agent-session-transitions.d.ts +224 -0
- package/dist/src/agent-session-transitions.js +850 -0
- package/dist/src/agent-session-transitions.js.map +1 -0
- package/dist/src/agent-session-values.d.ts +998 -0
- package/dist/src/agent-session-values.js +162 -0
- package/dist/src/agent-session-values.js.map +1 -0
- package/dist/src/agent-session-wait-steps.d.ts +53 -0
- package/dist/src/agent-session-wait-steps.js +389 -0
- package/dist/src/agent-session-wait-steps.js.map +1 -0
- package/dist/src/agent-tracing-options.d.ts +107 -0
- package/dist/src/agent-tracing-options.js +20 -0
- package/dist/src/agent-tracing-options.js.map +1 -0
- package/dist/src/ai-sdk/ai-sdk-model-client.d.ts +40 -0
- package/dist/src/ai-sdk/ai-sdk-model-client.js +235 -0
- package/dist/src/ai-sdk/ai-sdk-model-client.js.map +1 -0
- package/dist/src/ai-sdk/index.d.ts +1 -0
- package/dist/src/ai-sdk/index.js +9 -0
- package/dist/src/ai-sdk/index.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +17 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/index.d.ts +3 -0
- package/dist/src/mcp/index.js +10 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/mcp/mcp-resource-and-prompt-tools.d.ts +29 -0
- package/dist/src/mcp/mcp-resource-and-prompt-tools.js +185 -0
- package/dist/src/mcp/mcp-resource-and-prompt-tools.js.map +1 -0
- package/dist/src/mcp/mcp-server-config.d.ts +86 -0
- package/dist/src/mcp/mcp-server-config.js +38 -0
- package/dist/src/mcp/mcp-server-config.js.map +1 -0
- package/dist/src/mcp/mcp-tool-source.d.ts +470 -0
- package/dist/src/mcp/mcp-tool-source.js +1204 -0
- package/dist/src/mcp/mcp-tool-source.js.map +1 -0
- package/dist/src/stepwise/agent-definition.d.ts +158 -0
- package/dist/src/stepwise/agent-definition.js +9 -0
- package/dist/src/stepwise/agent-definition.js.map +1 -0
- package/dist/src/stepwise/function-tool.d.ts +130 -0
- package/dist/src/stepwise/function-tool.js +75 -0
- package/dist/src/stepwise/function-tool.js.map +1 -0
- package/dist/src/stepwise/index.d.ts +7 -0
- package/dist/src/stepwise/index.js +12 -0
- package/dist/src/stepwise/index.js.map +1 -0
- package/dist/src/stepwise/json-schema-validation.d.ts +9 -0
- package/dist/src/stepwise/json-schema-validation.js +141 -0
- package/dist/src/stepwise/json-schema-validation.js.map +1 -0
- package/dist/src/stepwise/model-client.d.ts +194 -0
- package/dist/src/stepwise/model-client.js +9 -0
- package/dist/src/stepwise/model-client.js.map +1 -0
- package/dist/src/stepwise/scripted-model-client.d.ts +99 -0
- package/dist/src/stepwise/scripted-model-client.js +156 -0
- package/dist/src/stepwise/scripted-model-client.js.map +1 -0
- package/dist/src/stepwise/stepwise-agent-runner.d.ts +189 -0
- package/dist/src/stepwise/stepwise-agent-runner.js +804 -0
- package/dist/src/stepwise/stepwise-agent-runner.js.map +1 -0
- package/dist/src/stepwise/stepwise-agent-session-flow.d.ts +35 -0
- package/dist/src/stepwise/stepwise-agent-session-flow.js +56 -0
- package/dist/src/stepwise/stepwise-agent-session-flow.js.map +1 -0
- package/dist/src/stepwise/stepwise-input-items.d.ts +12 -0
- package/dist/src/stepwise/stepwise-input-items.js +172 -0
- package/dist/src/stepwise/stepwise-input-items.js.map +1 -0
- package/dist/src/stepwise/tool-source.d.ts +103 -0
- package/dist/src/stepwise/tool-source.js +9 -0
- package/dist/src/stepwise/tool-source.js.map +1 -0
- package/dist/src/testing/agent-session-test-client.d.ts +205 -0
- package/dist/src/testing/agent-session-test-client.js +290 -0
- package/dist/src/testing/agent-session-test-client.js.map +1 -0
- package/dist/src/testing/agent-test-environment.d.ts +52 -0
- package/dist/src/testing/agent-test-environment.js +86 -0
- package/dist/src/testing/agent-test-environment.js.map +1 -0
- package/dist/src/testing/agent-worker-process.d.ts +91 -0
- package/dist/src/testing/agent-worker-process.js +150 -0
- package/dist/src/testing/agent-worker-process.js.map +1 -0
- package/dist/src/testing/child-process-group.d.ts +33 -0
- package/dist/src/testing/child-process-group.js +164 -0
- package/dist/src/testing/child-process-group.js.map +1 -0
- package/dist/src/testing/coordination.d.ts +99 -0
- package/dist/src/testing/coordination.js +155 -0
- package/dist/src/testing/coordination.js.map +1 -0
- package/dist/src/testing/index.d.ts +8 -0
- package/dist/src/testing/index.js +16 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/polling.d.ts +31 -0
- package/dist/src/testing/polling.js +51 -0
- package/dist/src/testing/polling.js.map +1 -0
- package/dist/src/testing/private-dex-engine.d.ts +101 -0
- package/dist/src/testing/private-dex-engine.js +223 -0
- package/dist/src/testing/private-dex-engine.js.map +1 -0
- package/dist/src/testing/scripted-model-client.d.ts +1 -0
- package/dist/src/testing/scripted-model-client.js +10 -0
- package/dist/src/testing/scripted-model-client.js.map +1 -0
- package/dist/src/testing/step-history.d.ts +40 -0
- package/dist/src/testing/step-history.js +59 -0
- package/dist/src/testing/step-history.js.map +1 -0
- package/dist/src/ui-stream/agent-event-json.d.ts +30 -0
- package/dist/src/ui-stream/agent-event-json.js +64 -0
- package/dist/src/ui-stream/agent-event-json.js.map +1 -0
- package/dist/src/ui-stream/agent-event-sse.d.ts +154 -0
- package/dist/src/ui-stream/agent-event-sse.js +159 -0
- package/dist/src/ui-stream/agent-event-sse.js.map +1 -0
- package/dist/src/ui-stream/agent-event-stream-positions.d.ts +42 -0
- package/dist/src/ui-stream/agent-event-stream-positions.js +153 -0
- package/dist/src/ui-stream/agent-event-stream-positions.js.map +1 -0
- package/dist/src/ui-stream/agent-session-record-reader.d.ts +72 -0
- package/dist/src/ui-stream/agent-session-record-reader.js +241 -0
- package/dist/src/ui-stream/agent-session-record-reader.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-message-stream-responses.d.ts +219 -0
- package/dist/src/ui-stream/agent-ui-message-stream-responses.js +440 -0
- package/dist/src/ui-stream/agent-ui-message-stream-responses.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-message-stream.d.ts +138 -0
- package/dist/src/ui-stream/agent-ui-message-stream.js +681 -0
- package/dist/src/ui-stream/agent-ui-message-stream.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-message-types.d.ts +238 -0
- package/dist/src/ui-stream/agent-ui-message-types.js +57 -0
- package/dist/src/ui-stream/agent-ui-message-types.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-messages.d.ts +66 -0
- package/dist/src/ui-stream/agent-ui-messages.js +220 -0
- package/dist/src/ui-stream/agent-ui-messages.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-stream-cursor.d.ts +64 -0
- package/dist/src/ui-stream/agent-ui-stream-cursor.js +186 -0
- package/dist/src/ui-stream/agent-ui-stream-cursor.js.map +1 -0
- package/dist/src/ui-stream/index.d.ts +14 -0
- package/dist/src/ui-stream/index.js +22 -0
- package/dist/src/ui-stream/index.js.map +1 -0
- package/dist/src/ui-stream/server-sent-event-responses.d.ts +6 -0
- package/dist/src/ui-stream/server-sent-event-responses.js +34 -0
- package/dist/src/ui-stream/server-sent-event-responses.js.map +1 -0
- package/package.json +86 -4
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { type Client, type StartFlowOptions } from "@superdurable/dex";
|
|
2
|
+
import type { AgentFrameworkRunner, DeferredToolOutcome } from "../agent-framework-runner.js";
|
|
3
|
+
import type { AgentSessionFlow } from "../agent-session-flow.js";
|
|
4
|
+
import type { AgentEvent, AgentInterrupt, AgentMessagesPage, AgentSnapshot, AgentState, AgentToolCallRecord, SessionSettings } from "../agent-session-values.js";
|
|
5
|
+
/** Options of the `waitFor*` methods of {@link AgentSessionTestClient}. */
|
|
6
|
+
export interface AgentSessionWaitOptions {
|
|
7
|
+
/** Total milliseconds to wait. Defaults to 60,000. */
|
|
8
|
+
readonly timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Typed Client calls for one agent session Flow, with waits suited to integration tests.
|
|
12
|
+
*
|
|
13
|
+
* RPC methods need a running Worker. The `read*` and `waitFor*` methods use `Client.getAttributes`,
|
|
14
|
+
* which reads committed Attributes without a Worker, so they also work while a killed Worker is down.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const sessions = new AgentSessionTestClient(environment.client, flow);
|
|
19
|
+
* await sessions.startSession(sessionId, "send the report");
|
|
20
|
+
* const interrupt = await sessions.waitForPendingInterrupt(sessionId, "send_email");
|
|
21
|
+
* assert.equal(await sessions.resolveInterrupt(sessionId, interrupt.interruptId, true), true);
|
|
22
|
+
* const state = await sessions.waitForCompletedTurns(sessionId, 1);
|
|
23
|
+
* const snapshot = await sessions.getAgentSnapshot(sessionId);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class AgentSessionTestClient {
|
|
27
|
+
/** Client used for every call. */
|
|
28
|
+
readonly client: Client;
|
|
29
|
+
/** Flow whose sessions this helper drives. */
|
|
30
|
+
readonly flow: AgentSessionFlow<AgentFrameworkRunner<DeferredToolOutcome>>;
|
|
31
|
+
/**
|
|
32
|
+
* Creates the helper.
|
|
33
|
+
* @param client - Client whose Registry contains `flow`.
|
|
34
|
+
* @param flow - The agent session Flow, the same object registered in the Client's Registry.
|
|
35
|
+
*/
|
|
36
|
+
constructor(
|
|
37
|
+
/** Client used for every call. */
|
|
38
|
+
client: Client,
|
|
39
|
+
/** Flow whose sessions this helper drives. */
|
|
40
|
+
flow: AgentSessionFlow<AgentFrameworkRunner<DeferredToolOutcome>>);
|
|
41
|
+
/**
|
|
42
|
+
* Starts a session.
|
|
43
|
+
* @param sessionId - Flow ID of the new session.
|
|
44
|
+
* @param initialMessage - First user message, or `null` to start idle. Defaults to `null`.
|
|
45
|
+
* @param startFlowOptions - Start options, such as a `configOverride` with `stepDurability`.
|
|
46
|
+
* @param sessionSettings - The session's settings, or `null` to start without settings. Defaults to `null`.
|
|
47
|
+
* @returns A Promise that resolves after Dex accepted the start.
|
|
48
|
+
* @throws {TypeError | RangeError} From `validateSessionSettings`, before anything is sent.
|
|
49
|
+
*/
|
|
50
|
+
startSession(sessionId: string, initialMessage?: string | null, startFlowOptions?: StartFlowOptions, sessionSettings?: SessionSettings | null): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Calls `update_session_settings`.
|
|
53
|
+
* @param sessionId - Session Flow ID.
|
|
54
|
+
* @param sessionSettings - The complete new settings; `{}` clears them.
|
|
55
|
+
* @returns The RPC result: `false` before the session initialized or for settings over the limit.
|
|
56
|
+
*/
|
|
57
|
+
updateSessionSettings(sessionId: string, sessionSettings: SessionSettings): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Calls `send_user_message`.
|
|
60
|
+
* @param sessionId - Session Flow ID.
|
|
61
|
+
* @param text - Message text.
|
|
62
|
+
* @param planMode - Whether the message starts a planning turn. Defaults to `false`.
|
|
63
|
+
* @returns The RPC result: `false` for blank text, or for a `planMode` message when plan mode is off.
|
|
64
|
+
*/
|
|
65
|
+
sendUserMessage(sessionId: string, text: string, planMode?: boolean): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Calls `execute_plan`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
68
|
+
* @param sessionId - Session Flow ID.
|
|
69
|
+
* @param revision - Plan revision to execute.
|
|
70
|
+
* @returns The RPC result: `false` when the revision is stale, already pending, or the session is busy.
|
|
71
|
+
*/
|
|
72
|
+
executePlan(sessionId: string, revision: number): Promise<boolean>;
|
|
73
|
+
/**
|
|
74
|
+
* Calls `steer_user_message` with new text; while no turn is active, the RPC queues it.
|
|
75
|
+
* @param sessionId - Session Flow ID.
|
|
76
|
+
* @param text - Text of the steered message.
|
|
77
|
+
* @returns The RPC result: `false` for blank text or a turn that does not accept steering.
|
|
78
|
+
*/
|
|
79
|
+
steerUserMessage(sessionId: string, text: string): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* Calls `steer_user_message` with a queued message's ID, which moves that message.
|
|
82
|
+
* @param sessionId - Session Flow ID.
|
|
83
|
+
* @param messageId - {@link PendingUserMessage.messageId} of a queued message.
|
|
84
|
+
* @returns The RPC result: `false` when the message is no longer queued.
|
|
85
|
+
* @throws {ChannelMessageNotFoundError} When the session consumed the message while the RPC ran.
|
|
86
|
+
*/
|
|
87
|
+
steerQueuedMessage(sessionId: string, messageId: string): Promise<boolean>;
|
|
88
|
+
/**
|
|
89
|
+
* Calls `delete_queued_message`.
|
|
90
|
+
* @param sessionId - Session Flow ID.
|
|
91
|
+
* @param messageId - {@link PendingUserMessage.messageId} of a queued message.
|
|
92
|
+
* @returns The RPC result: `false` when the message is no longer queued.
|
|
93
|
+
* @throws {ChannelMessageNotFoundError} When the session consumed the message while the RPC ran.
|
|
94
|
+
*/
|
|
95
|
+
deleteQueuedMessage(sessionId: string, messageId: string): Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Calls `cancel_turn`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
98
|
+
* @param sessionId - Session Flow ID.
|
|
99
|
+
* @param turnId - The turn to stop, or `null` for the active turn. Defaults to `null`.
|
|
100
|
+
* @returns The RPC result: `false` when no turn is active, `turnId` names another turn, or the turn does not
|
|
101
|
+
* accept cancellation.
|
|
102
|
+
*/
|
|
103
|
+
cancelTurn(sessionId: string, turnId?: string | null): Promise<boolean>;
|
|
104
|
+
/**
|
|
105
|
+
* Calls `resolve_interrupt`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
106
|
+
* @param sessionId - Session Flow ID.
|
|
107
|
+
* @param interruptId - Interrupt to resolve.
|
|
108
|
+
* @param approved - Whether the tool call may run.
|
|
109
|
+
* @param rejectionMessage - Text shown to the model on rejection. Defaults to `null`.
|
|
110
|
+
* @returns The RPC result: `false` when the interrupt was not pending.
|
|
111
|
+
*/
|
|
112
|
+
resolveInterrupt(sessionId: string, interruptId: string, approved: boolean, rejectionMessage?: string | null): Promise<boolean>;
|
|
113
|
+
/**
|
|
114
|
+
* Calls `answer_question`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
115
|
+
* @param sessionId - Session Flow ID.
|
|
116
|
+
* @param interruptId - Pending `question` interrupt to answer.
|
|
117
|
+
* @param answer - The answer text.
|
|
118
|
+
* @returns The RPC result: `false` when the question was not pending or the answer was refused.
|
|
119
|
+
*/
|
|
120
|
+
answerQuestion(sessionId: string, interruptId: string, answer: string): Promise<boolean>;
|
|
121
|
+
/**
|
|
122
|
+
* Calls `get_agent_snapshot`.
|
|
123
|
+
* @param sessionId - Session Flow ID.
|
|
124
|
+
* @returns The snapshot.
|
|
125
|
+
*/
|
|
126
|
+
getAgentSnapshot(sessionId: string): Promise<AgentSnapshot>;
|
|
127
|
+
/**
|
|
128
|
+
* Calls `list_agent_messages`.
|
|
129
|
+
* @param sessionId - Session Flow ID.
|
|
130
|
+
* @param beforeSequence - Exclusive upper bound, or `null` for the newest page.
|
|
131
|
+
* @param limit - Page size, clamped by the session to 1..200.
|
|
132
|
+
* @returns The page.
|
|
133
|
+
*/
|
|
134
|
+
listAgentMessages(sessionId: string, beforeSequence: number | null, limit: number): Promise<AgentMessagesPage>;
|
|
135
|
+
/**
|
|
136
|
+
* Reads the committed `AgentState` without a Worker.
|
|
137
|
+
* @param sessionId - Session Flow ID.
|
|
138
|
+
* @returns The state, or `undefined` before `StartAgentSession` committed.
|
|
139
|
+
*/
|
|
140
|
+
readAgentState(sessionId: string): Promise<AgentState | undefined>;
|
|
141
|
+
/**
|
|
142
|
+
* Reads one committed tool call record without a Worker.
|
|
143
|
+
* @param sessionId - Session Flow ID.
|
|
144
|
+
* @param toolCallId - Tool call ID.
|
|
145
|
+
* @returns The record, or `undefined` when it does not exist or its turn completed.
|
|
146
|
+
*/
|
|
147
|
+
readToolCall(sessionId: string, toolCallId: string): Promise<AgentToolCallRecord | undefined>;
|
|
148
|
+
/**
|
|
149
|
+
* Reads the committed `PendingInterrupts` without a Worker.
|
|
150
|
+
* @param sessionId - Session Flow ID.
|
|
151
|
+
* @returns The pending interrupts, or an empty array.
|
|
152
|
+
*/
|
|
153
|
+
readPendingInterrupts(sessionId: string): Promise<readonly AgentInterrupt[]>;
|
|
154
|
+
/**
|
|
155
|
+
* Polls `get_agent_snapshot` until a predicate holds; RPC failures while no Worker runs count as not ready.
|
|
156
|
+
* @param sessionId - Session Flow ID.
|
|
157
|
+
* @param predicate - Returns `true` for the awaited snapshot.
|
|
158
|
+
* @param description - What is awaited, for the timeout error.
|
|
159
|
+
* @param options - Timeout.
|
|
160
|
+
* @returns The first matching snapshot.
|
|
161
|
+
* @throws {Error} When the timeout passes.
|
|
162
|
+
*/
|
|
163
|
+
waitForAgentSnapshot(sessionId: string, predicate: (snapshot: AgentSnapshot) => boolean, description: string, options?: AgentSessionWaitOptions): Promise<AgentSnapshot>;
|
|
164
|
+
/**
|
|
165
|
+
* Polls the committed `AgentState` until a predicate holds.
|
|
166
|
+
* @param sessionId - Session Flow ID.
|
|
167
|
+
* @param predicate - Returns `true` for the awaited state.
|
|
168
|
+
* @param description - What is awaited, for the timeout error.
|
|
169
|
+
* @param options - Timeout.
|
|
170
|
+
* @returns The first matching state.
|
|
171
|
+
* @throws {Error} When the timeout passes.
|
|
172
|
+
*/
|
|
173
|
+
waitForAgentState(sessionId: string, predicate: (state: AgentState) => boolean, description: string, options?: AgentSessionWaitOptions): Promise<AgentState>;
|
|
174
|
+
/**
|
|
175
|
+
* Waits until the session is idle after at least `turnCount` completed or failed turns.
|
|
176
|
+
* @param sessionId - Session Flow ID.
|
|
177
|
+
* @param turnCount - Minimum `turnCount`.
|
|
178
|
+
* @param options - Timeout.
|
|
179
|
+
* @returns The idle state.
|
|
180
|
+
*/
|
|
181
|
+
waitForCompletedTurns(sessionId: string, turnCount: number, options?: AgentSessionWaitOptions): Promise<AgentState>;
|
|
182
|
+
/**
|
|
183
|
+
* Polls `PendingInterrupts` until an interrupt appears.
|
|
184
|
+
* @param sessionId - Session Flow ID.
|
|
185
|
+
* @param toolName - Tool whose interrupt is awaited, or `undefined` for any tool.
|
|
186
|
+
* @param options - Timeout.
|
|
187
|
+
* @returns The first matching pending interrupt.
|
|
188
|
+
*/
|
|
189
|
+
waitForPendingInterrupt(sessionId: string, toolName?: string, options?: AgentSessionWaitOptions): Promise<AgentInterrupt>;
|
|
190
|
+
/**
|
|
191
|
+
* Polls one tool call record until a predicate holds.
|
|
192
|
+
* @param sessionId - Session Flow ID.
|
|
193
|
+
* @param toolCallId - Tool call ID.
|
|
194
|
+
* @param predicate - Returns `true` for the awaited record.
|
|
195
|
+
* @param options - Timeout.
|
|
196
|
+
* @returns The first matching record.
|
|
197
|
+
*/
|
|
198
|
+
waitForToolCall(sessionId: string, toolCallId: string, predicate: (toolCall: AgentToolCallRecord) => boolean, options?: AgentSessionWaitOptions): Promise<AgentToolCallRecord>;
|
|
199
|
+
/**
|
|
200
|
+
* Reads every retained `AgentEvents` message, oldest first.
|
|
201
|
+
* @param sessionId - Session Flow ID.
|
|
202
|
+
* @returns The retained events; trimming may have removed the oldest.
|
|
203
|
+
*/
|
|
204
|
+
listAgentEvents(sessionId: string): Promise<AgentEvent[]>;
|
|
205
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Sustainable Use License 1.0.
|
|
4
|
+
// You may not use this file except in compliance with the License.
|
|
5
|
+
// See the LICENSE file in the repository root.
|
|
6
|
+
//
|
|
7
|
+
// SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
|
|
8
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
9
|
+
import { RpcLockConflictError } from "@superdurable/dex";
|
|
10
|
+
import { validateSessionSettings } from "../agent-session-settings.js";
|
|
11
|
+
import { pollUntil } from "./polling.js";
|
|
12
|
+
const lockConflictRetryTimeoutMs = 30_000;
|
|
13
|
+
const lockConflictRetryIntervalMs = 50;
|
|
14
|
+
const streamPageSize = 500;
|
|
15
|
+
/**
|
|
16
|
+
* Typed Client calls for one agent session Flow, with waits suited to integration tests.
|
|
17
|
+
*
|
|
18
|
+
* RPC methods need a running Worker. The `read*` and `waitFor*` methods use `Client.getAttributes`,
|
|
19
|
+
* which reads committed Attributes without a Worker, so they also work while a killed Worker is down.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const sessions = new AgentSessionTestClient(environment.client, flow);
|
|
24
|
+
* await sessions.startSession(sessionId, "send the report");
|
|
25
|
+
* const interrupt = await sessions.waitForPendingInterrupt(sessionId, "send_email");
|
|
26
|
+
* assert.equal(await sessions.resolveInterrupt(sessionId, interrupt.interruptId, true), true);
|
|
27
|
+
* const state = await sessions.waitForCompletedTurns(sessionId, 1);
|
|
28
|
+
* const snapshot = await sessions.getAgentSnapshot(sessionId);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class AgentSessionTestClient {
|
|
32
|
+
client;
|
|
33
|
+
flow;
|
|
34
|
+
/**
|
|
35
|
+
* Creates the helper.
|
|
36
|
+
* @param client - Client whose Registry contains `flow`.
|
|
37
|
+
* @param flow - The agent session Flow, the same object registered in the Client's Registry.
|
|
38
|
+
*/
|
|
39
|
+
constructor(
|
|
40
|
+
/** Client used for every call. */
|
|
41
|
+
client,
|
|
42
|
+
/** Flow whose sessions this helper drives. */
|
|
43
|
+
flow) {
|
|
44
|
+
this.client = client;
|
|
45
|
+
this.flow = flow;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Starts a session.
|
|
49
|
+
* @param sessionId - Flow ID of the new session.
|
|
50
|
+
* @param initialMessage - First user message, or `null` to start idle. Defaults to `null`.
|
|
51
|
+
* @param startFlowOptions - Start options, such as a `configOverride` with `stepDurability`.
|
|
52
|
+
* @param sessionSettings - The session's settings, or `null` to start without settings. Defaults to `null`.
|
|
53
|
+
* @returns A Promise that resolves after Dex accepted the start.
|
|
54
|
+
* @throws {TypeError | RangeError} From `validateSessionSettings`, before anything is sent.
|
|
55
|
+
*/
|
|
56
|
+
async startSession(sessionId, initialMessage = null, startFlowOptions = {}, sessionSettings = null) {
|
|
57
|
+
if (sessionSettings !== null) {
|
|
58
|
+
validateSessionSettings(sessionSettings);
|
|
59
|
+
}
|
|
60
|
+
await this.client.startFlow(this.flow, sessionId, { initialMessage, sessionSettings }, startFlowOptions);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Calls `update_session_settings`.
|
|
64
|
+
* @param sessionId - Session Flow ID.
|
|
65
|
+
* @param sessionSettings - The complete new settings; `{}` clears them.
|
|
66
|
+
* @returns The RPC result: `false` before the session initialized or for settings over the limit.
|
|
67
|
+
*/
|
|
68
|
+
updateSessionSettings(sessionId, sessionSettings) {
|
|
69
|
+
return this.client.invokeRPC(this.flow.updateSessionSettings, sessionId, { sessionSettings });
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Calls `send_user_message`.
|
|
73
|
+
* @param sessionId - Session Flow ID.
|
|
74
|
+
* @param text - Message text.
|
|
75
|
+
* @param planMode - Whether the message starts a planning turn. Defaults to `false`.
|
|
76
|
+
* @returns The RPC result: `false` for blank text, or for a `planMode` message when plan mode is off.
|
|
77
|
+
*/
|
|
78
|
+
sendUserMessage(sessionId, text, planMode = false) {
|
|
79
|
+
return this.client.invokeRPC(this.flow.sendUserMessage, sessionId, { text, planMode });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Calls `execute_plan`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
83
|
+
* @param sessionId - Session Flow ID.
|
|
84
|
+
* @param revision - Plan revision to execute.
|
|
85
|
+
* @returns The RPC result: `false` when the revision is stale, already pending, or the session is busy.
|
|
86
|
+
*/
|
|
87
|
+
executePlan(sessionId, revision) {
|
|
88
|
+
return retryLockConflicts(() => this.client.invokeRPC(this.flow.executePlan, sessionId, { revision }));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Calls `steer_user_message` with new text; while no turn is active, the RPC queues it.
|
|
92
|
+
* @param sessionId - Session Flow ID.
|
|
93
|
+
* @param text - Text of the steered message.
|
|
94
|
+
* @returns The RPC result: `false` for blank text or a turn that does not accept steering.
|
|
95
|
+
*/
|
|
96
|
+
steerUserMessage(sessionId, text) {
|
|
97
|
+
return this.client.invokeRPC(this.flow.steerUserMessage, sessionId, { messageId: null, text });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Calls `steer_user_message` with a queued message's ID, which moves that message.
|
|
101
|
+
* @param sessionId - Session Flow ID.
|
|
102
|
+
* @param messageId - {@link PendingUserMessage.messageId} of a queued message.
|
|
103
|
+
* @returns The RPC result: `false` when the message is no longer queued.
|
|
104
|
+
* @throws {ChannelMessageNotFoundError} When the session consumed the message while the RPC ran.
|
|
105
|
+
*/
|
|
106
|
+
steerQueuedMessage(sessionId, messageId) {
|
|
107
|
+
return this.client.invokeRPC(this.flow.steerUserMessage, sessionId, { messageId, text: null });
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Calls `delete_queued_message`.
|
|
111
|
+
* @param sessionId - Session Flow ID.
|
|
112
|
+
* @param messageId - {@link PendingUserMessage.messageId} of a queued message.
|
|
113
|
+
* @returns The RPC result: `false` when the message is no longer queued.
|
|
114
|
+
* @throws {ChannelMessageNotFoundError} When the session consumed the message while the RPC ran.
|
|
115
|
+
*/
|
|
116
|
+
deleteQueuedMessage(sessionId, messageId) {
|
|
117
|
+
return this.client.invokeRPC(this.flow.deleteQueuedMessage, sessionId, { messageId });
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Calls `cancel_turn`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
121
|
+
* @param sessionId - Session Flow ID.
|
|
122
|
+
* @param turnId - The turn to stop, or `null` for the active turn. Defaults to `null`.
|
|
123
|
+
* @returns The RPC result: `false` when no turn is active, `turnId` names another turn, or the turn does not
|
|
124
|
+
* accept cancellation.
|
|
125
|
+
*/
|
|
126
|
+
cancelTurn(sessionId, turnId = null) {
|
|
127
|
+
return retryLockConflicts(() => this.client.invokeRPC(this.flow.cancelTurn, sessionId, { turnId }));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Calls `resolve_interrupt`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
131
|
+
* @param sessionId - Session Flow ID.
|
|
132
|
+
* @param interruptId - Interrupt to resolve.
|
|
133
|
+
* @param approved - Whether the tool call may run.
|
|
134
|
+
* @param rejectionMessage - Text shown to the model on rejection. Defaults to `null`.
|
|
135
|
+
* @returns The RPC result: `false` when the interrupt was not pending.
|
|
136
|
+
*/
|
|
137
|
+
resolveInterrupt(sessionId, interruptId, approved, rejectionMessage = null) {
|
|
138
|
+
return retryLockConflicts(() => this.client.invokeRPC(this.flow.resolveInterrupt, sessionId, { interruptId, approved, rejectionMessage }));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Calls `answer_question`, retrying an `RpcLockConflictError` for up to 30 seconds.
|
|
142
|
+
* @param sessionId - Session Flow ID.
|
|
143
|
+
* @param interruptId - Pending `question` interrupt to answer.
|
|
144
|
+
* @param answer - The answer text.
|
|
145
|
+
* @returns The RPC result: `false` when the question was not pending or the answer was refused.
|
|
146
|
+
*/
|
|
147
|
+
answerQuestion(sessionId, interruptId, answer) {
|
|
148
|
+
return retryLockConflicts(() => this.client.invokeRPC(this.flow.answerQuestion, sessionId, { interruptId, answer }));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Calls `get_agent_snapshot`.
|
|
152
|
+
* @param sessionId - Session Flow ID.
|
|
153
|
+
* @returns The snapshot.
|
|
154
|
+
*/
|
|
155
|
+
getAgentSnapshot(sessionId) {
|
|
156
|
+
return this.client.invokeRPC(this.flow.getAgentSnapshot, sessionId);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Calls `list_agent_messages`.
|
|
160
|
+
* @param sessionId - Session Flow ID.
|
|
161
|
+
* @param beforeSequence - Exclusive upper bound, or `null` for the newest page.
|
|
162
|
+
* @param limit - Page size, clamped by the session to 1..200.
|
|
163
|
+
* @returns The page.
|
|
164
|
+
*/
|
|
165
|
+
listAgentMessages(sessionId, beforeSequence, limit) {
|
|
166
|
+
return this.client.invokeRPC(this.flow.listAgentMessages, sessionId, { beforeSequence, limit });
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Reads the committed `AgentState` without a Worker.
|
|
170
|
+
* @param sessionId - Session Flow ID.
|
|
171
|
+
* @returns The state, or `undefined` before `StartAgentSession` committed.
|
|
172
|
+
*/
|
|
173
|
+
async readAgentState(sessionId) {
|
|
174
|
+
const [state] = await this.client.getAttributes(sessionId, this.flow.agentState);
|
|
175
|
+
return state;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Reads one committed tool call record without a Worker.
|
|
179
|
+
* @param sessionId - Session Flow ID.
|
|
180
|
+
* @param toolCallId - Tool call ID.
|
|
181
|
+
* @returns The record, or `undefined` when it does not exist or its turn completed.
|
|
182
|
+
*/
|
|
183
|
+
async readToolCall(sessionId, toolCallId) {
|
|
184
|
+
const [toolCall] = await this.client.getAttributes(sessionId, this.flow.agentToolCalls.instance(toolCallId));
|
|
185
|
+
return toolCall;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Reads the committed `PendingInterrupts` without a Worker.
|
|
189
|
+
* @param sessionId - Session Flow ID.
|
|
190
|
+
* @returns The pending interrupts, or an empty array.
|
|
191
|
+
*/
|
|
192
|
+
async readPendingInterrupts(sessionId) {
|
|
193
|
+
const [interrupts] = await this.client.getAttributes(sessionId, this.flow.pendingInterrupts);
|
|
194
|
+
return interrupts ?? [];
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Polls `get_agent_snapshot` until a predicate holds; RPC failures while no Worker runs count as not ready.
|
|
198
|
+
* @param sessionId - Session Flow ID.
|
|
199
|
+
* @param predicate - Returns `true` for the awaited snapshot.
|
|
200
|
+
* @param description - What is awaited, for the timeout error.
|
|
201
|
+
* @param options - Timeout.
|
|
202
|
+
* @returns The first matching snapshot.
|
|
203
|
+
* @throws {Error} When the timeout passes.
|
|
204
|
+
*/
|
|
205
|
+
waitForAgentSnapshot(sessionId, predicate, description, options = {}) {
|
|
206
|
+
return pollUntil(async () => {
|
|
207
|
+
const snapshot = await this.getAgentSnapshot(sessionId);
|
|
208
|
+
return predicate(snapshot) ? snapshot : undefined;
|
|
209
|
+
}, { description: `${description} in session ${sessionId}`, ...options });
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Polls the committed `AgentState` until a predicate holds.
|
|
213
|
+
* @param sessionId - Session Flow ID.
|
|
214
|
+
* @param predicate - Returns `true` for the awaited state.
|
|
215
|
+
* @param description - What is awaited, for the timeout error.
|
|
216
|
+
* @param options - Timeout.
|
|
217
|
+
* @returns The first matching state.
|
|
218
|
+
* @throws {Error} When the timeout passes.
|
|
219
|
+
*/
|
|
220
|
+
waitForAgentState(sessionId, predicate, description, options = {}) {
|
|
221
|
+
return pollUntil(async () => {
|
|
222
|
+
const state = await this.readAgentState(sessionId);
|
|
223
|
+
return state !== undefined && predicate(state) ? state : undefined;
|
|
224
|
+
}, { description: `${description} in session ${sessionId}`, ...options });
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Waits until the session is idle after at least `turnCount` completed or failed turns.
|
|
228
|
+
* @param sessionId - Session Flow ID.
|
|
229
|
+
* @param turnCount - Minimum `turnCount`.
|
|
230
|
+
* @param options - Timeout.
|
|
231
|
+
* @returns The idle state.
|
|
232
|
+
*/
|
|
233
|
+
waitForCompletedTurns(sessionId, turnCount, options = {}) {
|
|
234
|
+
return this.waitForAgentState(sessionId, (state) => state.status === "waiting_for_user_message" && state.turnCount >= turnCount, `${turnCount} finished turns`, options);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Polls `PendingInterrupts` until an interrupt appears.
|
|
238
|
+
* @param sessionId - Session Flow ID.
|
|
239
|
+
* @param toolName - Tool whose interrupt is awaited, or `undefined` for any tool.
|
|
240
|
+
* @param options - Timeout.
|
|
241
|
+
* @returns The first matching pending interrupt.
|
|
242
|
+
*/
|
|
243
|
+
waitForPendingInterrupt(sessionId, toolName, options = {}) {
|
|
244
|
+
return pollUntil(async () => (await this.readPendingInterrupts(sessionId)).find((interrupt) => toolName === undefined || interrupt.toolName === toolName), { description: `pending interrupt for ${toolName ?? "any tool"} in session ${sessionId}`, ...options });
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Polls one tool call record until a predicate holds.
|
|
248
|
+
* @param sessionId - Session Flow ID.
|
|
249
|
+
* @param toolCallId - Tool call ID.
|
|
250
|
+
* @param predicate - Returns `true` for the awaited record.
|
|
251
|
+
* @param options - Timeout.
|
|
252
|
+
* @returns The first matching record.
|
|
253
|
+
*/
|
|
254
|
+
waitForToolCall(sessionId, toolCallId, predicate, options = {}) {
|
|
255
|
+
return pollUntil(async () => {
|
|
256
|
+
const toolCall = await this.readToolCall(sessionId, toolCallId);
|
|
257
|
+
return toolCall !== undefined && predicate(toolCall) ? toolCall : undefined;
|
|
258
|
+
}, { description: `tool call ${toolCallId} in session ${sessionId}`, ...options });
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Reads every retained `AgentEvents` message, oldest first.
|
|
262
|
+
* @param sessionId - Session Flow ID.
|
|
263
|
+
* @returns The retained events; trimming may have removed the oldest.
|
|
264
|
+
*/
|
|
265
|
+
async listAgentEvents(sessionId) {
|
|
266
|
+
const newestFirst = [];
|
|
267
|
+
let pageToken = "";
|
|
268
|
+
do {
|
|
269
|
+
const page = await this.client.listStreamMessages(sessionId, this.flow.agentEvents, streamPageSize, pageToken);
|
|
270
|
+
newestFirst.push(...page.messages.map((message) => message.value));
|
|
271
|
+
pageToken = page.nextPageToken;
|
|
272
|
+
} while (pageToken !== "");
|
|
273
|
+
return newestFirst.reverse();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async function retryLockConflicts(invoke) {
|
|
277
|
+
const deadline = Date.now() + lockConflictRetryTimeoutMs;
|
|
278
|
+
for (;;) {
|
|
279
|
+
try {
|
|
280
|
+
return await invoke();
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
if (!(error instanceof RpcLockConflictError) || Date.now() >= deadline) {
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
await delay(lockConflictRetryIntervalMs);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=agent-session-test-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-test-client.js","sourceRoot":"","sources":["../../../src/testing/agent-session-test-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Client, Registry, Worker, type Flow } from "@superdurable/dex";
|
|
2
|
+
/** Options of {@link AgentTestEnvironment.start}. */
|
|
3
|
+
export interface AgentTestEnvironmentOptions {
|
|
4
|
+
/** Dex Server address, usually {@link PrivateDexEngine.serverAddress}. */
|
|
5
|
+
readonly serverAddress: string;
|
|
6
|
+
/** Flows registered in the environment's Registry. */
|
|
7
|
+
readonly flows: readonly Flow<any>[];
|
|
8
|
+
/**
|
|
9
|
+
* Whether to run a pull Worker in this process. Pass `false` when the Worker runs in a
|
|
10
|
+
* {@link KillableWorkerProcess}; the Client still needs the same Flows in its Registry.
|
|
11
|
+
*/
|
|
12
|
+
readonly isWorkerInProcess: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A Registry, BlobCache, Client, and optional in-process Worker for one test file.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const environment = await AgentTestEnvironment.start({
|
|
20
|
+
* serverAddress: engine.serverAddress, flows: [flow], isWorkerInProcess: true });
|
|
21
|
+
* try {
|
|
22
|
+
* const sessions = new AgentSessionTestClient(environment.client, flow);
|
|
23
|
+
* // ...
|
|
24
|
+
* } finally {
|
|
25
|
+
* await environment.close();
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class AgentTestEnvironment {
|
|
30
|
+
/** Registry shared by the Client and the in-process Worker. */
|
|
31
|
+
readonly registry: Registry;
|
|
32
|
+
/** Client connected to the server. */
|
|
33
|
+
readonly client: Client;
|
|
34
|
+
/** In-process Worker, or `undefined` when the Worker runs elsewhere. */
|
|
35
|
+
readonly worker: Worker | undefined;
|
|
36
|
+
private readonly blobCache;
|
|
37
|
+
private readonly cacheDirectory;
|
|
38
|
+
private constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Creates the environment and, when requested, starts the Worker and waits for its registration.
|
|
41
|
+
* @param options - Server address, Flows, and Worker placement.
|
|
42
|
+
* @returns The ready environment.
|
|
43
|
+
* @throws {FlowDefinitionError} When the Flows are invalid.
|
|
44
|
+
* @throws {Error} When the Worker cannot register within 30 seconds.
|
|
45
|
+
*/
|
|
46
|
+
static start(options: AgentTestEnvironmentOptions): Promise<AgentTestEnvironment>;
|
|
47
|
+
/**
|
|
48
|
+
* Closes the Client, the in-process Worker, and the BlobCache, then deletes the cache directory.
|
|
49
|
+
* @returns A Promise that resolves after everything closed.
|
|
50
|
+
*/
|
|
51
|
+
close(): Promise<void>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Sustainable Use License 1.0.
|
|
4
|
+
// You may not use this file except in compliance with the License.
|
|
5
|
+
// See the LICENSE file in the repository root.
|
|
6
|
+
//
|
|
7
|
+
// SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { Client, Registry, Worker, openBlobCache, } from "@superdurable/dex";
|
|
13
|
+
/**
|
|
14
|
+
* A Registry, BlobCache, Client, and optional in-process Worker for one test file.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const environment = await AgentTestEnvironment.start({
|
|
19
|
+
* serverAddress: engine.serverAddress, flows: [flow], isWorkerInProcess: true });
|
|
20
|
+
* try {
|
|
21
|
+
* const sessions = new AgentSessionTestClient(environment.client, flow);
|
|
22
|
+
* // ...
|
|
23
|
+
* } finally {
|
|
24
|
+
* await environment.close();
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class AgentTestEnvironment {
|
|
29
|
+
registry;
|
|
30
|
+
client;
|
|
31
|
+
worker;
|
|
32
|
+
blobCache;
|
|
33
|
+
cacheDirectory;
|
|
34
|
+
constructor(
|
|
35
|
+
/** Registry shared by the Client and the in-process Worker. */
|
|
36
|
+
registry,
|
|
37
|
+
/** Client connected to the server. */
|
|
38
|
+
client,
|
|
39
|
+
/** In-process Worker, or `undefined` when the Worker runs elsewhere. */
|
|
40
|
+
worker, blobCache, cacheDirectory) {
|
|
41
|
+
this.registry = registry;
|
|
42
|
+
this.client = client;
|
|
43
|
+
this.worker = worker;
|
|
44
|
+
this.blobCache = blobCache;
|
|
45
|
+
this.cacheDirectory = cacheDirectory;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates the environment and, when requested, starts the Worker and waits for its registration.
|
|
49
|
+
* @param options - Server address, Flows, and Worker placement.
|
|
50
|
+
* @returns The ready environment.
|
|
51
|
+
* @throws {FlowDefinitionError} When the Flows are invalid.
|
|
52
|
+
* @throws {Error} When the Worker cannot register within 30 seconds.
|
|
53
|
+
*/
|
|
54
|
+
static async start(options) {
|
|
55
|
+
const registry = new Registry(options.flows);
|
|
56
|
+
const cacheDirectory = mkdtempSync(join(tmpdir(), "dex-agents-test-cache-"));
|
|
57
|
+
const blobCache = openBlobCache({ directory: cacheDirectory, maxBytes: 64 * 1024 * 1024 });
|
|
58
|
+
let worker;
|
|
59
|
+
if (options.isWorkerInProcess) {
|
|
60
|
+
worker = new Worker(registry, blobCache, {
|
|
61
|
+
serverAddress: options.serverAddress,
|
|
62
|
+
// Parallel tool Steps must not depend on the CI machine's CPU count.
|
|
63
|
+
pull: { appWorkerID: `dex-agents-test-worker-${randomUUID()}`, maxConcurrentTasks: 8 },
|
|
64
|
+
});
|
|
65
|
+
await worker.start();
|
|
66
|
+
await worker.waitPullRegistered({ timeoutMs: 30_000 });
|
|
67
|
+
}
|
|
68
|
+
const client = new Client(registry, blobCache, { serverAddress: options.serverAddress });
|
|
69
|
+
return new AgentTestEnvironment(registry, client, worker, blobCache, cacheDirectory);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Closes the Client, the in-process Worker, and the BlobCache, then deletes the cache directory.
|
|
73
|
+
* @returns A Promise that resolves after everything closed.
|
|
74
|
+
*/
|
|
75
|
+
async close() {
|
|
76
|
+
try {
|
|
77
|
+
await this.client.close();
|
|
78
|
+
await this.worker?.close();
|
|
79
|
+
this.blobCache.close();
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
rmSync(this.cacheDirectory, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=agent-test-environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-test-environment.js","sourceRoot":"","sources":["../../../src/testing/agent-test-environment.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kDAAkD;AAClD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,aAAa,GAGd,MAAM,mBAAmB,CAAC;AAe3B;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,oBAAoB;IAGb;IAEA;IAEA;IACC;IACA;IARnB;IACE,+DAA+D;IAC/C,QAAkB;IAClC,sCAAsC;IACtB,MAAc;IAC9B,wEAAwE;IACxD,MAA0B,EACzB,SAAoB,EACpB,cAAsB;QANvB,aAAQ,GAAR,QAAQ,CAAU;QAElB,WAAM,GAAN,MAAM,CAAQ;QAEd,WAAM,GAAN,MAAM,CAAoB;QACzB,cAAS,GAAT,SAAS,CAAW;QACpB,mBAAc,GAAd,cAAc,CAAQ;IACtC,CAAC;IAEJ;;;;;;OAMG;IACI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAoC;QAC5D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CAAC;QAC7E,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QAC3F,IAAI,MAA0B,CAAC;QAC/B,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;YAC9B,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE;gBACvC,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,qEAAqE;gBACrE,IAAI,EAAE,EAAE,WAAW,EAAE,0BAA0B,UAAU,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,EAAE;aACvF,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,MAAM,CAAC,kBAAkB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;QACzF,OAAO,IAAI,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IACvF,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;CACF"}
|