@plurnk/plurnk-service 1.12.0 → 1.14.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.
Files changed (82) hide show
  1. package/.env.defaults +4 -6
  2. package/SPEC.md +62 -85
  3. package/dist/build-info.json +1 -1
  4. package/dist/content/matcher.d.ts.map +1 -1
  5. package/dist/content/matcher.js +21 -1
  6. package/dist/content/matcher.js.map +1 -1
  7. package/dist/core/ChannelWrite.d.ts +1 -17
  8. package/dist/core/ChannelWrite.d.ts.map +1 -1
  9. package/dist/core/ChannelWrite.js +5 -5
  10. package/dist/core/ChannelWrite.js.map +1 -1
  11. package/dist/core/CoreSchemeServices.d.ts +1 -0
  12. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  13. package/dist/core/CoreSchemeServices.js +1 -0
  14. package/dist/core/CoreSchemeServices.js.map +1 -1
  15. package/dist/core/Dispatcher.d.ts +3 -4
  16. package/dist/core/Dispatcher.d.ts.map +1 -1
  17. package/dist/core/Dispatcher.js +13 -43
  18. package/dist/core/Dispatcher.js.map +1 -1
  19. package/dist/core/Engine.d.ts +3 -12
  20. package/dist/core/Engine.d.ts.map +1 -1
  21. package/dist/core/Engine.js +103 -45
  22. package/dist/core/Engine.js.map +1 -1
  23. package/dist/core/Engine.sql +2 -0
  24. package/dist/core/PacketBuilder.d.ts.map +1 -1
  25. package/dist/core/PacketBuilder.js +1 -6
  26. package/dist/core/PacketBuilder.js.map +1 -1
  27. package/dist/core/TurnRunner.d.ts.map +1 -1
  28. package/dist/core/TurnRunner.js +26 -5
  29. package/dist/core/TurnRunner.js.map +1 -1
  30. package/dist/core/VectorPump.d.ts +28 -0
  31. package/dist/core/VectorPump.d.ts.map +1 -0
  32. package/dist/core/VectorPump.js +130 -0
  33. package/dist/core/VectorPump.js.map +1 -0
  34. package/dist/core/packet-wire.d.ts +1 -1
  35. package/dist/core/packet-wire.d.ts.map +1 -1
  36. package/dist/core/packet-wire.js +2 -5
  37. package/dist/core/packet-wire.js.map +1 -1
  38. package/dist/core/scheme-types.d.ts +1 -0
  39. package/dist/core/scheme-types.d.ts.map +1 -1
  40. package/dist/digest/Digest.d.ts.map +1 -1
  41. package/dist/digest/Digest.js +69 -16
  42. package/dist/digest/Digest.js.map +1 -1
  43. package/dist/schemes/Log.d.ts.map +1 -1
  44. package/dist/schemes/Log.js +14 -0
  45. package/dist/schemes/Log.js.map +1 -1
  46. package/dist/schemes/Log.sql +3 -0
  47. package/dist/schemes/Worker.d.ts.map +1 -1
  48. package/dist/schemes/Worker.js +0 -2
  49. package/dist/schemes/Worker.js.map +1 -1
  50. package/dist/schemes/_entry-graph.sql +21 -1
  51. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  52. package/dist/schemes/_entry-semantic.js +5 -0
  53. package/dist/schemes/_entry-semantic.js.map +1 -1
  54. package/dist/schemes/_search-index.d.ts +5 -1
  55. package/dist/schemes/_search-index.d.ts.map +1 -1
  56. package/dist/schemes/_search-index.js +66 -24
  57. package/dist/schemes/_search-index.js.map +1 -1
  58. package/dist/server/Daemon.d.ts +1 -1
  59. package/dist/server/Daemon.d.ts.map +1 -1
  60. package/dist/server/Daemon.js +81 -149
  61. package/dist/server/Daemon.js.map +1 -1
  62. package/dist/server/drain.sql +16 -0
  63. package/dist/server/lifecycle-recovery.sql +0 -7
  64. package/dist/server/model-route.d.ts +2 -2
  65. package/dist/server/model-route.d.ts.map +1 -1
  66. package/dist/server/model-route.js +8 -1
  67. package/dist/server/model-route.js.map +1 -1
  68. package/migrations/001_schema.sql +0 -51
  69. package/package.json +37 -37
  70. package/dist/core/BranchReceipt.d.ts +0 -6
  71. package/dist/core/BranchReceipt.d.ts.map +0 -1
  72. package/dist/core/BranchReceipt.js +0 -29
  73. package/dist/core/BranchReceipt.js.map +0 -1
  74. package/dist/core/GitBranch.d.ts +0 -23
  75. package/dist/core/GitBranch.d.ts.map +0 -1
  76. package/dist/core/GitBranch.js +0 -120
  77. package/dist/core/GitBranch.js.map +0 -1
  78. package/dist/server/BranchBatches.d.ts +0 -49
  79. package/dist/server/BranchBatches.d.ts.map +0 -1
  80. package/dist/server/BranchBatches.js +0 -599
  81. package/dist/server/BranchBatches.js.map +0 -1
  82. package/dist/server/branch-batch.sql +0 -156
@@ -15,7 +15,7 @@ import WorkerSettingsReader from "../core/worker-settings.js";
15
15
  import EffectPolicy from "../schemes/EffectPolicy.js";
16
16
  import QuestionTool, { questionRuntimeDecl } from "../schemes/QuestionTool.js";
17
17
  import DrainSupervisor from "./DrainSupervisor.js";
18
- import { parsePath, CapabilityAdmission, Validator, } from "@plurnk/plurnk-contracts";
18
+ import { parsePath, CapabilityAdmission, Validator } from "@plurnk/plurnk-contracts";
19
19
  import LogEntry from "./logEntry.js";
20
20
  import Envelope from "./envelope.js";
21
21
  import ClientInput from "./client-input.js";
