norns-sdk 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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +262 -2
- package/dist/index.cjs +1666 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +679 -0
- package/dist/index.d.ts +679 -0
- package/dist/index.js +1629 -0
- package/dist/index.js.map +1 -0
- package/package.json +92 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
import { LanguageModel, JSONSchema7, FlexibleSchema } from 'ai';
|
|
2
|
+
export { jsonSchema } from 'ai';
|
|
3
|
+
import { SocketConnectOption } from 'phoenix';
|
|
4
|
+
|
|
5
|
+
/** Where a run is: on the way, finished, or parked on a human. */
|
|
6
|
+
type RunStatus = "pending" | "running" | "waiting" | "completed" | "failed" | "error";
|
|
7
|
+
/** The question a run is parked on when its status is `"waiting"`. */
|
|
8
|
+
interface WaitingFor {
|
|
9
|
+
question: string;
|
|
10
|
+
toolCallId: string;
|
|
11
|
+
askedAt: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
interface Run {
|
|
14
|
+
id: number;
|
|
15
|
+
agentId: number;
|
|
16
|
+
conversationId: number | null;
|
|
17
|
+
parentRunId: number | null;
|
|
18
|
+
gardId: number | null;
|
|
19
|
+
status: RunStatus;
|
|
20
|
+
triggerType: string;
|
|
21
|
+
input: Record<string, unknown>;
|
|
22
|
+
output: string | null;
|
|
23
|
+
/** Present when `status` is `"waiting"`: answer it with `client.runs.reply()`. */
|
|
24
|
+
waitingFor: WaitingFor | null;
|
|
25
|
+
failureMetadata: Record<string, unknown>;
|
|
26
|
+
inputTokens: number;
|
|
27
|
+
outputTokens: number;
|
|
28
|
+
/** Dollars as a decimal string, so nothing rounds them through a float. */
|
|
29
|
+
cost: {
|
|
30
|
+
usd: string;
|
|
31
|
+
unpriced: boolean;
|
|
32
|
+
} | null;
|
|
33
|
+
insertedAt: string;
|
|
34
|
+
updatedAt: string;
|
|
35
|
+
}
|
|
36
|
+
interface RunEvent {
|
|
37
|
+
id: number;
|
|
38
|
+
sequence: number;
|
|
39
|
+
eventType: string;
|
|
40
|
+
payload: Record<string, unknown>;
|
|
41
|
+
source: string;
|
|
42
|
+
insertedAt: string;
|
|
43
|
+
}
|
|
44
|
+
/** An agent as the server describes it. */
|
|
45
|
+
interface AgentInfo {
|
|
46
|
+
id: number;
|
|
47
|
+
name: string;
|
|
48
|
+
purpose: string | null;
|
|
49
|
+
status: string;
|
|
50
|
+
model: string;
|
|
51
|
+
systemPrompt: string;
|
|
52
|
+
maxSteps: number;
|
|
53
|
+
contextStrategy: string;
|
|
54
|
+
contextWindow: number;
|
|
55
|
+
archivedAt: string | null;
|
|
56
|
+
insertedAt: string;
|
|
57
|
+
updatedAt: string;
|
|
58
|
+
}
|
|
59
|
+
interface Conversation {
|
|
60
|
+
id: number;
|
|
61
|
+
agentId: number;
|
|
62
|
+
key: string;
|
|
63
|
+
summary: string | null;
|
|
64
|
+
messageCount: number;
|
|
65
|
+
tokenEstimate: number;
|
|
66
|
+
insertedAt: string;
|
|
67
|
+
updatedAt: string;
|
|
68
|
+
}
|
|
69
|
+
/** What `sendMessage()` returns. */
|
|
70
|
+
interface MessageResult {
|
|
71
|
+
runId: number;
|
|
72
|
+
/** `"accepted"` when not waiting; otherwise the run's status when the wait ended. */
|
|
73
|
+
status: "accepted" | RunStatus;
|
|
74
|
+
output: string | null;
|
|
75
|
+
conversationKey: string | undefined;
|
|
76
|
+
/** The agent is parked on a question and will not move until it is answered. */
|
|
77
|
+
isWaiting: boolean;
|
|
78
|
+
waitingFor: WaitingFor | null;
|
|
79
|
+
}
|
|
80
|
+
/** Every stream payload carries the run and agent it belongs to. */
|
|
81
|
+
interface Tagged {
|
|
82
|
+
run_id: number;
|
|
83
|
+
agent_id: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* An event from a run as it happens, keyed by `type`. The two terminal
|
|
87
|
+
* events are `completed` and `error`. Unknown types pass through as
|
|
88
|
+
* `{ type: string, data: object }` so a newer server never breaks a client.
|
|
89
|
+
*/
|
|
90
|
+
type StreamEvent = {
|
|
91
|
+
type: "agent_started";
|
|
92
|
+
data: Tagged;
|
|
93
|
+
} | {
|
|
94
|
+
type: "llm_response";
|
|
95
|
+
data: Tagged & {
|
|
96
|
+
step: number;
|
|
97
|
+
finish_reason: string;
|
|
98
|
+
content: string;
|
|
99
|
+
tool_calls: Array<{
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
arguments: unknown;
|
|
103
|
+
}> | null;
|
|
104
|
+
};
|
|
105
|
+
} | {
|
|
106
|
+
type: "tool_call";
|
|
107
|
+
data: Tagged & {
|
|
108
|
+
name: string;
|
|
109
|
+
arguments: unknown;
|
|
110
|
+
};
|
|
111
|
+
} | {
|
|
112
|
+
type: "tool_result";
|
|
113
|
+
data: Tagged & {
|
|
114
|
+
tool_call_id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
content: string;
|
|
117
|
+
[key: string]: unknown;
|
|
118
|
+
};
|
|
119
|
+
} | {
|
|
120
|
+
type: "waiting_for_user";
|
|
121
|
+
data: Tagged & {
|
|
122
|
+
tool_call_id: string;
|
|
123
|
+
question: string;
|
|
124
|
+
};
|
|
125
|
+
} | {
|
|
126
|
+
type: "waiting_timer";
|
|
127
|
+
data: Tagged & {
|
|
128
|
+
seconds: number;
|
|
129
|
+
reason: string | null;
|
|
130
|
+
};
|
|
131
|
+
} | {
|
|
132
|
+
type: "context_compacted";
|
|
133
|
+
data: Tagged & {
|
|
134
|
+
step: number;
|
|
135
|
+
dropped: number;
|
|
136
|
+
kept: number;
|
|
137
|
+
};
|
|
138
|
+
} | {
|
|
139
|
+
type: "completed";
|
|
140
|
+
data: Tagged & {
|
|
141
|
+
output: string;
|
|
142
|
+
};
|
|
143
|
+
} | {
|
|
144
|
+
type: "error";
|
|
145
|
+
data: Tagged & {
|
|
146
|
+
error: string;
|
|
147
|
+
};
|
|
148
|
+
} | {
|
|
149
|
+
type: string;
|
|
150
|
+
data: Tagged & Record<string, unknown>;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** A class that behaves like the browser `WebSocket`; the global one by default. */
|
|
154
|
+
type WebSocketConstructor = new (url: string, protocols?: string | string[]) => object;
|
|
155
|
+
interface NornsClientOptions {
|
|
156
|
+
/** The Norns base URL. Defaults to `NORNS_URL`. */
|
|
157
|
+
url?: string;
|
|
158
|
+
/** The tenant's bearer token. Defaults to `NORNS_API_KEY`. */
|
|
159
|
+
apiKey?: string;
|
|
160
|
+
/** Replace the global `fetch`, for tests or custom transport. */
|
|
161
|
+
fetch?: typeof globalThis.fetch;
|
|
162
|
+
/** Replace the global `WebSocket` for streams. */
|
|
163
|
+
transport?: WebSocketConstructor;
|
|
164
|
+
/** Extra options for the Phoenix socket that streams use. */
|
|
165
|
+
socketOptions?: Partial<SocketConnectOption>;
|
|
166
|
+
}
|
|
167
|
+
/** An agent's id, or its name. */
|
|
168
|
+
type AgentRef = number | string;
|
|
169
|
+
interface SendMessageOptions {
|
|
170
|
+
/** Key for multi-turn conversations, e.g. a Slack user id. */
|
|
171
|
+
conversationKey?: string;
|
|
172
|
+
/** Opaque context stored on the run. */
|
|
173
|
+
context?: Record<string, unknown>;
|
|
174
|
+
/** Pin the run to a gard. */
|
|
175
|
+
gardId?: number;
|
|
176
|
+
/**
|
|
177
|
+
* Poll until the run completes, fails, or parks on a question. A parked
|
|
178
|
+
* agent is waiting on you, so the wait ends there too.
|
|
179
|
+
*/
|
|
180
|
+
wait?: boolean;
|
|
181
|
+
/** How long to wait, with `wait`. Default 30 seconds. */
|
|
182
|
+
timeoutMs?: number;
|
|
183
|
+
signal?: AbortSignal;
|
|
184
|
+
}
|
|
185
|
+
interface StreamOptions {
|
|
186
|
+
conversationKey?: string;
|
|
187
|
+
context?: Record<string, unknown>;
|
|
188
|
+
gardId?: number;
|
|
189
|
+
/** Give up if no event arrives for this long. Default 2 minutes. */
|
|
190
|
+
idleTimeoutMs?: number;
|
|
191
|
+
signal?: AbortSignal;
|
|
192
|
+
}
|
|
193
|
+
interface ListRunsOptions {
|
|
194
|
+
/** How many the server returns, most recent first. Default 50. */
|
|
195
|
+
limit?: number;
|
|
196
|
+
status?: RunStatus;
|
|
197
|
+
agentId?: number;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Talks to Norns from application code: sends messages, reads runs, streams
|
|
201
|
+
* events. For serving an agent, see `NornsWorker`.
|
|
202
|
+
*
|
|
203
|
+
* const client = new NornsClient({ url, apiKey });
|
|
204
|
+
* const result = await client.sendMessage("support-bot", "Where's my order?", { wait: true });
|
|
205
|
+
*/
|
|
206
|
+
declare class NornsClient {
|
|
207
|
+
#private;
|
|
208
|
+
readonly url: string;
|
|
209
|
+
constructor(options?: NornsClientOptions);
|
|
210
|
+
readonly agents: {
|
|
211
|
+
list: () => Promise<AgentInfo[]>;
|
|
212
|
+
/** By id, or by name. */
|
|
213
|
+
get: (agent: AgentRef) => Promise<AgentInfo>;
|
|
214
|
+
/**
|
|
215
|
+
* Create the agent, or update it to match the definition if it exists,
|
|
216
|
+
* so code changes are always picked up. The worker does this on start.
|
|
217
|
+
*/
|
|
218
|
+
ensure: (definition: AgentDefinition) => Promise<AgentInfo>;
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Send a message to an agent. Returns at once with the run id, or, with
|
|
222
|
+
* `wait`, once the run completes, fails, or parks on a question.
|
|
223
|
+
*/
|
|
224
|
+
sendMessage(agent: AgentRef, content: string, options?: SendMessageOptions): Promise<MessageResult>;
|
|
225
|
+
/**
|
|
226
|
+
* Send a message and iterate the run's events as they happen, until it
|
|
227
|
+
* completes or errors.
|
|
228
|
+
*
|
|
229
|
+
* for await (const event of client.stream("researcher", "Summarise this")) {
|
|
230
|
+
* if (event.type === "completed") console.log(event.data.output);
|
|
231
|
+
* }
|
|
232
|
+
*/
|
|
233
|
+
stream(agent: AgentRef, content: string, options?: StreamOptions): AsyncGenerator<StreamEvent, void, undefined>;
|
|
234
|
+
readonly runs: {
|
|
235
|
+
get: (runId: number) => Promise<Run>;
|
|
236
|
+
/**
|
|
237
|
+
* The tenant's runs, most recent first. Use it to find runs nobody holds
|
|
238
|
+
* a handle to, above all the ones parked on an `ask_human` question:
|
|
239
|
+
*
|
|
240
|
+
* for (const run of await client.runs.list({ status: "waiting" })) {
|
|
241
|
+
* console.log(run.id, run.waitingFor?.question);
|
|
242
|
+
* }
|
|
243
|
+
*
|
|
244
|
+
* `status` and `agentId` filter the page the server returned, so a
|
|
245
|
+
* narrow filter over a busy tenant may need a larger `limit`.
|
|
246
|
+
*/
|
|
247
|
+
list: (options?: ListRunsOptions) => Promise<Run[]>;
|
|
248
|
+
events: (runId: number) => Promise<RunEvent[]>;
|
|
249
|
+
/**
|
|
250
|
+
* Answer a run parked on an `ask_human` question. Sending the agent a
|
|
251
|
+
* normal message does the same and is usually what a chat client wants;
|
|
252
|
+
* this targets one specific run.
|
|
253
|
+
*/
|
|
254
|
+
reply: (runId: number, answer: string) => Promise<void>;
|
|
255
|
+
/** Re-run a finished or failed run's message. Returns the new run id. */
|
|
256
|
+
retry: (runId: number) => Promise<number>;
|
|
257
|
+
/** Iterate an existing run's events as they happen, until it finishes. */
|
|
258
|
+
watch: (runId: number, options?: Pick<StreamOptions, "idleTimeoutMs" | "signal">) => AsyncGenerator<StreamEvent, void, undefined>;
|
|
259
|
+
};
|
|
260
|
+
readonly conversations: {
|
|
261
|
+
list: (agent: AgentRef) => Promise<Conversation[]>;
|
|
262
|
+
get: (agent: AgentRef, key: string) => Promise<Conversation>;
|
|
263
|
+
/** Reset a conversation: its history is gone, the next message starts fresh. */
|
|
264
|
+
delete: (agent: AgentRef, key: string) => Promise<void>;
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Where the SDK writes what it is doing. Anything with these four methods
|
|
270
|
+
* works: `console`, pino, winston, or your own.
|
|
271
|
+
*/
|
|
272
|
+
interface Logger {
|
|
273
|
+
debug(message: string, ...rest: unknown[]): void;
|
|
274
|
+
info(message: string, ...rest: unknown[]): void;
|
|
275
|
+
warn(message: string, ...rest: unknown[]): void;
|
|
276
|
+
error(message: string, ...rest: unknown[]): void;
|
|
277
|
+
}
|
|
278
|
+
/** Logs info and above to the console, prefixed so it is easy to grep. */
|
|
279
|
+
declare const consoleLogger: Logger;
|
|
280
|
+
declare const silentLogger: Logger;
|
|
281
|
+
|
|
282
|
+
/** Turns the model name on an LLM task into something the AI SDK can call. */
|
|
283
|
+
type ModelResolver = (modelId: string) => LanguageModel | Promise<LanguageModel>;
|
|
284
|
+
/** A provider, or a registry from `createProviderRegistry()`. */
|
|
285
|
+
interface ModelSource {
|
|
286
|
+
languageModel(modelId: string): LanguageModel;
|
|
287
|
+
}
|
|
288
|
+
type ModelsOption = ModelResolver | ModelSource;
|
|
289
|
+
/**
|
|
290
|
+
* Split `"provider/model"` (or `"provider:model"`) into its parts, inferring
|
|
291
|
+
* the provider from the model's name when there is no prefix.
|
|
292
|
+
*/
|
|
293
|
+
declare function inferProvider(model: string): {
|
|
294
|
+
provider: string;
|
|
295
|
+
modelId: string;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
type Capability = "llm" | "tools";
|
|
299
|
+
interface NornsWorkerOptions {
|
|
300
|
+
/** The agent, or agents, this worker serves. */
|
|
301
|
+
agents: AgentDefinition | AgentDefinition[];
|
|
302
|
+
/** The Norns base URL. Defaults to `NORNS_URL`. */
|
|
303
|
+
url?: string;
|
|
304
|
+
/** The tenant's bearer token. Defaults to `NORNS_API_KEY`. */
|
|
305
|
+
apiKey?: string;
|
|
306
|
+
/**
|
|
307
|
+
* How a model name on an LLM task becomes a model to call: a function, a
|
|
308
|
+
* provider (`anthropic`), or a registry from `createProviderRegistry()`.
|
|
309
|
+
* By default the provider is inferred from the name and its `@ai-sdk/*`
|
|
310
|
+
* package loaded; API keys come from the provider's own env var.
|
|
311
|
+
*/
|
|
312
|
+
models?: ModelsOption;
|
|
313
|
+
/** What this worker serves. Default both; `["tools"]` for a worker with no LLM access. */
|
|
314
|
+
capabilities?: Capability[];
|
|
315
|
+
/** A stable identity. Defaults to `NORNS_WORKER_ID`, else a random one. */
|
|
316
|
+
workerId?: string;
|
|
317
|
+
/** Claim a gard (defaults to `NORNS_GARD`); needs `claimToken`. */
|
|
318
|
+
gard?: string | number;
|
|
319
|
+
/** Defaults to `NORNS_GARD_CLAIM_TOKEN`. */
|
|
320
|
+
claimToken?: string;
|
|
321
|
+
/** How long `stop()` waits for in-flight tasks. Default 30 s, or `NORNS_SHUTDOWN_TIMEOUT` (seconds). */
|
|
322
|
+
shutdownTimeoutMs?: number;
|
|
323
|
+
/** Drain on SIGINT/SIGTERM. Default true. */
|
|
324
|
+
handleSignals?: boolean;
|
|
325
|
+
/** Where to log. `null` for silence. Default: the console. */
|
|
326
|
+
logger?: Logger | null;
|
|
327
|
+
/**
|
|
328
|
+
* Where completed side-effecting calls are remembered across restarts.
|
|
329
|
+
* Defaults to `NORNS_STATE_DIR`, else the system temp dir; `null` disables.
|
|
330
|
+
*/
|
|
331
|
+
stateDir?: string | null;
|
|
332
|
+
/** Replace the global `fetch` (used to register the agents). */
|
|
333
|
+
fetch?: typeof globalThis.fetch;
|
|
334
|
+
/** Replace the global `WebSocket`. */
|
|
335
|
+
transport?: WebSocketConstructor;
|
|
336
|
+
/** Extra options for the Phoenix socket. */
|
|
337
|
+
socketOptions?: Partial<SocketConnectOption>;
|
|
338
|
+
}
|
|
339
|
+
interface RegisterPortOptions {
|
|
340
|
+
name?: string;
|
|
341
|
+
url?: string;
|
|
342
|
+
protocol?: string;
|
|
343
|
+
}
|
|
344
|
+
interface RegisteredPort {
|
|
345
|
+
id: number;
|
|
346
|
+
internalPort: number;
|
|
347
|
+
url: string | null;
|
|
348
|
+
}
|
|
349
|
+
type WorkerState = "idle" | "starting" | "running" | "draining" | "stopped";
|
|
350
|
+
/**
|
|
351
|
+
* Serves agents: connects to Norns, registers their tools, and handles the
|
|
352
|
+
* LLM and tool tasks it is sent.
|
|
353
|
+
*
|
|
354
|
+
* const worker = new NornsWorker({ agents: [supportBot] });
|
|
355
|
+
* await worker.start(); // resolves once joined; Ctrl-C drains and exits
|
|
356
|
+
*
|
|
357
|
+
* `start()` resolves when the worker has joined, so the rest of your program
|
|
358
|
+
* can carry on; the open socket keeps the process alive. `stop()` drains:
|
|
359
|
+
* Norns stops sending work, in-flight tasks finish and report, and the
|
|
360
|
+
* worker leaves. `stopped` settles when that is done.
|
|
361
|
+
*/
|
|
362
|
+
declare class NornsWorker {
|
|
363
|
+
#private;
|
|
364
|
+
readonly url: string;
|
|
365
|
+
readonly workerId: string;
|
|
366
|
+
readonly gard: string | number | undefined;
|
|
367
|
+
constructor(options: NornsWorkerOptions);
|
|
368
|
+
/** The agents this worker serves. */
|
|
369
|
+
get agents(): readonly AgentDefinition[];
|
|
370
|
+
get state(): WorkerState;
|
|
371
|
+
/**
|
|
372
|
+
* Settles once the worker has stopped: resolves after a drain, rejects
|
|
373
|
+
* with `JoinError` or `GardDestroyedError` when it was forced off.
|
|
374
|
+
*/
|
|
375
|
+
get stopped(): Promise<void>;
|
|
376
|
+
/**
|
|
377
|
+
* Register the agents, connect, and join. Resolves once Norns has accepted
|
|
378
|
+
* the worker; rejects with `JoinError` when it never will (bad claim
|
|
379
|
+
* token, destroyed gard). A server that is down is retried until it is up.
|
|
380
|
+
*/
|
|
381
|
+
start(): Promise<void>;
|
|
382
|
+
/**
|
|
383
|
+
* Drain and stop: tell Norns to send no more work, finish the tasks in
|
|
384
|
+
* flight (up to the shutdown timeout), report them, leave. Tasks still
|
|
385
|
+
* running at the deadline are dropped and Norns re-dispatches them.
|
|
386
|
+
*/
|
|
387
|
+
stop(): Promise<void>;
|
|
388
|
+
/**
|
|
389
|
+
* Expose a service port for the dashboard. Only in a gard: the gard is
|
|
390
|
+
* inferred from the connection. Call it from a tool after starting a
|
|
391
|
+
* service.
|
|
392
|
+
*/
|
|
393
|
+
registerPort(internalPort: number, options?: RegisterPortOptions): Promise<RegisteredPort>;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* What a tool's `execute` is told about the call, besides its input.
|
|
398
|
+
*
|
|
399
|
+
* `signal` aborts when the worker gives up on the call: the socket dropped
|
|
400
|
+
* (Norns re-dispatches the task) or a drain hit its deadline. A long tool
|
|
401
|
+
* should pass it to whatever it waits on.
|
|
402
|
+
*/
|
|
403
|
+
interface ToolContext {
|
|
404
|
+
taskId: string;
|
|
405
|
+
runId: number | undefined;
|
|
406
|
+
agentId: number | undefined;
|
|
407
|
+
/**
|
|
408
|
+
* Set when Norns considers the call side-effecting. Same run, same step,
|
|
409
|
+
* same tool call, same key, however many times it is dispatched; hand it
|
|
410
|
+
* to a provider that dedupes (most payment APIs take one) and the effect
|
|
411
|
+
* survives this worker dying mid-call too.
|
|
412
|
+
*/
|
|
413
|
+
idempotencyKey: string | undefined;
|
|
414
|
+
signal: AbortSignal;
|
|
415
|
+
worker: NornsWorker;
|
|
416
|
+
}
|
|
417
|
+
/** A Zod schema, any Standard Schema, an `ai` `Schema`, or raw JSON Schema. */
|
|
418
|
+
type ToolInputSchema = FlexibleSchema | JSONSchema7;
|
|
419
|
+
/** The argument type `execute` receives for a given `input` schema. */
|
|
420
|
+
type InferToolInput<S> = S extends FlexibleSchema<infer T> ? T : Record<string, unknown>;
|
|
421
|
+
interface ToolOptions<S extends ToolInputSchema = ToolInputSchema> {
|
|
422
|
+
/** What the model calls it by. */
|
|
423
|
+
name: string;
|
|
424
|
+
/** What the model reads to decide when to call it. */
|
|
425
|
+
description: string;
|
|
426
|
+
/**
|
|
427
|
+
* The input shape. A Zod object schema gives a typed, validated `execute`;
|
|
428
|
+
* leave it out for a tool that takes nothing.
|
|
429
|
+
*/
|
|
430
|
+
input?: S;
|
|
431
|
+
/**
|
|
432
|
+
* Mark a tool whose call must not happen twice. Norns then names each call
|
|
433
|
+
* with an idempotency key, and a re-dispatched call is answered from the
|
|
434
|
+
* result this worker kept rather than run again.
|
|
435
|
+
*/
|
|
436
|
+
sideEffect?: boolean;
|
|
437
|
+
/**
|
|
438
|
+
* The handler. Return a string, or anything JSON-serialisable (it is
|
|
439
|
+
* stringified for the model). Throwing reports an error result; the model
|
|
440
|
+
* sees the message.
|
|
441
|
+
*/
|
|
442
|
+
execute: (input: InferToolInput<S>, context: ToolContext) => unknown | Promise<unknown>;
|
|
443
|
+
}
|
|
444
|
+
type ValidationResult<T> = {
|
|
445
|
+
success: true;
|
|
446
|
+
value: T;
|
|
447
|
+
} | {
|
|
448
|
+
success: false;
|
|
449
|
+
error: Error;
|
|
450
|
+
};
|
|
451
|
+
/** A tool ready to register with a worker: what `tool()` returns. */
|
|
452
|
+
interface ToolDefinition<I = unknown> {
|
|
453
|
+
readonly name: string;
|
|
454
|
+
readonly description: string;
|
|
455
|
+
readonly inputSchema: JSONSchema7;
|
|
456
|
+
readonly sideEffect: boolean;
|
|
457
|
+
/** Validates and coerces an input against the schema, when there is one. */
|
|
458
|
+
readonly validate: ((value: unknown) => Promise<ValidationResult<I>>) | undefined;
|
|
459
|
+
execute(input: I, context: ToolContext): Promise<unknown>;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Define a tool.
|
|
463
|
+
*
|
|
464
|
+
* const searchDocs = tool({
|
|
465
|
+
* name: "search_docs",
|
|
466
|
+
* description: "Search product documentation.",
|
|
467
|
+
* input: z.object({ query: z.string() }),
|
|
468
|
+
* execute: async ({ query }) => db.search(query),
|
|
469
|
+
* });
|
|
470
|
+
*/
|
|
471
|
+
declare function tool<S extends ToolInputSchema>(options: ToolOptions<S>): ToolDefinition<InferToolInput<S>>;
|
|
472
|
+
|
|
473
|
+
type AgentMode = "task" | "conversation";
|
|
474
|
+
type CheckpointPolicy = "every_step" | "on_tool_call" | "manual";
|
|
475
|
+
type ContextStrategy = "sliding_window" | "none";
|
|
476
|
+
type OnFailure = "stop" | "retry_last_step";
|
|
477
|
+
type SubagentConversation = "per_launch" | "per_parent";
|
|
478
|
+
/** Fold older history into a summary once a response reports `compactAt` input tokens. */
|
|
479
|
+
interface ContextPolicy {
|
|
480
|
+
compactAt: number;
|
|
481
|
+
/** Messages kept verbatim after the fold. */
|
|
482
|
+
keep: number;
|
|
483
|
+
}
|
|
484
|
+
/** What this agent may do with `launch_agent` and `list_agents`. */
|
|
485
|
+
interface SubagentPolicy {
|
|
486
|
+
mode?: "open" | "allowlist" | "disabled";
|
|
487
|
+
allowedAgents?: string[];
|
|
488
|
+
allowListAgents?: boolean;
|
|
489
|
+
maxDepth?: number;
|
|
490
|
+
}
|
|
491
|
+
interface AgentOptions {
|
|
492
|
+
name: string;
|
|
493
|
+
/** Shown to other agents by `list_agents`. */
|
|
494
|
+
purpose?: string;
|
|
495
|
+
/**
|
|
496
|
+
* The model name as the provider knows it, e.g. `"claude-sonnet-5"`,
|
|
497
|
+
* `"gpt-4o"`. Prefix it (`"openai/gpt-4o"`) to pin the provider.
|
|
498
|
+
*/
|
|
499
|
+
model?: string;
|
|
500
|
+
systemPrompt?: string;
|
|
501
|
+
/** What this worker serves for the agent (see `allowedTools` for what it is offered). */
|
|
502
|
+
tools?: ToolDefinition[];
|
|
503
|
+
mode?: AgentMode;
|
|
504
|
+
checkpointPolicy?: CheckpointPolicy;
|
|
505
|
+
contextStrategy?: ContextStrategy;
|
|
506
|
+
contextWindow?: number;
|
|
507
|
+
maxSteps?: number;
|
|
508
|
+
onFailure?: OnFailure;
|
|
509
|
+
/** Pair with `contextStrategy: "none"`; the summarisation is served by this worker. */
|
|
510
|
+
contextPolicy?: ContextPolicy;
|
|
511
|
+
/**
|
|
512
|
+
* The ceiling on one response, not on the history. Unset leaves it to the
|
|
513
|
+
* worker (8192). Raise it for an agent whose turns are long.
|
|
514
|
+
*/
|
|
515
|
+
maxTokens?: number;
|
|
516
|
+
/**
|
|
517
|
+
* Names from `tools` this agent is offered. Unset offers every tool
|
|
518
|
+
* registered in the tenant, other agents' included. Built-ins (`ask_human`,
|
|
519
|
+
* `wait`, `launch_agent`, `list_agents`) are never filtered.
|
|
520
|
+
*/
|
|
521
|
+
allowedTools?: string[];
|
|
522
|
+
subagents?: SubagentPolicy;
|
|
523
|
+
/** When launched by another agent: a fresh conversation per launch, or one per parent. */
|
|
524
|
+
subagentConversation?: SubagentConversation;
|
|
525
|
+
}
|
|
526
|
+
/** A complete agent definition: `defineAgent()`'s result. */
|
|
527
|
+
interface AgentDefinition {
|
|
528
|
+
readonly name: string;
|
|
529
|
+
readonly purpose: string | undefined;
|
|
530
|
+
readonly model: string;
|
|
531
|
+
readonly systemPrompt: string;
|
|
532
|
+
readonly tools: readonly ToolDefinition[];
|
|
533
|
+
readonly mode: AgentMode;
|
|
534
|
+
readonly checkpointPolicy: CheckpointPolicy;
|
|
535
|
+
readonly contextStrategy: ContextStrategy;
|
|
536
|
+
readonly contextWindow: number;
|
|
537
|
+
readonly maxSteps: number;
|
|
538
|
+
readonly onFailure: OnFailure;
|
|
539
|
+
readonly contextPolicy: ContextPolicy | undefined;
|
|
540
|
+
readonly maxTokens: number | undefined;
|
|
541
|
+
readonly allowedTools: readonly string[] | undefined;
|
|
542
|
+
readonly subagents: SubagentPolicy | undefined;
|
|
543
|
+
readonly subagentConversation: SubagentConversation | undefined;
|
|
544
|
+
}
|
|
545
|
+
declare const DEFAULT_MODEL = "claude-sonnet-5";
|
|
546
|
+
/**
|
|
547
|
+
* Define an agent. Defaults are filled in and the combination is checked
|
|
548
|
+
* here, so a mistake fails where it was written rather than on the server.
|
|
549
|
+
*/
|
|
550
|
+
declare function defineAgent(options: AgentOptions): AgentDefinition;
|
|
551
|
+
|
|
552
|
+
/** Base class for every error this SDK raises. */
|
|
553
|
+
declare class NornsError extends Error {
|
|
554
|
+
constructor(message: string, options?: {
|
|
555
|
+
cause?: unknown;
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
/** The Norns API answered with a non-2xx status. */
|
|
559
|
+
declare class NornsApiError extends NornsError {
|
|
560
|
+
readonly status: number;
|
|
561
|
+
readonly body: unknown;
|
|
562
|
+
constructor(status: number, body: unknown, message?: string);
|
|
563
|
+
}
|
|
564
|
+
/** The thing asked for does not exist (404). */
|
|
565
|
+
declare class NotFoundError extends NornsApiError {
|
|
566
|
+
constructor(what: string, body?: unknown);
|
|
567
|
+
}
|
|
568
|
+
/** A wait or a stream ran out of time. */
|
|
569
|
+
declare class NornsTimeoutError extends NornsError {
|
|
570
|
+
constructor(message: string);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* The worker's channel join was rejected for a reason a retry cannot fix:
|
|
574
|
+
* a bad claim token, a destroyed or missing gard, an invalid registration.
|
|
575
|
+
*/
|
|
576
|
+
declare class JoinError extends NornsError {
|
|
577
|
+
readonly reason: string;
|
|
578
|
+
readonly code: string | undefined;
|
|
579
|
+
constructor(reason: string, code?: string);
|
|
580
|
+
}
|
|
581
|
+
/** The gard this worker claimed was destroyed while it was connected. */
|
|
582
|
+
declare class GardDestroyedError extends NornsError {
|
|
583
|
+
constructor(gard: string | number | undefined);
|
|
584
|
+
}
|
|
585
|
+
/** A stream's socket closed before the run reached a terminal event. */
|
|
586
|
+
declare class StreamClosedError extends NornsError {
|
|
587
|
+
readonly reason: unknown;
|
|
588
|
+
constructor(reason: unknown);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* The provider-neutral wire format Norns speaks, and its translation to and
|
|
593
|
+
* from the AI SDK. Also the prose the worker writes for the model: Norns
|
|
594
|
+
* never does ("Content is opaque"), so composing the system prompt,
|
|
595
|
+
* rendering kinded messages, eliding old tool results, and choosing the
|
|
596
|
+
* run's final output all happen here.
|
|
597
|
+
*/
|
|
598
|
+
|
|
599
|
+
interface NeutralToolCall {
|
|
600
|
+
id: string;
|
|
601
|
+
name: string;
|
|
602
|
+
arguments: unknown;
|
|
603
|
+
}
|
|
604
|
+
interface NeutralMessage {
|
|
605
|
+
role: "user" | "assistant" | "tool" | "system";
|
|
606
|
+
content?: unknown;
|
|
607
|
+
tool_calls?: NeutralToolCall[];
|
|
608
|
+
tool_call_id?: string;
|
|
609
|
+
name?: string;
|
|
610
|
+
is_error?: boolean;
|
|
611
|
+
/** Set on messages Norns resolved itself; the worker renders them. */
|
|
612
|
+
kind?: string;
|
|
613
|
+
data?: Record<string, unknown>;
|
|
614
|
+
}
|
|
615
|
+
interface NeutralTool {
|
|
616
|
+
name: string;
|
|
617
|
+
description?: string;
|
|
618
|
+
parameters?: JSONSchema7;
|
|
619
|
+
input_schema?: JSONSchema7;
|
|
620
|
+
}
|
|
621
|
+
interface LlmTask {
|
|
622
|
+
task_id: string;
|
|
623
|
+
purpose?: "compact";
|
|
624
|
+
model?: string;
|
|
625
|
+
system_prompt?: string | null;
|
|
626
|
+
summary?: string | null;
|
|
627
|
+
date?: string | null;
|
|
628
|
+
messages?: NeutralMessage[];
|
|
629
|
+
/** In place of `messages` once this worker holds the run's history (see HistoryCache). */
|
|
630
|
+
messages_delta?: {
|
|
631
|
+
base_count: number;
|
|
632
|
+
drop: number;
|
|
633
|
+
append: NeutralMessage[];
|
|
634
|
+
};
|
|
635
|
+
tools?: NeutralTool[];
|
|
636
|
+
context_policy?: unknown;
|
|
637
|
+
max_tokens?: number | null;
|
|
638
|
+
agent_id?: number;
|
|
639
|
+
run_id?: number;
|
|
640
|
+
step?: number;
|
|
641
|
+
}
|
|
642
|
+
interface ToolTask {
|
|
643
|
+
task_id: string;
|
|
644
|
+
tool_name: string;
|
|
645
|
+
input?: Record<string, unknown>;
|
|
646
|
+
agent_id?: number;
|
|
647
|
+
run_id?: number;
|
|
648
|
+
idempotency_key?: string;
|
|
649
|
+
}
|
|
650
|
+
interface Usage {
|
|
651
|
+
input_tokens: number;
|
|
652
|
+
output_tokens: number;
|
|
653
|
+
cache_read_tokens?: number;
|
|
654
|
+
cache_write_tokens?: number;
|
|
655
|
+
}
|
|
656
|
+
type NeutralFinishReason = "stop" | "tool_call" | "length" | "error";
|
|
657
|
+
type LlmResult = {
|
|
658
|
+
status: "ok";
|
|
659
|
+
content: string;
|
|
660
|
+
finish_reason: NeutralFinishReason;
|
|
661
|
+
usage: Usage;
|
|
662
|
+
tool_calls?: NeutralToolCall[];
|
|
663
|
+
model?: string;
|
|
664
|
+
final_output?: string;
|
|
665
|
+
} | {
|
|
666
|
+
status: "error";
|
|
667
|
+
error: string;
|
|
668
|
+
};
|
|
669
|
+
type ToolResult = {
|
|
670
|
+
status: "ok";
|
|
671
|
+
result: string;
|
|
672
|
+
duplicate?: true;
|
|
673
|
+
} | {
|
|
674
|
+
status: "error";
|
|
675
|
+
error: string;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
export { DEFAULT_MODEL, GardDestroyedError, JoinError, NornsApiError, NornsClient, NornsError, NornsTimeoutError, NornsWorker, NotFoundError, StreamClosedError, consoleLogger, defineAgent, inferProvider, silentLogger, tool };
|
|
679
|
+
export type { AgentDefinition, AgentInfo, AgentMode, AgentOptions, AgentRef, Capability, CheckpointPolicy, ContextPolicy, ContextStrategy, Conversation, InferToolInput, ListRunsOptions, LlmResult, LlmTask, Logger, MessageResult, ModelResolver, ModelSource, ModelsOption, NeutralMessage, NeutralToolCall, NornsClientOptions, NornsWorkerOptions, OnFailure, RegisterPortOptions, RegisteredPort, Run, RunEvent, RunStatus, SendMessageOptions, StreamEvent, StreamOptions, SubagentConversation, SubagentPolicy, ToolContext, ToolDefinition, ToolInputSchema, ToolOptions, ToolResult, ToolTask, WaitingFor, WebSocketConstructor };
|