norns-sdk 0.1.0 → 0.3.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 +20 -0
- package/README.md +6 -0
- package/dist/index.cjs +88 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +86 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -264,6 +264,12 @@ declare class NornsClient {
|
|
|
264
264
|
delete: (agent: AgentRef, key: string) => Promise<void>;
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* A key issued by `POST /api/v1/api_keys` with a worker scope (`nrnw_...`).
|
|
269
|
+
* It opens the worker socket and nothing else, so a worker holding one must
|
|
270
|
+
* not try the REST API.
|
|
271
|
+
*/
|
|
272
|
+
declare function isScopedKey(apiKey: string): boolean;
|
|
267
273
|
|
|
268
274
|
/**
|
|
269
275
|
* Where the SDK writes what it is doing. Anything with these four methods
|
|
@@ -301,7 +307,11 @@ interface NornsWorkerOptions {
|
|
|
301
307
|
agents: AgentDefinition | AgentDefinition[];
|
|
302
308
|
/** The Norns base URL. Defaults to `NORNS_URL`. */
|
|
303
309
|
url?: string;
|
|
304
|
-
/**
|
|
310
|
+
/**
|
|
311
|
+
* The bearer token. Defaults to `NORNS_API_KEY`. A scoped worker key
|
|
312
|
+
* (`nrnw_...`) joins without registering the agents, since it cannot reach
|
|
313
|
+
* the REST API; register the definitions with a tenant key.
|
|
314
|
+
*/
|
|
305
315
|
apiKey?: string;
|
|
306
316
|
/**
|
|
307
317
|
* How a model name on an LLM task becomes a model to call: a function, a
|
|
@@ -582,6 +592,10 @@ declare class JoinError extends NornsError {
|
|
|
582
592
|
declare class GardDestroyedError extends NornsError {
|
|
583
593
|
constructor(gard: string | number | undefined);
|
|
584
594
|
}
|
|
595
|
+
/** The scoped key this worker joined on was revoked while it was connected. */
|
|
596
|
+
declare class KeyRevokedError extends NornsError {
|
|
597
|
+
constructor();
|
|
598
|
+
}
|
|
585
599
|
/** A stream's socket closed before the run reached a terminal event. */
|
|
586
600
|
declare class StreamClosedError extends NornsError {
|
|
587
601
|
readonly reason: unknown;
|
|
@@ -626,6 +640,12 @@ interface LlmTask {
|
|
|
626
640
|
summary?: string | null;
|
|
627
641
|
date?: string | null;
|
|
628
642
|
messages?: NeutralMessage[];
|
|
643
|
+
/** In place of `messages` once this worker holds the run's history (see HistoryCache). */
|
|
644
|
+
messages_delta?: {
|
|
645
|
+
base_count: number;
|
|
646
|
+
drop: number;
|
|
647
|
+
append: NeutralMessage[];
|
|
648
|
+
};
|
|
629
649
|
tools?: NeutralTool[];
|
|
630
650
|
context_policy?: unknown;
|
|
631
651
|
max_tokens?: number | null;
|
|
@@ -669,5 +689,5 @@ type ToolResult = {
|
|
|
669
689
|
error: string;
|
|
670
690
|
};
|
|
671
691
|
|
|
672
|
-
export { DEFAULT_MODEL, GardDestroyedError, JoinError, NornsApiError, NornsClient, NornsError, NornsTimeoutError, NornsWorker, NotFoundError, StreamClosedError, consoleLogger, defineAgent, inferProvider, silentLogger, tool };
|
|
692
|
+
export { DEFAULT_MODEL, GardDestroyedError, JoinError, KeyRevokedError, NornsApiError, NornsClient, NornsError, NornsTimeoutError, NornsWorker, NotFoundError, StreamClosedError, consoleLogger, defineAgent, inferProvider, isScopedKey, silentLogger, tool };
|
|
673
693
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -264,6 +264,12 @@ declare class NornsClient {
|
|
|
264
264
|
delete: (agent: AgentRef, key: string) => Promise<void>;
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* A key issued by `POST /api/v1/api_keys` with a worker scope (`nrnw_...`).
|
|
269
|
+
* It opens the worker socket and nothing else, so a worker holding one must
|
|
270
|
+
* not try the REST API.
|
|
271
|
+
*/
|
|
272
|
+
declare function isScopedKey(apiKey: string): boolean;
|
|
267
273
|
|
|
268
274
|
/**
|
|
269
275
|
* Where the SDK writes what it is doing. Anything with these four methods
|
|
@@ -301,7 +307,11 @@ interface NornsWorkerOptions {
|
|
|
301
307
|
agents: AgentDefinition | AgentDefinition[];
|
|
302
308
|
/** The Norns base URL. Defaults to `NORNS_URL`. */
|
|
303
309
|
url?: string;
|
|
304
|
-
/**
|
|
310
|
+
/**
|
|
311
|
+
* The bearer token. Defaults to `NORNS_API_KEY`. A scoped worker key
|
|
312
|
+
* (`nrnw_...`) joins without registering the agents, since it cannot reach
|
|
313
|
+
* the REST API; register the definitions with a tenant key.
|
|
314
|
+
*/
|
|
305
315
|
apiKey?: string;
|
|
306
316
|
/**
|
|
307
317
|
* How a model name on an LLM task becomes a model to call: a function, a
|
|
@@ -582,6 +592,10 @@ declare class JoinError extends NornsError {
|
|
|
582
592
|
declare class GardDestroyedError extends NornsError {
|
|
583
593
|
constructor(gard: string | number | undefined);
|
|
584
594
|
}
|
|
595
|
+
/** The scoped key this worker joined on was revoked while it was connected. */
|
|
596
|
+
declare class KeyRevokedError extends NornsError {
|
|
597
|
+
constructor();
|
|
598
|
+
}
|
|
585
599
|
/** A stream's socket closed before the run reached a terminal event. */
|
|
586
600
|
declare class StreamClosedError extends NornsError {
|
|
587
601
|
readonly reason: unknown;
|
|
@@ -626,6 +640,12 @@ interface LlmTask {
|
|
|
626
640
|
summary?: string | null;
|
|
627
641
|
date?: string | null;
|
|
628
642
|
messages?: NeutralMessage[];
|
|
643
|
+
/** In place of `messages` once this worker holds the run's history (see HistoryCache). */
|
|
644
|
+
messages_delta?: {
|
|
645
|
+
base_count: number;
|
|
646
|
+
drop: number;
|
|
647
|
+
append: NeutralMessage[];
|
|
648
|
+
};
|
|
629
649
|
tools?: NeutralTool[];
|
|
630
650
|
context_policy?: unknown;
|
|
631
651
|
max_tokens?: number | null;
|
|
@@ -669,5 +689,5 @@ type ToolResult = {
|
|
|
669
689
|
error: string;
|
|
670
690
|
};
|
|
671
691
|
|
|
672
|
-
export { DEFAULT_MODEL, GardDestroyedError, JoinError, NornsApiError, NornsClient, NornsError, NornsTimeoutError, NornsWorker, NotFoundError, StreamClosedError, consoleLogger, defineAgent, inferProvider, silentLogger, tool };
|
|
692
|
+
export { DEFAULT_MODEL, GardDestroyedError, JoinError, KeyRevokedError, NornsApiError, NornsClient, NornsError, NornsTimeoutError, NornsWorker, NotFoundError, StreamClosedError, consoleLogger, defineAgent, inferProvider, isScopedKey, silentLogger, tool };
|
|
673
693
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -206,6 +206,12 @@ var GardDestroyedError = class extends NornsError {
|
|
|
206
206
|
this.name = "GardDestroyedError";
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
|
+
var KeyRevokedError = class extends NornsError {
|
|
210
|
+
constructor() {
|
|
211
|
+
super("the worker's scoped key was revoked");
|
|
212
|
+
this.name = "KeyRevokedError";
|
|
213
|
+
}
|
|
214
|
+
};
|
|
209
215
|
var StreamClosedError = class extends NornsError {
|
|
210
216
|
reason;
|
|
211
217
|
constructor(reason) {
|
|
@@ -534,6 +540,9 @@ function resolveApiKey(apiKey) {
|
|
|
534
540
|
if (!value) throw new NornsError("Norns API key missing: pass `apiKey` or set NORNS_API_KEY");
|
|
535
541
|
return value;
|
|
536
542
|
}
|
|
543
|
+
function isScopedKey(apiKey) {
|
|
544
|
+
return apiKey.startsWith("nrnw_");
|
|
545
|
+
}
|
|
537
546
|
function wsBase(url) {
|
|
538
547
|
return url.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://");
|
|
539
548
|
}
|
|
@@ -1113,6 +1122,51 @@ function neutralUsage(usage) {
|
|
|
1113
1122
|
return out;
|
|
1114
1123
|
}
|
|
1115
1124
|
|
|
1125
|
+
// src/history-cache.ts
|
|
1126
|
+
var HistoryCache = class {
|
|
1127
|
+
#runs = /* @__PURE__ */ new Map();
|
|
1128
|
+
#maxRuns;
|
|
1129
|
+
constructor(maxRuns = 256) {
|
|
1130
|
+
this.#maxRuns = maxRuns;
|
|
1131
|
+
}
|
|
1132
|
+
/** The task with `messages` filled in, or `undefined` on a miss. */
|
|
1133
|
+
resolve(task) {
|
|
1134
|
+
if (task.purpose === "compact") return task;
|
|
1135
|
+
const runId = task.run_id;
|
|
1136
|
+
const delta = task.messages_delta;
|
|
1137
|
+
if (delta === void 0) {
|
|
1138
|
+
if (runId !== void 0 && Array.isArray(task.messages)) this.#remember(runId, task.messages);
|
|
1139
|
+
return task;
|
|
1140
|
+
}
|
|
1141
|
+
const previous = runId === void 0 ? void 0 : this.#runs.get(runId);
|
|
1142
|
+
if (runId === void 0 || previous === void 0 || previous.length !== delta.base_count) {
|
|
1143
|
+
return void 0;
|
|
1144
|
+
}
|
|
1145
|
+
const messages = [...previous.slice(delta.drop), ...delta.append];
|
|
1146
|
+
this.#remember(runId, messages);
|
|
1147
|
+
const resolved = { ...task, messages };
|
|
1148
|
+
delete resolved.messages_delta;
|
|
1149
|
+
return resolved;
|
|
1150
|
+
}
|
|
1151
|
+
/** Forget everything: a new connection gets the full history anyway. */
|
|
1152
|
+
clear() {
|
|
1153
|
+
this.#runs.clear();
|
|
1154
|
+
}
|
|
1155
|
+
/** How many runs are held; for tests. */
|
|
1156
|
+
get size() {
|
|
1157
|
+
return this.#runs.size;
|
|
1158
|
+
}
|
|
1159
|
+
#remember(runId, messages) {
|
|
1160
|
+
this.#runs.delete(runId);
|
|
1161
|
+
this.#runs.set(runId, messages);
|
|
1162
|
+
while (this.#runs.size > this.#maxRuns) {
|
|
1163
|
+
const oldest = this.#runs.keys().next().value;
|
|
1164
|
+
if (oldest === void 0) break;
|
|
1165
|
+
this.#runs.delete(oldest);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
|
|
1116
1170
|
// src/worker.ts
|
|
1117
1171
|
var TOPIC = "worker:lobby";
|
|
1118
1172
|
var NornsWorker = class {
|
|
@@ -1133,6 +1187,7 @@ var NornsWorker = class {
|
|
|
1133
1187
|
#client;
|
|
1134
1188
|
#resolveModel;
|
|
1135
1189
|
#inFlight = /* @__PURE__ */ new Set();
|
|
1190
|
+
#history = new HistoryCache();
|
|
1136
1191
|
#state = "idle";
|
|
1137
1192
|
#socket;
|
|
1138
1193
|
#channel;
|
|
@@ -1201,9 +1256,15 @@ var NornsWorker = class {
|
|
|
1201
1256
|
return this.#starting;
|
|
1202
1257
|
}
|
|
1203
1258
|
async #start() {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1259
|
+
if (isScopedKey(this.#apiKey)) {
|
|
1260
|
+
this.#logger.info(
|
|
1261
|
+
"Worker key is scoped (nrnw_): skipping agent registration. Register the agent definitions with a tenant key."
|
|
1262
|
+
);
|
|
1263
|
+
} else {
|
|
1264
|
+
for (const agent of this.#agents) {
|
|
1265
|
+
const info = await this.#client.agents.ensure(agent);
|
|
1266
|
+
this.#logger.info(`Registered agent '${agent.name}' (id=${info.id})`);
|
|
1267
|
+
}
|
|
1207
1268
|
}
|
|
1208
1269
|
if (this.#capabilities.includes("llm")) {
|
|
1209
1270
|
for (const model of new Set(this.#agents.map((a) => a.model))) {
|
|
@@ -1225,7 +1286,10 @@ var NornsWorker = class {
|
|
|
1225
1286
|
...this.#socketOptions
|
|
1226
1287
|
});
|
|
1227
1288
|
this.#socket = socket;
|
|
1228
|
-
socket.onOpen(() =>
|
|
1289
|
+
socket.onOpen(() => {
|
|
1290
|
+
this.#logger.info(`Connected to ${this.url}`);
|
|
1291
|
+
this.#history.clear();
|
|
1292
|
+
});
|
|
1229
1293
|
socket.onError((err) => {
|
|
1230
1294
|
if (this.#state === "starting" || this.#state === "running") {
|
|
1231
1295
|
this.#logger.warn(`Connection error (${errorMessage(err)}); retrying`);
|
|
@@ -1251,6 +1315,10 @@ var NornsWorker = class {
|
|
|
1251
1315
|
this.#logger.error("This worker's gard was destroyed; shutting down");
|
|
1252
1316
|
this.#fail(new GardDestroyedError(this.gard));
|
|
1253
1317
|
});
|
|
1318
|
+
channel.on("key_revoked", () => {
|
|
1319
|
+
this.#logger.error("This worker's key was revoked; shutting down");
|
|
1320
|
+
this.#fail(new KeyRevokedError());
|
|
1321
|
+
});
|
|
1254
1322
|
channel.onError((reason) => {
|
|
1255
1323
|
if (this.#state === "running")
|
|
1256
1324
|
this.#logger.warn(`Channel error (${errorMessage(reason)}); rejoining`);
|
|
@@ -1284,7 +1352,10 @@ var NornsWorker = class {
|
|
|
1284
1352
|
const params = {
|
|
1285
1353
|
worker_id: this.workerId,
|
|
1286
1354
|
tools: [...this.#tools.values()].map(toolRegistration),
|
|
1287
|
-
capabilities: this.#capabilities
|
|
1355
|
+
capabilities: this.#capabilities,
|
|
1356
|
+
// This worker keeps the history it was last sent per run, so Norns may
|
|
1357
|
+
// send later steps as the change only (HistoryCache).
|
|
1358
|
+
features: ["message_cache"]
|
|
1288
1359
|
};
|
|
1289
1360
|
if (this.gard !== void 0) {
|
|
1290
1361
|
params.gard = this.gard;
|
|
@@ -1413,7 +1484,14 @@ var NornsWorker = class {
|
|
|
1413
1484
|
(resp) => this.#logger.warn(`Server rejected result for ${taskId}: ${errorMessage(resp)}`)
|
|
1414
1485
|
);
|
|
1415
1486
|
}
|
|
1416
|
-
async #runLlm(
|
|
1487
|
+
async #runLlm(received, signal) {
|
|
1488
|
+
const task = this.#history.resolve(received);
|
|
1489
|
+
if (task === void 0) {
|
|
1490
|
+
this.#logger.info(
|
|
1491
|
+
"LLM task arrived as a delta on an unknown base; asking for the full history"
|
|
1492
|
+
);
|
|
1493
|
+
return { status: "error", error: "message_cache_miss" };
|
|
1494
|
+
}
|
|
1417
1495
|
const model = task.model ?? "?";
|
|
1418
1496
|
const tools = task.tools ?? [];
|
|
1419
1497
|
this.#logger.info(
|
|
@@ -1553,6 +1631,7 @@ export {
|
|
|
1553
1631
|
DEFAULT_MODEL,
|
|
1554
1632
|
GardDestroyedError,
|
|
1555
1633
|
JoinError,
|
|
1634
|
+
KeyRevokedError,
|
|
1556
1635
|
NornsApiError,
|
|
1557
1636
|
NornsClient,
|
|
1558
1637
|
NornsError,
|
|
@@ -1563,6 +1642,7 @@ export {
|
|
|
1563
1642
|
consoleLogger,
|
|
1564
1643
|
defineAgent,
|
|
1565
1644
|
inferProvider,
|
|
1645
|
+
isScopedKey,
|
|
1566
1646
|
jsonSchema3 as jsonSchema,
|
|
1567
1647
|
silentLogger,
|
|
1568
1648
|
tool
|