@@ -23,12 +23,11 @@ import Turn from "../core/Turn.js";
23
23
  import SkillsFunctionality from "./SkillsFunctionality.js";
24
24
  import MembersFunctionality from "./MembersFunctionality.js";
25
25
  import Fork from "../core/fork.js";
26
- import WorkerName from "../core/WorkerName.js";
27
26
  import WorkerControlAddress from "../core/WorkerControlAddress.js";
28
27
  import LoopLifecycle from "../core/LoopLifecycle.js";
29
28
  import { promptLoopPrefix } from "../core/plurnk-uri.js";
30
29
  import { contentWeight } from "../core/content-weight.js";
31
- import { parseAliasesFromEnv, resolveActiveRoute, UnsupportedReasoningPolicyError, } from "@plurnk/plurnk-providers";
30
+ import { parseAliasesFromEnv, resolveActiveRoute, UnsupportedReasoningPolicyError } from "@plurnk/plurnk-providers";
32
31
  import ProviderInstantiate from "../core/ProviderInstantiate.js";
33
32
  import { resolveLoopRoute } from "./loop-model.js";
34
33
  import LoopPolicyReader from "../core/LoopPolicyReader.js";
@@ -36,7 +35,6 @@ import CapabilityPolicies from "../core/CapabilityPolicies.js";
36
35
  import Results, { OperationFailureError } from "../core/results.js";
37
36
  import WorkspaceGate from "../core/WorkspaceGate.js";
38
37
  import WorkerResidency from "./WorkerResidency.js";
39
- import BranchBatches from "./BranchBatches.js";
40
38
  import Functionality from "./Functionality.js";
41
39
  import { observed, observedSync } from "../observe/spans.js";
42
40
  import { listModelCatalog } from "./model-catalog.js";
@@ -46,7 +44,7 @@ const clientActionFailure = (error) => {
46
44
  console.error("Client action failed outside its operation result contract:", error);
47
45
  return Results.failure("daemon:client", "action-threw", 500, "The client action failed outside its operation result contract.", {}, {
48
46
  stage: "client-action",
49
- retryable: false,
47
+ retryable: false
50
48
  });
51
49
  };
52
50
  const daemonFailure = (owner, code, status, detail, extensions = {}) => new OperationFailureError(Results.failure(owner, code, status, detail, {}, extensions));
@@ -58,7 +56,6 @@ export default class Daemon {
58
56
  #db;
59
57
  #engine;
60
58
  #workspaceGate;
61
- #branchBatches;
62
59
  #lifecycle;
63
60
  #drains;
64
61
  #schemes;
@@ -79,7 +76,7 @@ export default class Daemon {
79
76
  // module (plurnk-agui) subscribes and fans out to its OWN clients; core emits, never owns
80
77
  // client transport or connection state.
81
78
  #eventSubscribers = new Set();
82
- constructor({ db, schemes, mimetypes, provider, nodeModulesPath, skills, }) {
79
+ constructor({ db, schemes, mimetypes, provider, nodeModulesPath, skills }) {
83
80
  this.#db = db;
84
81
  this.#lifecycle = new LoopLifecycle(db);
85
82
  this.#schemes = schemes ?? new SchemeRegistry();
@@ -97,16 +94,16 @@ export default class Daemon {
97
94
  this.#ownsMimetypes = mimetypes === undefined;
98
95
  this.#mimetypes = mimetypes ?? new Mimetypes({
99
96
  defaultMimetype: "text/markdown",
100
- discoverOptions: { cwd: this.#discoveryCwd },
97
+ discoverOptions: { cwd: this.#discoveryCwd }
101
98
  });
102
99
  const bootSpec = resolveActiveRoute();
103
100
  if (this.#provider !== null && bootSpec !== null) {
104
101
  ProviderInstantiate.registerInstance(this.#provider, bootSpec);
105
102
  }
106
103
  this.#workspaceGate = new WorkspaceGate(async (workerId, rootWorkerId) => {
107
- const row = await this.#db.branch_batch_worker_lineage.get({
104
+ const row = await this.#db.worker_lineage_contains.get({
108
105
  worker_id: workerId,
109
- root_worker_id: rootWorkerId,
106
+ root_worker_id: rootWorkerId
110
107
  });
111
108
  return row !== undefined;
112
109
  });
@@ -116,7 +113,7 @@ export default class Daemon {
116
113
  db,
117
114
  engine: () => this.#engine,
118
115
  workspaceGate: this.#workspaceGate,
119
- normalizeRuntime: (registration) => this.#normalizeRuntime(registration),
116
+ normalizeRuntime: (registration) => this.#normalizeRuntime(registration)
120
117
  });
121
118
  // {§functionality-coordinator} — the one owner above every family
122
119
  // adapter; Core hosts it, modules register adapters through the seam.
@@ -125,7 +122,7 @@ export default class Daemon {
125
122
  registerWorkerCapabilityProvider: (owner, provider) => this.registerWorkerCapabilityProvider(owner, provider),
126
123
  readWorkerModuleState: (workerId, owner) => this.readWorkerModuleState(workerId, owner),
127
124
  replaceWorkerCapabilities: (replacement, options) => this.replaceWorkerCapabilities(replacement, options),
128
- retainWorker: (workerId) => this.#residency.retain(workerId),
125
+ retainWorker: (workerId) => this.#residency.retain(workerId)
129
126
  });
130
127
  // {§skills-functionality} — Core's own family: standard Agent Skills.
131
128
  this.#skills = new SkillsFunctionality({ db, ...skills });
