norns-sdk 0.1.0 → 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 CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to `norns-sdk` are documented in this file.
4
4
 
5
+ ## [0.2.0] - 2026-10-16
6
+
7
+ ### Added
8
+ - The worker joins with `features: ["message_cache"]` and keeps the message
9
+ list Norns last sent it per run, so Norns can send later steps as
10
+ `messages_delta` (the change) instead of the whole history. A delta on a
11
+ base the worker no longer holds is answered with `message_cache_miss` and
12
+ Norns resends in full. Older Norns ignores the feature and sends the full
13
+ list as before.
14
+
5
15
  ## [0.1.0] - 2026-10-15
6
16
 
7
17
  ### Added
package/dist/index.cjs CHANGED
@@ -1149,6 +1149,51 @@ function neutralUsage(usage) {
1149
1149
  return out;
1150
1150
  }
1151
1151
 
1152
+ // src/history-cache.ts
1153
+ var HistoryCache = class {
1154
+ #runs = /* @__PURE__ */ new Map();
1155
+ #maxRuns;
1156
+ constructor(maxRuns = 256) {
1157
+ this.#maxRuns = maxRuns;
1158
+ }
1159
+ /** The task with `messages` filled in, or `undefined` on a miss. */
1160
+ resolve(task) {
1161
+ if (task.purpose === "compact") return task;
1162
+ const runId = task.run_id;
1163
+ const delta = task.messages_delta;
1164
+ if (delta === void 0) {
1165
+ if (runId !== void 0 && Array.isArray(task.messages)) this.#remember(runId, task.messages);
1166
+ return task;
1167
+ }
1168
+ const previous = runId === void 0 ? void 0 : this.#runs.get(runId);
1169
+ if (runId === void 0 || previous === void 0 || previous.length !== delta.base_count) {
1170
+ return void 0;
1171
+ }
1172
+ const messages = [...previous.slice(delta.drop), ...delta.append];
1173
+ this.#remember(runId, messages);
1174
+ const resolved = { ...task, messages };
1175
+ delete resolved.messages_delta;
1176
+ return resolved;
1177
+ }
1178
+ /** Forget everything: a new connection gets the full history anyway. */
1179
+ clear() {
1180
+ this.#runs.clear();
1181
+ }
1182
+ /** How many runs are held; for tests. */
1183
+ get size() {
1184
+ return this.#runs.size;
1185
+ }
1186
+ #remember(runId, messages) {
1187
+ this.#runs.delete(runId);
1188
+ this.#runs.set(runId, messages);
1189
+ while (this.#runs.size > this.#maxRuns) {
1190
+ const oldest = this.#runs.keys().next().value;
1191
+ if (oldest === void 0) break;
1192
+ this.#runs.delete(oldest);
1193
+ }
1194
+ }
1195
+ };
1196
+
1152
1197
  // src/worker.ts
1153
1198
  var TOPIC = "worker:lobby";
1154
1199
  var NornsWorker = class {
@@ -1169,6 +1214,7 @@ var NornsWorker = class {
1169
1214
  #client;
1170
1215
  #resolveModel;
1171
1216
  #inFlight = /* @__PURE__ */ new Set();
1217
+ #history = new HistoryCache();
1172
1218
  #state = "idle";
1173
1219
  #socket;
1174
1220
  #channel;
@@ -1261,7 +1307,10 @@ var NornsWorker = class {
1261
1307
  ...this.#socketOptions
1262
1308
  });
1263
1309
  this.#socket = socket;
1264
- socket.onOpen(() => this.#logger.info(`Connected to ${this.url}`));
1310
+ socket.onOpen(() => {
1311
+ this.#logger.info(`Connected to ${this.url}`);
1312
+ this.#history.clear();
1313
+ });
1265
1314
  socket.onError((err) => {
1266
1315
  if (this.#state === "starting" || this.#state === "running") {
1267
1316
  this.#logger.warn(`Connection error (${errorMessage(err)}); retrying`);
@@ -1320,7 +1369,10 @@ var NornsWorker = class {
1320
1369
  const params = {
1321
1370
  worker_id: this.workerId,
1322
1371
  tools: [...this.#tools.values()].map(toolRegistration),
1323
- capabilities: this.#capabilities
1372
+ capabilities: this.#capabilities,
1373
+ // This worker keeps the history it was last sent per run, so Norns may
1374
+ // send later steps as the change only (HistoryCache).
1375
+ features: ["message_cache"]
1324
1376
  };
1325
1377
  if (this.gard !== void 0) {
1326
1378
  params.gard = this.gard;
@@ -1449,7 +1501,14 @@ var NornsWorker = class {
1449
1501
  (resp) => this.#logger.warn(`Server rejected result for ${taskId}: ${errorMessage(resp)}`)
1450
1502
  );
1451
1503
  }
1452
- async #runLlm(task, signal) {
1504
+ async #runLlm(received, signal) {
1505
+ const task = this.#history.resolve(received);
1506
+ if (task === void 0) {
1507
+ this.#logger.info(
1508
+ "LLM task arrived as a delta on an unknown base; asking for the full history"
1509
+ );
1510
+ return { status: "error", error: "message_cache_miss" };
1511
+ }
1453
1512
  const model = task.model ?? "?";
1454
1513
  const tools = task.tools ?? [];
1455
1514
  this.#logger.info(