@@ -133,64 +130,6 @@ export default class Daemon {
133
130
  // {§members-functionality} — Core's own family: file membership on the same surface.
134
131
  this.#members = new MembersFunctionality({ db, engine: () => this.#engine });
135
132
  this.#functionality.register(this.#members);
136
- this.#branchBatches = new BranchBatches(db, this.#workspaceGate, {
137
- settleWorkspace: async (workspaceId) => this.#engine.drainWorkspaceDerivations(workspaceId),
138
- createChild: async ({ workspaceId, parentWorkerId, parentLoopId, op, name, prompt, policy, origin }) => {
139
- const parentPolicy = await this.#providerPolicyForLoop(parentLoopId);
140
- const providerSpec = parentPolicy.childProviderSpec ?? parentPolicy.providerSpec;
141
- const workerName = WorkerName.assert(name);
142
- const capabilityBound = await CapabilityPolicies.delegationBound(this.#db, workspaceId, parentWorkerId, policy);
143
- const workerId = op === "FORK"
144
- ? await Fork.fork(this.#db, parentWorkerId, workerName, capabilityBound, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, parentWorkerId))
145
- : (await this.#db.fork_insert_worker.get({
146
- workspace_id: workspaceId,
147
- name: workerName,
148
- parent_worker_id: parentWorkerId,
149
- origin,
150
- fork_snapshot: 0,
151
- capability_bound: JSON.stringify(capabilityBound),
152
- }))?.id;
153
- if (workerId === undefined)
154
- throw new Error("Branch worker insert returned no row");
155
- // {§worker-model-selection} — lineage inheritance by value: the branch
156
- // child begins with the spawning loop's effective spawn model.
157
- await this.#db.worker_generation_policy_update.run({
158
- id: workerId,
159
- model_route_id: await routeForSpec(this.#db, providerSpec),
160
- spawn_model_route_id: null,
161
- reasoning_policy: parentPolicy.reasoningPolicy,
162
- });
163
- const source = await this.#workerPromptSource(workspaceId, parentWorkerId, workerId);
164
- const loopId = await this.#drains.enqueueFreshLoop({
165
- workerId,
166
- prompt,
167
- ...(source === undefined ? {} : { source }),
168
- providerSpec,
169
- reasoningPolicy: parentPolicy.reasoningPolicy,
170
- childProviderSpec: parentPolicy.childProviderSpec,
171
- policy,
172
- });
173
- return { workerId, loopId };
174
- },
175
- startChild: async (workspaceId, workerId, loopId) => {
176
- const systemPrompt = await readFile(Paths.instructionsSystem, "utf8");
177
- const started = await this.#drains.ensureDrain({ workspaceId, workerId, systemPrompt });
178
- if (started === null)
179
- throw new Error(`Branch worker ${workerId} already has a live drain`);
180
- const result = await started.firstLoopPromise;
181
- if (result.loopId !== loopId) {
182
- throw new Error(`Branch worker ${workerId} drained loop ${result.loopId}, expected ${loopId}`);
183
- }
184
- return result.result;
185
- },
186
- wakeParent: async (workspaceId, workerId) => {
187
- const systemPrompt = await readFile(Paths.instructionsSystem, "utf8");
188
- await this.#drains.settleCompletionWake(workspaceId, workerId, systemPrompt, false);
189
- },
190
- notify: (workspaceId, payload) => {
191
- this.#broadcast({ workspaceId }, "workspace/branch-batch", payload);
192
- },
193
- });
194
133
  this.#engine = new Engine({
195
134
  db, schemes: this.#schemes, mimetypes: this.#mimetypes,
196
135
  // {§tokenomics-agnostic-ruler} — stored and catalog curation weights
@@ -237,7 +176,7 @@ export default class Daemon {
237
176
  id: workerId,
238
177
  model_route_id: await routeForSpec(this.#db, providerSpec),
239
178
  spawn_model_route_id: null,
240
- reasoning_policy: reasoningPolicy,
179
+ reasoning_policy: reasoningPolicy
241
180
  });
242
181
  }
243
182
  const { action, loopId } = await this.inject({
@@ -249,12 +188,10 @@ export default class Daemon {
249
188
  reasoningPolicy,
250
189
  childProviderSpec,
251
190
  systemPrompt,
252
- ...(freshLoopPolicy === undefined ? {} : { freshLoopPolicy }),
191
+ ...(freshLoopPolicy === undefined ? {} : { freshLoopPolicy })
253
192
  });
254
193
  return { action, loopId };
255
194
  },
256
- branchWorker: async (args) => this.#branchBatches.enqueue(args),
257
- branchCompletionGate: async (workerId) => this.#branchBatches.completionGate(workerId),
258
195
  acquireWorkspaceTurn: async (workspaceId, workerId) => this.#workspaceGate.acquireTurn(workspaceId, workerId),
259
196
  // {§skills-hotload} — filesystem installers operate out of band.
260
197
  // Republish under the workspace turn gate before packet assembly so
@@ -262,9 +199,6 @@ export default class Daemon {
262
199
  workspaceTurnStarting: async ({ workspaceId, workerId }) => {
263
200
  await this.#skills.refreshIfChanged({ workspaceId, workerId });
264
201
  },
265
- workspaceTurnCompleted: async ({ turnId }) => {
266
- await this.#branchBatches.sealTurn(turnId);
267
- },
268
202
  // worker:// KILL (terminate) — cancel the addressed worker subtree and
269
203
  // tear down its held streams before the operation completes.
270
204
  cancelWorker: async (workerId, reason) => this.#drains.cancelWorkerTree(workerId, reason),
@@ -272,13 +206,13 @@ export default class Daemon {
272
206
  noticeNotify: (workspaceId, payload) => this.notifyNotice(workspaceId, payload),
273
207
  loopPacketNotify: (workspaceId, payload) => {
274
208
  this.#broadcast({ workspaceId }, "loop/packet", payload);
275
- },
209
+ }
276
210
  });
277
211
  this.#drains = new DrainSupervisor({
278
212
  db,
279
213
  lifecycle: this.#lifecycle,
280
214
  injectPrompt: (workerId, prompt, openPaths, source) => this.#engine.inject(workerId, prompt, openPaths, source),
281
- assertInjectionCompatibility: async ({ workerId, loopId, providerSpec, providerSpecExplicit, reasoningPolicy, childProviderSpec, turnCeiling, policy, }) => {
215
+ assertInjectionCompatibility: async ({ workerId, loopId, providerSpec, providerSpecExplicit, reasoningPolicy, childProviderSpec, turnCeiling, policy }) => {
282
216
  await this.#assertFoldPosture(workerId, policy, loopId);
283
217
  // An omitted selector keeps the loop's durable provider; only an
284
218
  // explicit selection is checked against it (a deliberate switch).
@@ -292,7 +226,7 @@ export default class Daemon {
292
226
  await this.#assertLoopMaxTurns(loopId, turnCeiling?.source === "explicit" ? turnCeiling.effective : undefined);
293
227
  },
294
228
  reconcilePrompts: (workerId, endedLoopId) => this.#reconcileOrphanedPrompts(workerId, endedLoopId),
295
- runLoop: async ({ workspaceId, workerId, loopId, maxTurns, prompt, systemPrompt, signal, onSettled, }) => {
229
+ runLoop: async ({ workspaceId, workerId, loopId, maxTurns, prompt, systemPrompt, signal, onSettled }) => {
296
230
  // The drain callback is the final provider/model boundary. Every
297
231
  // admission path, including boot recovery, terminates here.
298
232
  await this.#assertModelWorker(workspaceId, workerId);
@@ -315,7 +249,7 @@ export default class Daemon {
315
249
  { role: "user", content: prompt },
316
250
  ],
317
251
  signal,
318
- onSettled,
252
+ onSettled
319
253
  });
320
254
  }
321
255
  finally {
@@ -336,7 +270,7 @@ export default class Daemon {
336
270
  const entry = await LogEntry.fetchLogEntry(this.#db, logEntryId);
337
271
  this.#broadcast({ workspaceId }, "log/entry", { entry });
338
272
  },
339
- emit: (workspaceId, method, params) => this.#broadcast({ workspaceId }, method, params),
273
+ emit: (workspaceId, method, params) => this.#broadcast({ workspaceId }, method, params)
340
274
  });
341
275
  // Wire proposal-pending events to the loop/proposal WS notification.
342
276
  // Sessionid scopes the broadcast to clients on the same workspace.
@@ -407,7 +341,7 @@ export default class Daemon {
407
341
  throw new OperationFailureError(Results.failure("daemon:provider", "not-configured", 501, "No provider is configured for this worker.", {}, {
408
342
  stage: "provider-selection",
409
343
  recovery: "Select a configured model provider.",
410
- retryable: false,
344
+ retryable: false
411
345
  }));
412
346
  }
413
347
  const { providerSpec: selection, reasoningPolicy } = generationPolicy;
@@ -421,7 +355,7 @@ export default class Daemon {
421
355
  const maxTurns = ceiling < 0 ? requested : (requested < 0 ? ceiling : Math.min(requested, ceiling));
422
356
  const turnCeiling = {
423
357
  effective: maxTurns,
424
- source: requestedMaxTurns === undefined ? "implicit" : "explicit",
358
+ source: requestedMaxTurns === undefined ? "implicit" : "explicit"
425
359
  };
426
360
  const { action, loopId, turnSeq } = await this.inject({
427
361
  workspaceId,
@@ -435,7 +369,7 @@ export default class Daemon {
435
369
  providerSpecExplicit: selectorExplicit,
436
370
  reasoningPolicy,
437
371
  childProviderSpec: childSelection,
438
- systemPrompt,
372
+ systemPrompt
439
373
  });
440
374
  return { status: 100, action, loopId, ...(turnSeq !== undefined ? { turnSeq } : {}) };
441
375
  }
@@ -474,7 +408,7 @@ export default class Daemon {
474
408
  supportedReasoningPolicies: cause.supported,
475
409
  stage: "provider-selection",
476
410
  recovery: "Select one of the provider's supported reasoning policies.",
477
- retryable: false,
411
+ retryable: false
478
412
  });
479
413
  }
480
414
  console.error(`${modelRouteLabel(spec)} could not be instantiated:`, cause);
@@ -483,7 +417,7 @@ export default class Daemon {
483
417
  provider: spec.provider,
484
418
  model: spec.model,
485
419
  stage: "provider-selection",
486
- retryable: false,
420
+ retryable: false
487
421
  });
488
422
  }
489
423
  }
@@ -511,7 +445,7 @@ export default class Daemon {
511
445
  id: workerId,
512
446
  model_route_id: await routeForSpec(this.#db, spec),
513
447
  spawn_model_route_id: worker.spawn_model_route_id,
514
- reasoning_policy: reasoningPolicy,
448
+ reasoning_policy: reasoningPolicy
515
449
  });
516
450
  return { providerSpec: spec, reasoningPolicy };
517
451
  }
@@ -541,7 +475,7 @@ export default class Daemon {
541
475
  id: workerId,
542
476
  model_route_id: await routeForSpec(this.#db, spec),
543
477
  spawn_model_route_id: worker.spawn_model_route_id,
544
- reasoning_policy: reasoningPolicy,
478
+ reasoning_policy: reasoningPolicy
545
479
  });
546
480
  return { providerSpec: spec, reasoningPolicy };
547
481
  }
@@ -563,7 +497,7 @@ export default class Daemon {
563
497
  id: workerId,
564
498
  model_route_id: worker.model_route_id,
565
499
  spawn_model_route_id: spec === null ? null : await routeForSpec(this.#db, spec),
566
- reasoning_policy: worker.reasoning_policy,
500
+ reasoning_policy: worker.reasoning_policy
567
501
  });
568
502
  return spec;
569
503
  }
@@ -585,7 +519,7 @@ export default class Daemon {
585
519
  id: workerId,
586
520
  model_route_id: worker.model_route_id,
587
521
  spawn_model_route_id: await routeForSpec(this.#db, spec),
588
- reasoning_policy: worker.reasoning_policy,
522
+ reasoning_policy: worker.reasoning_policy
589
523
  });
590
524
  }
591
525
  return spec;
@@ -607,7 +541,7 @@ export default class Daemon {
607
541
  return {
608
542
  providerSpec,
609
543
  childProviderSpec: await specForRoute(this.#db, row.spawn_model_route_id),
610
- reasoningPolicy: row.reasoning_policy,
544
+ reasoningPolicy: row.reasoning_policy
611
545
  };
612
546
  }
613
547
  async #providerSpecForLoop(loopId) {
@@ -632,7 +566,7 @@ export default class Daemon {
632
566
  requestedModel: `${requested.provider}/${requested.model}`,
633
567
  stage: "loop-injection",
634
568
  recovery: "Cancel or conclude the loop before selecting another provider.",
635
- retryable: false,
569
+ retryable: false
636
570
  });
637
571
  }
638
572
  }
@@ -645,7 +579,7 @@ export default class Daemon {
645
579
  requestedReasoningPolicy: requested,
646
580
  stage: "loop-injection",
647
581
  recovery: "Cancel or conclude the loop before selecting another reasoning policy.",
648
- retryable: false,
582
+ retryable: false
649
583
  });
650
584
  }
651
585
  }
@@ -660,7 +594,7 @@ export default class Daemon {
660
594
  requestedChildModel: requested === null ? null : `${requested.provider}/${requested.model}`,
661
595
  stage: "loop-injection",
662
596
  recovery: "Cancel or conclude the loop before changing its child provider policy.",
663
- retryable: false,
597
+ retryable: false
664
598
  });
665
599
  }
666
600
  }
@@ -677,7 +611,7 @@ export default class Daemon {
677
611
  requestedMaximumTurns: requested,
678
612
  stage: "loop-injection",
679
613
  recovery: "Cancel or conclude the loop before selecting another turn ceiling.",
680
- retryable: false,
614
+ retryable: false
681
615
  });
682
616
  }
683
617
  }
@@ -698,7 +632,7 @@ export default class Daemon {
698
632
  const normalized = ClientInput.normalizeWorkerSettings(settings);
699
633
  const current = await WorkerSettingsReader.read(this.#db, workerId);
700
634
  const merged = {
701
- capabilities: normalized.capabilities ?? current.capabilities,
635
+ capabilities: normalized.capabilities ?? current.capabilities
702
636
  };
703
637
  await this.#db.worker_settings_update.run({ id: workerId, settings: JSON.stringify(merged) });
704
638
  }
@@ -733,8 +667,8 @@ export default class Daemon {
733
667
  const modelSpec = await specForRoute(this.#db, row.model_route_id);
734
668
  const spawnModelSpec = await specForRoute(this.#db, row.spawn_model_route_id);
735
669
  return {
736
- model: modelSpec === null ? null : projectModelRoute(modelSpec),
737
- spawnModel: spawnModelSpec === null ? null : projectModelRoute(spawnModelSpec),
670
+ model: modelSpec === null ? null : projectModelRoute(modelSpec, row.reasoning_policy),
671
+ spawnModel: spawnModelSpec === null ? null : projectModelRoute(spawnModelSpec, row.reasoning_policy)
738
672
  };
739
673
  }
740
674
  // {§worker-model-selection} — persist an explicit model selection onto the worker
@@ -749,7 +683,7 @@ export default class Daemon {
749
683
  if (policy === null) {
750
684
  throw new OperationFailureError(Results.failure("daemon:provider", "not-configured", 501, "No provider is configured for this worker.", {}, { stage: "provider-selection", recovery: "Select a configured model provider.", retryable: false }));
751
685
  }
752
- return projectModelRoute(policy.providerSpec);
686
+ return projectModelRoute(policy.providerSpec, policy.reasoningPolicy);
753
687
  }
754
688
  async #reasoningSupportForWorker(workerId, row, providerSpec, policy) {
755
689
  const provider = await this.#providerForPolicy(providerSpec, policy);
@@ -785,7 +719,7 @@ export default class Daemon {
785
719
  throw new Error(`worker ${workerId}: model route is missing`);
786
720
  return {
787
721
  policy: row.reasoning_policy,
788
- supportedPolicies: await this.#reasoningSupportForWorker(workerId, row, spec, row.reasoning_policy),
722
+ supportedPolicies: await this.#reasoningSupportForWorker(workerId, row, spec, row.reasoning_policy)
789
723
  };
790
724
  }
791
725
  // {§worker-reasoning-policy} — mutate between loops, validate against both
@@ -812,7 +746,7 @@ export default class Daemon {
812
746
  workerId,
813
747
  stage: "provider-selection",
814
748
  recovery: "Select a model before selecting its reasoning policy.",
815
- retryable: false,
749
+ retryable: false
816
750
  });
817
751
  }
818
752
  const supportedPolicies = await this.#reasoningSupportForWorker(workerId, row, spec, policy);
@@ -820,7 +754,7 @@ export default class Daemon {
820
754
  id: workerId,
821
755
  model_route_id: row.model_route_id,
822
756
  spawn_model_route_id: row.spawn_model_route_id,
823
- reasoning_policy: policy,
757
+ reasoning_policy: policy
824
758
  });
825
759
  return { policy, supportedPolicies };
826
760
  }
@@ -833,7 +767,10 @@ export default class Daemon {
833
767
  await this.#assertWorkerSelectable(workerId);
834
768
  const selector = ClientInput.assertChildSelector("worker.child.set", args.selector);
835
769
  const spec = await this.#resolveWorkerSpawnModel(workerId, selector);
836
- return spec === null ? null : projectModelRoute(spec);
770
+ if (spec === null)
771
+ return null;
772
+ const row = await this.#db.worker_generation_policy_read.get({ id: workerId });
773
+ return projectModelRoute(spec, row?.reasoning_policy ?? null);
837
774
  }
838
775
  // {§worker-model-selection} — a selection must not mutate underneath active
839
776
  // work: a live or parked loop is a precise 409, never a silent retroactive
@@ -844,7 +781,7 @@ export default class Daemon {
844
781
  workerId,
845
782
  stage: "model-selection",
846
783
  recovery: "Conclude or cancel the active loop before selecting a model.",
847
- retryable: false,
784
+ retryable: false
848
785
  });
849
786
  }
850
787
  }
@@ -870,7 +807,7 @@ export default class Daemon {
870
807
  workerId,
871
808
  workspaceId,
872
809
  actualWorkspaceId: worker.workspace_id,
873
- retryable: false,
810
+ retryable: false
874
811
  });
875
812
  }
876
813
  if (worker.origin !== "model") {
@@ -878,7 +815,7 @@ export default class Daemon {
878
815
  workerId,
879
816
  actualOrigin: worker.origin,
880
817
  recovery: "Select or create a model worker for this loop.",
881
- retryable: false,
818
+ retryable: false
882
819
  });
883
820
  }
884
821
  }
@@ -965,18 +902,17 @@ export default class Daemon {
965
902
  const { id: turnId } = await Turn.open(this.#db, {
966
903
  loopId,
967
904
  producer: "client",
968
- kind: "operation",
905
+ kind: "operation"
969
906
  });
970
907
  let turnOpen = true;
971
908
  try {
972
909
  const entryIds = [];
973
910
  const result = await this.#engine.dispatch({
974
911
  statement, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence: 1,
975
- origin: "client", onDispatch: (logEntryId) => { entryIds.push(logEntryId); },
912
+ origin: "client", onDispatch: (logEntryId) => { entryIds.push(logEntryId); }
976
913
  });
977
914
  await Turn.complete(this.#db, turnId, result.status);
978
915
  turnOpen = false;
979
- await this.#branchBatches.sealTurn(turnId);
980
916
  for (const logEntryId of entryIds) {
981
917
  const entry = await LogEntry.fetchLogEntry(this.#db, logEntryId);
982
918
  this.#broadcast({ workspaceId }, "log/entry", { entry });
@@ -1050,7 +986,7 @@ export default class Daemon {
1050
986
  workerId,
1051
987
  workspaceId,
1052
988
  actualWorkspaceId: target.workspace_id,
1053
- retryable: false,
989
+ retryable: false
1054
990
  });
1055
991
  }
1056
992
  const coordinateFields = {
@@ -1059,7 +995,7 @@ export default class Daemon {
1059
995
  sinceId: args.sinceId,
1060
996
  loopSeq: args.loopSeq,
1061
997
  turnSeq: args.turnSeq,
1062
- sequence: args.sequence,
998
+ sequence: args.sequence
1063
999
  };
1064
1000
  for (const [field, value] of Object.entries(coordinateFields)) {
1065
1001
  if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
@@ -1068,7 +1004,7 @@ export default class Daemon {
1068
1004
  value,
1069
1005
  stage: "log-read",
1070
1006
  recovery: "Use a non-negative integer coordinate.",
1071
- retryable: false,
1007
+ retryable: false
1072
1008
  });
1073
1009
  }
1074
1010
  }
@@ -1078,14 +1014,14 @@ export default class Daemon {
1078
1014
  value: args.limit,
1079
1015
  stage: "log-read",
1080
1016
  recovery: "Use a positive integer log limit.",
1081
- retryable: false,
1017
+ retryable: false
1082
1018
  });
1083
1019
  }
1084
1020
  const rows = await this.#db.log_read_recent_ids.all({
1085
1021
  worker_id: workerId,
1086
1022
  loop_id: args.loopId ?? null, turn_id: args.turnId ?? null, since_id: args.sinceId ?? null,
1087
1023
  loop_seq: args.loopSeq ?? null, turn_seq: args.turnSeq ?? null, sequence: args.sequence ?? null,
1088
- limit: Math.min(args.limit ?? 100, 1000),
1024
+ limit: Math.min(args.limit ?? 100, 1000)
1089
1025
  });
1090
1026
  const entries = [];
1091
1027
  for (const r of rows)
@@ -1101,9 +1037,9 @@ export default class Daemon {
1101
1037
  const isActive = active !== null && active.alias === a.alias;
1102
1038
  return {
1103
1039
  alias: a.alias, provider: a.provider, model: a.model, active: isActive,
1104
- inputCapacity: isActive && this.#provider !== null ? this.#provider.inputCapacity : null,
1040
+ inputCapacity: isActive && this.#provider !== null ? this.#provider.inputCapacity : null
1105
1041
  };
1106
- }),
1042
+ })
1107
1043
  };
1108
1044
  }
1109
1045
  // {§model-catalog} The catalog is a bounded, worldless local projection.
@@ -1123,14 +1059,14 @@ export default class Daemon {
1123
1059
  return {
1124
1060
  kind: "scheme",
1125
1061
  scheme,
1126
- display: glyph === undefined ? {} : { glyph },
1062
+ display: glyph === undefined ? {} : { glyph }
1127
1063
  };
1128
1064
  });
1129
1065
  const mimetypes = (await this.#mimetypes.displayMetadata())
1130
1066
  .map(({ mimetype, glyph }) => ({
1131
1067
  kind: "mimetype",
1132
1068
  mimetype,
1133
- display: glyph.length === 0 ? {} : { glyph },
1069
+ display: glyph.length === 0 ? {} : { glyph }
1134
1070
  }));
1135
1071
  return Validator.assertClientDisplayCapabilities([...schemes, ...mimetypes]);
1136
1072
  }
@@ -1149,7 +1085,7 @@ export default class Daemon {
1149
1085
  // the envelope keys the filter on the property's presence, not its value.
1150
1086
  return Envelope.listWorkersForWorkspace(this.#db, ClientInput.assertId("workspace.workers", "workspaceId", workspaceId), {
1151
1087
  ...(origin === undefined ? {} : { origin }),
1152
- ...(parentWorkerId === undefined ? {} : { parentWorkerId }),
1088
+ ...(parentWorkerId === undefined ? {} : { parentWorkerId })
1153
1089
  });
1154
1090
  }
1155
1091
  async readWorker(args) {
@@ -1161,11 +1097,11 @@ export default class Daemon {
1161
1097
  }
1162
1098
  const row = hasId
1163
1099
  ? await this.#db.envelope_get_worker_by_id.get({
1164
- id: ClientInput.assertId("worker.read", "id", args.identity.id),
1100
+ id: ClientInput.assertId("worker.read", "id", args.identity.id)
1165
1101
  })
1166
1102
  : await this.#db.envelope_get_worker_by_name.get({
1167
1103
  workspace_id: workspaceId,
1168
- name: ClientInput.assertOptionalWorkerName("worker.read", "name", args.identity.name),
1104
+ name: ClientInput.assertOptionalWorkerName("worker.read", "name", args.identity.name)
1169
1105
  });
1170
1106
  if (row === undefined || row.workspace_id !== workspaceId)
1171
1107
  return null;
@@ -1188,7 +1124,7 @@ export default class Daemon {
1188
1124
  packetCount: row.packetCount,
1189
1125
  terminalResult: row.terminalResult === null
1190
1126
  ? null
1191
- : Validator.assertOperationResult(JSON.parse(row.terminalResult)),
1127
+ : Validator.assertOperationResult(JSON.parse(row.terminalResult))
1192
1128
  }));
1193
1129
  }
1194
1130
  // {§methods-workspace-prompts}: root-conversation loop seeds, newest-first.
@@ -1245,7 +1181,7 @@ export default class Daemon {
1245
1181
  field: "target",
1246
1182
  stage: "input-validation",
1247
1183
  recovery: "Provide an entry URI.",
1248
- retryable: false,
1184
+ retryable: false
1249
1185
  });
1250
1186
  }
1251
1187
  const channel = ClientInput.assertOptionalChannel("entry.read", args.channel);
@@ -1258,7 +1194,7 @@ export default class Daemon {
1258
1194
  workerId,
1259
1195
  workspaceId,
1260
1196
  actualWorkspaceId: worker.workspace_id,
1261
- retryable: false,
1197
+ retryable: false
1262
1198
  });
1263
1199
  }
1264
1200
  const releaseCapabilities = await this.#residency.acquire(workspaceId, workerId);
@@ -1277,7 +1213,7 @@ export default class Daemon {
1277
1213
  target: args.target,
1278
1214
  stage: "entry-read",
1279
1215
  recovery: "Use a scheme://path target.",
1280
- retryable: false,
1216
+ retryable: false
1281
1217
  }));
1282
1218
  }
1283
1219
  if (args.offset !== undefined && channel === undefined) {
@@ -1285,7 +1221,7 @@ export default class Daemon {
1285
1221
  offset: args.offset,
1286
1222
  stage: "entry-read",
1287
1223
  recovery: "Select the channel to read from the offset.",
1288
- retryable: false,
1224
+ retryable: false
1289
1225
  }));
1290
1226
  }
1291
1227
  if (args.offset !== undefined && (!Number.isSafeInteger(args.offset) || args.offset < 0)) {
@@ -1293,20 +1229,20 @@ export default class Daemon {
1293
1229
  offset: args.offset,
1294
1230
  stage: "entry-read",
1295
1231
  recovery: "Use a non-negative integer offset.",
1296
- retryable: false,
1232
+ retryable: false
1297
1233
  }));
1298
1234
  }
1299
1235
  if (parsed.username !== null || parsed.password !== null) {
1300
1236
  return entryReadResult(Results.failure("daemon:entry", "userinfo-not-allowed", 400, "Entry target URL userinfo is not allowed.", { entry: null }, {
1301
1237
  stage: "entry-read",
1302
1238
  recovery: "Remove credentials from the entry URL.",
1303
- retryable: false,
1239
+ retryable: false
1304
1240
  }));
1305
1241
  }
1306
1242
  const location = await this.#engine.resolveEntryAddress({
1307
1243
  workspaceId,
1308
1244
  workerId,
1309
- target: parsed,
1245
+ target: parsed
1310
1246
  });
1311
1247
  if (location === null) {
1312
1248
  return entryReadResult(Results.failure("daemon:entry", "entry-not-found", 404, "No visible entry exists at the requested target.", { entry: null }, { target: args.target }));
@@ -1316,7 +1252,7 @@ export default class Daemon {
1316
1252
  owner_id: location.ownerId,
1317
1253
  scheme: location.scheme,
1318
1254
  authority: location.authority,
1319
- pathname: location.pathname,
1255
+ pathname: location.pathname
1320
1256
  });
1321
1257
  if (row === undefined) {
1322
1258
  return entryReadResult(Results.failure("daemon:entry", "entry-not-found", 404, `No visible entry exists at ${location.target}.`, { entry: null }, { target: location.target }));
@@ -1337,7 +1273,7 @@ export default class Daemon {
1337
1273
  ...(availableChannels.length === 0
1338
1274
  ? {}
1339
1275
  : { recovery: `Use one of the available channels: ${availableChannels.map((channel) => `#${channel}`).join(", ")}.` }),
1340
- retryable: false,
1276
+ retryable: false
1341
1277
  }));
1342
1278
  }
1343
1279
  channelRows = [r];
@@ -1350,7 +1286,7 @@ export default class Daemon {
1350
1286
  contentLength: c.contentLength,
1351
1287
  mimetype: c.mimetype,
1352
1288
  weight: c.weight,
1353
- state: c.state,
1289
+ state: c.state
1354
1290
  };
1355
1291
  }
1356
1292
  return entryReadResult({
@@ -1358,8 +1294,8 @@ export default class Daemon {
1358
1294
  entry: {
1359
1295
  entryId: row.id,
1360
1296
  target: location.target,
1361
- channels,
1362
- },
1297
+ channels
1298
+ }
1363
1299
  });
1364
1300
  }
1365
1301
  finally {
@@ -1403,7 +1339,7 @@ export default class Daemon {
1403
1339
  workerId,
1404
1340
  workspaceId,
1405
1341
  actualWorkspaceId: owner.workspace_id,
1406
- retryable: false,
1342
+ retryable: false
1407
1343
  });
1408
1344
  }
1409
1345
  if (name !== undefined) {
@@ -1447,9 +1383,9 @@ export default class Daemon {
1447
1383
  ...(runtime.resourcesPath === undefined ? {} : { resourcesPath: runtime.resourcesPath }),
1448
1384
  ...(runtime.expandTools === undefined ? {} : { expandTools: runtime.expandTools }),
1449
1385
  available: availability.available,
1450
- detail: availability.detail,
1386
+ detail: availability.detail
1451
1387
  },
1452
- scheme,
1388
+ scheme
1453
1389
  };
1454
1390
  }
1455
1391
  registerModuleAction(registration) {
@@ -1485,7 +1421,7 @@ export default class Daemon {
1485
1421
  name,
1486
1422
  scope,
1487
1423
  inputSchema,
1488
- outputSchema,
1424
+ outputSchema
1489
1425
  }))
1490
1426
  .toSorted((left, right) => left.name.localeCompare(right.name));
1491
1427
  }
@@ -1566,8 +1502,8 @@ export default class Daemon {
1566
1502
  invocation: questionRuntimeDecl.invocation,
1567
1503
  details: questionRuntimeDecl.details ?? "",
1568
1504
  available: true,
1569
- detail: "in-process",
1570
- },
1505
+ detail: "in-process"
1506
+ }
1571
1507
  }]);
1572
1508
  // {§effect-policy-tunable} — invalid operator policy fails boot, not the first EXEC.
1573
1509
  EffectPolicy.validateConfiguration();
@@ -1617,7 +1553,6 @@ export default class Daemon {
1617
1553
  await this.#db.recovery_error_orphan_subscription_channels.run({});
1618
1554
  await this.#db.recovery_fail_orphan_subscriptions.run({});
1619
1555
  await this.#db.recovery_resume_unblocked_parks.run({});
1620
- await this.#branchBatches.recover();
1621
1556
  const orphanSources = await this.#db.recovery_orphan_prompt_sources.all({});
1622
1557
  const invalidOrphan = orphanSources.find(({ origin }) => origin !== "model");
1623
1558
  if (invalidOrphan !== undefined) {
@@ -1641,7 +1576,7 @@ export default class Daemon {
1641
1576
  const started = await this.#drains.ensureDrain({
1642
1577
  workspaceId: row.workspace_id,
1643
1578
  workerId: row.worker_id,
1644
- systemPrompt,
1579
+ systemPrompt
1645
1580
  });
1646
1581
  started?.drainPromise.catch((err) => {
1647
1582
  if (this.#started) {
@@ -1673,7 +1608,6 @@ export default class Daemon {
1673
1608
  const derivationAbort = new DOMException("daemon stopping", "AbortError");
1674
1609
  this.#engine.cancelAllProposals("daemon_stopping");
1675
1610
  this.#engine.cancelDerivations(derivationAbort);
1676
- this.#branchBatches.beginStop();
1677
1611
  this.#drains.beginStop("daemon_stopping");
1678
1612
  this.#residency.beginStop();
1679
1613
  const stopDeadlineMs = Daemon.#stopDeadlineMs();
@@ -1714,7 +1648,6 @@ export default class Daemon {
1714
1648
  // the daemon forever. Past the deadline the waits are abandoned; the
1715
1649
  // process may exit with the WAL in place (SQLite recovers) rather than
1716
1650
  // leak as a live-but-wedged tree.
1717
- const branchResult = await settle("branch batches idle", () => this.#branchBatches.idle());
1718
1651
  const drainResult = await settle("drains idle", () => this.#drains.idle());
1719
1652
  // A boundary publication queued behind the last turn settles before
1720
1653
  // modules close and before the DB goes away ({§functionality-publication}).
@@ -1737,7 +1670,6 @@ export default class Daemon {
1737
1670
  const closeErrors = [
1738
1671
  moduleResult,
1739
1672
  functionalityResult,
1740
- branchResult,
1741
1673
  drainResult,
1742
1674
  streamingResult,
1743
1675
  derivationResult,
@@ -1800,7 +1732,7 @@ export default class Daemon {
1800
1732
  conflicts,
1801
1733
  stage: "loop-injection",
1802
1734
  recovery: "Cancel the active loop before changing policy, or omit policy to keep its current posture.",
1803
- retryable: false,
1735
+ retryable: false
1804
1736
  });
1805
1737
  }
1806
1738
  }
@@ -1830,7 +1762,7 @@ export default class Daemon {
1830
1762
  reasoning_policy: first.reasoning_policy,
1831
1763
  max_turns: first.max_turns,
1832
1764
  open_paths: first.open_paths ?? "[]",
1833
- orphan_source_loop_id: endedLoopId,
1765
+ orphan_source_loop_id: endedLoopId
1834
1766
  });
1835
1767
  if (recovery === undefined)
1836
1768
  throw new Error("reconcileOrphanedPrompts: enqueue returned no row");
@@ -1841,7 +1773,7 @@ export default class Daemon {
1841
1773
  source_loop_id: endedLoopId,
1842
1774
  source_pattern: `${prefix}%`,
1843
1775
  source_prefix_len: prefix.length,
1844
- target_prefix: promptLoopPrefix(recovery.sequence),
1776
+ target_prefix: promptLoopPrefix(recovery.sequence)
1845
1777
  });
1846
1778
  if (moved.length !== frames.length) {
1847
1779
  throw new Error(`reconcileOrphanedPrompts: expected to re-home ${frames.length} frames, moved ${moved.length}`);