zelari-code 2.16.4 → 2.17.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 (38) hide show
  1. package/dist/cli/app.js +3 -0
  2. package/dist/cli/app.js.map +1 -1
  3. package/dist/cli/companion/serve.js +29 -0
  4. package/dist/cli/companion/serve.js.map +1 -1
  5. package/dist/cli/harnessState.js +240 -0
  6. package/dist/cli/harnessState.js.map +1 -0
  7. package/dist/cli/headless/harnessStateEmit.js +38 -0
  8. package/dist/cli/headless/harnessStateEmit.js.map +1 -0
  9. package/dist/cli/headless/policyGate.js +6 -3
  10. package/dist/cli/headless/policyGate.js.map +1 -1
  11. package/dist/cli/headless/runOneTurn.js +29 -5
  12. package/dist/cli/headless/runOneTurn.js.map +1 -1
  13. package/dist/cli/headless.js +20 -9
  14. package/dist/cli/headless.js.map +1 -1
  15. package/dist/cli/hooks/useChatTurn.js +25 -1
  16. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  17. package/dist/cli/hooks/useSlashDispatch.js +1 -1
  18. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  19. package/dist/cli/kraken/verificationBridge.js +27 -4
  20. package/dist/cli/kraken/verificationBridge.js.map +1 -1
  21. package/dist/cli/lsp/manager.js +31 -11
  22. package/dist/cli/lsp/manager.js.map +1 -1
  23. package/dist/cli/main.bundled.js +849 -471
  24. package/dist/cli/main.bundled.js.map +4 -4
  25. package/dist/cli/memory/fileBackend.js +7 -3
  26. package/dist/cli/memory/fileBackend.js.map +1 -1
  27. package/dist/cli/memory/spineTelemetry.js +49 -0
  28. package/dist/cli/memory/spineTelemetry.js.map +1 -0
  29. package/dist/cli/runHeadless.js +122 -30
  30. package/dist/cli/runHeadless.js.map +1 -1
  31. package/dist/cli/safety/jails/win32.js.map +1 -1
  32. package/dist/cli/serve/harnessServer.js +20 -1
  33. package/dist/cli/serve/harnessServer.js.map +1 -1
  34. package/dist/cli/sessionSpine.js +7 -13
  35. package/dist/cli/sessionSpine.js.map +1 -1
  36. package/dist/cli/slashHandlers/krakenGraph.js +11 -0
  37. package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
  38. package/package.json +2 -2
@@ -118,13 +118,17 @@ export function isMemoryEnabled(env = process.env) {
118
118
  /**
119
119
  * Resolve and initialise the memory backend for a project. Returns a no-op
120
120
  * backend (never throws) when disabled or when initialisation fails, so callers
121
- * can always `await memory.search(...)` without guarding.
121
+ * can always `await memory.search(...)` without guarding. `onEvent` (optional)
122
+ * receives V2 memory telemetry — hosts pass a spine sink (`memorySinkFor`) or
123
+ * `spineMemoryEventNote` to project it onto the session spine (W2).
122
124
  */
123
- export async function getMemoryBackend(projectRoot, env = process.env) {
125
+ export async function getMemoryBackend(projectRoot, env = process.env, onEvent) {
124
126
  if (!isMemoryEnabled(env))
125
127
  return new NoopMemoryBackend();
126
128
  if (isMemoryV2Enabled(env)) {
127
- const service = await getMemoryService(projectRoot, env);
129
+ const service = await getMemoryService(projectRoot, env, {
130
+ ...(onEvent ? { onEvent } : {}),
131
+ });
128
132
  if (service instanceof NoopMemoryService)
129
133
  return new NoopMemoryBackend();
130
134
  return new LegacyMemoryBackendAdapter(service);
@@ -1 +1 @@
1
- {"version":3,"file":"fileBackend.js","sourceRoot":"","sources":["../../../src/cli/memory/fileBackend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fileBackend.js","sourceRoot":"","sources":["../../../src/cli/memory/fileBackend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Write one memory telemetry event as a spine `note`. Per-turn context
3
+ * projection (`memory_recall_end` with reason `context-built`) gets the
4
+ * dedicated `context.projection` subject; every other event lands under
5
+ * `memory_event` with its type. Never throws — a degraded/disabled spine
6
+ * must not break the run over telemetry.
7
+ */
8
+ export function spineMemoryEventNote(handle, event) {
9
+ try {
10
+ if (event.type === 'memory_recall_end' && event.reason === 'context-built') {
11
+ handle.note('context.projection', {
12
+ subject: 'context.projection',
13
+ ...(event.contextChars !== undefined ? { contextChars: event.contextChars } : {}),
14
+ ...(event.returnedCount !== undefined ? { returnedCount: event.returnedCount } : {}),
15
+ ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),
16
+ ...(event.backend !== undefined ? { backend: event.backend } : {}),
17
+ });
18
+ return;
19
+ }
20
+ handle.note(`memory_${event.type}`, {
21
+ subject: 'memory_event',
22
+ type: event.type,
23
+ ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),
24
+ ...(event.candidateCount !== undefined ? { candidateCount: event.candidateCount } : {}),
25
+ ...(event.returnedCount !== undefined ? { returnedCount: event.returnedCount } : {}),
26
+ ...(event.backend !== undefined ? { backend: event.backend } : {}),
27
+ ...(event.reason !== undefined ? { reason: event.reason } : {}),
28
+ ...(event.memoryId !== undefined ? { memoryId: event.memoryId } : {}),
29
+ });
30
+ }
31
+ catch {
32
+ // Degraded spine must NEVER break the run over telemetry.
33
+ }
34
+ }
35
+ /**
36
+ * Late-binding seam for hosts where the memory service is constructed BEFORE
37
+ * the spine opens (headless single-turn / council): events are dropped while
38
+ * `holder.current` is unset and flow once the spine handle is assigned.
39
+ * TUI hosts pass a getter-backed holder (`{ get current() { return … } }`) —
40
+ * a getter on `current` satisfies this shape.
41
+ */
42
+ export function memorySinkFor(holder) {
43
+ return (event) => {
44
+ const handle = holder.current;
45
+ if (handle)
46
+ spineMemoryEventNote(handle, event);
47
+ };
48
+ }
49
+ //# sourceMappingURL=spineTelemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spineTelemetry.js","sourceRoot":"","sources":["../../../src/cli/memory/spineTelemetry.ts"],"names":[],"mappings":"AAeA;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB,EAAE,KAAkB;IAC9E,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,IAAI,KAAK,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;YAC3E,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;gBAChC,OAAO,EAAE,oBAAoB;gBAC7B,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpF,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3E,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnE,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,EAAE,EAAE;YAClC,OAAO,EAAE,cAAc;YACvB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,GAAG,CAAC,KAAK,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvF,GAAG,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpF,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtE,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,MAAqC;IACjE,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,MAAM;YAAE,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC"}
@@ -3,6 +3,8 @@ import { buildCouncilTaskWithHistory, } from './hooks/conversationContext.js';
3
3
  import { createBuiltinToolRegistry } from './toolRegistry.js';
4
4
  import { chooseOrchestration } from './orchestration/policy.js';
5
5
  import { collectOrchestrationFacts, spineOrchestrationNote } from './orchestration/facts.js';
6
+ // W2: memory telemetry projected onto the session spine as state-only notes.
7
+ import { memorySinkFor, spineMemoryEventNote } from './memory/spineTelemetry.js';
6
8
  import { COUNCIL_TIER_SIZES } from './councilConfig.js';
7
9
  import { emitEvent, resolveHeadlessCwd, resolveHeadlessKey, resolveHeadlessProvider, } from './headless.js';
8
10
  import { createLocalCliProvider } from './provider/localCli/claudeProvider.js';
@@ -16,19 +18,21 @@ import { writeSessionTodos } from './sessionTodos.js';
16
18
  import { promises as fs } from 'node:fs';
17
19
  import path from 'node:path';
18
20
  import { randomUUID } from 'node:crypto';
19
- import { evaluateStrictBuildGate, strictGateEventPayload, strictGateExitCode } from './kraken/verificationBridge.js';
21
+ import { evaluateStrictBuildGate, strictEnvOverlay, strictGateEventPayload, strictGateExitCode } from './kraken/verificationBridge.js';
20
22
  import { setActiveProofPersistenceSurface, } from './kraken/completionProofPersist.js';
21
23
  import { buildModelContext } from './budget/modelContextBuilder.js';
22
24
  import { recordCompactionMetrics } from './metrics.js';
23
25
  import { openHeadlessSpine, resolveHeadlessProfileId, seedHeadlessModelHistory, sessionStartedEvent, } from './headlessSpine.js';
26
+ import { emitHarnessStateEvent } from './headless/harnessStateEmit.js';
24
27
  import { checkStrictPolicyLoad, recordPolicyLoadBlockedOnSpine, reportPolicyLoadBlocked, } from './headless/policyGate.js';
25
28
  import { activePolicyLoadMode, setActivePolicyLoadSurface, } from './safety/policyLoadMode.js';
26
29
  import { planModeFromOpts, registerHeadlessMcp, runOneTurn, writeProofSafe } from './headless/runOneTurn.js';
27
30
  export async function runHeadless(opts) {
28
31
  resetTaskSpawnCount();
29
- if (opts.strictDone) {
30
- process.env.ZELARI_STRICT_DONE = '1';
31
- }
32
+ // H10-fix1: strictDone/missionStrict NEVER touch process.env (not even
33
+ // once-per-process) each gate site builds a per-invocation overlay via
34
+ // strictEnvOverlay(opts), so `--no-strict-done` (false→'0') is honored
35
+ // and a concurrent sidecar can never race on the real env.
32
36
  // === Global crash handlers (headless-only) ===
33
37
  // Without these, an uncaught exception during a tool call (e.g. write_file
34
38
  // failing deep in the harness) kills the process silently: no agent_end,
@@ -121,7 +125,13 @@ export async function runHeadless(opts) {
121
125
  model,
122
126
  });
123
127
  }
124
- return dispatchHeadlessTurn(opts, provider, model, providerStream);
128
+ return dispatchHeadlessTurn(opts, provider, model, providerStream, {
129
+ // H10-fix3: the gate already ran above on the SAME input (no chdir in
130
+ // between) — the one-shot marker keeps dispatchHeadlessTurn from
131
+ // re-running it (duplicate `[policy]` stderr warning + double policy
132
+ // load). Per-invocation flag only; never process-global.
133
+ policyGateDone: true,
134
+ });
125
135
  }
126
136
  /** Strict policy-load + phase/proof surfaces. Returns the block exit code, or undefined. */
127
137
  async function applyHeadlessPolicyGate(opts) {
@@ -134,6 +144,9 @@ async function applyHeadlessPolicyGate(opts) {
134
144
  reportPolicyLoadBlocked(policyLoad.block, opts.output);
135
145
  await recordPolicyLoadBlockedOnSpine(policyLoad.block, {
136
146
  mode: opts.mode,
147
+ // H10-fix2: the spine must land in the RESOLVED workspace, not the
148
+ // process cwd — a sidecar hosts N workspaces without `chdir`.
149
+ workspace: cwd,
137
150
  ...(opts.profile ? { profile: opts.profile } : {}),
138
151
  ...(opts.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {}),
139
152
  });
@@ -147,9 +160,13 @@ async function applyHeadlessPolicyGate(opts) {
147
160
  /**
148
161
  * Per-turn dispatch shared by `--headless` (one-shot process) and
149
162
  * `--serve-harness` (long-lived sidecar). Does NOT install process-fatal
150
- * handlers or mutate `ZELARI_STRICT_DONE` — those belong to the one-shot
151
- * CLI wrapper. Honors `opts.cwd` so N sidecar sessions can sit on
152
- * different folders without `chdir`.
163
+ * handlers or touch `ZELARI_STRICT_DONE` — the strict knobs ride
164
+ * HeadlessOptions into a per-invocation env overlay (strictEnvOverlay)
165
+ * consumed at each gate site (runOneTurn / runHeadlessZelari). Honors
166
+ * `opts.cwd` so N sidecar sessions can sit on
167
+ * different folders without `chdir`. `oneShot.policyGateDone` is set by
168
+ * the one-shot wrapper ONLY (its gate already ran); the sidecar never
169
+ * passes it, so the gate keeps running every turn here.
153
170
  */
154
171
  const KRAKEN_TURN_ENV = [
155
172
  ['krakenExploreModel', 'ZELARI_KRAKEN_EXPLORE_MODEL'],
@@ -166,7 +183,11 @@ function applyKrakenTurnEnv(opts) {
166
183
  }
167
184
  }
168
185
  }
169
- export async function dispatchHeadlessTurn(opts, provider, model, providerStream) {
186
+ export async function dispatchHeadlessTurn(opts, provider, model, providerStream, oneShot,
187
+ // t37: serve hosts pass the kernel-owned workspace LspManager here so
188
+ // every dispatch path (kraken single / council / zelari) registers the
189
+ // LSP tools against THAT server instead of the shared per-root manager.
190
+ extras) {
170
191
  const cwd = resolveHeadlessCwd(opts);
171
192
  if (typeof opts.mode === 'string') {
172
193
  const parsed = parseMode(opts.mode);
@@ -176,6 +197,10 @@ export async function dispatchHeadlessTurn(opts, provider, model, providerStream
176
197
  opts = { ...opts, cwd };
177
198
  }
178
199
  applyKrakenTurnEnv(opts);
200
+ // Each parent user turn gets a fresh tentacle budget (GUIDA: default 6).
201
+ // Desktop sidecar never enters runHeadless(), so without this the counter
202
+ // lives for the whole Node process — 6 tentacles total, then spawn cap.
203
+ resetTaskSpawnCount();
179
204
  if (opts.todos && opts.todos.length > 0) {
180
205
  writeSessionTodos(opts.todos, { merge: false });
181
206
  }
@@ -196,9 +221,17 @@ export async function dispatchHeadlessTurn(opts, provider, model, providerStream
196
221
  catch {
197
222
  /* non-fatal */
198
223
  }
199
- const policyBlock = await applyHeadlessPolicyGate(opts);
200
- if (policyBlock !== undefined)
201
- return policyBlock;
224
+ // H10-fix3: the sidecar (serve/harnessServer) needs this gate EVERY turn —
225
+ // it also re-pins setPhase/setActivePolicyLoadSurface/setActiveProof-
226
+ // PersistenceSurface per turn. The one-shot wrapper passes
227
+ // `policyGateDone` because runHeadless() already ran the identical gate
228
+ // moments ago; re-running it would emit the same `[policy]` warning twice
229
+ // and reload the policy set for nothing.
230
+ if (!oneShot?.policyGateDone) {
231
+ const policyBlock = await applyHeadlessPolicyGate(opts);
232
+ if (policyBlock !== undefined)
233
+ return policyBlock;
234
+ }
202
235
  // P1.1 (t12) -> t23 (P1.E): `--mode auto` resolves ONCE here, before any
203
236
  // dispatch check. Pure classifier (./orchestration/policy.js); the only
204
237
  // I/O is the CHEAP FACTS collection (bounded repo walk + active-contract
@@ -257,12 +290,12 @@ export async function dispatchHeadlessTurn(opts, provider, model, providerStream
257
290
  process.stderr.write(`[zelari-code --headless] ${line}\n`);
258
291
  }
259
292
  if (mode === 'zelari') {
260
- return runHeadlessZelari(opts, provider, model, providerStream);
293
+ return runHeadlessZelari(opts, provider, model, providerStream, extras);
261
294
  }
262
295
  if (mode === 'council' || opts.useCouncil) {
263
- return runHeadlessCouncil(opts, provider, model, providerStream);
296
+ return runHeadlessCouncil(opts, provider, model, providerStream, extras);
264
297
  }
265
- return runOneTurn(opts, provider, model, providerStream);
298
+ return runOneTurn(opts, provider, model, providerStream, extras);
266
299
  }
267
300
  /**
268
301
  * `--kraken-graph <goal>`: plan (F4) + execute (F3) a Kraken task graph,
@@ -287,9 +320,23 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
287
320
  const { createKrakenSubAgentContextFactory } = await import('./toolRegistry.js');
288
321
  const cwd = resolveHeadlessCwd(opts);
289
322
  const sessionId = crypto.randomUUID();
323
+ // W1: kraken-graph was the only headless dispatch path with no ADR-0016
324
+ // session spine — hosts had no event log to resume or replay. Mirror the
325
+ // council/zelari hosts: same open shape, same degrade-and-stop discipline.
326
+ const spine = await openHeadlessSpine({ sessionId, mode: 'kraken', workspace: cwd });
327
+ // E1.4: advertise the spine session id so hosts (Desktop) resume the same
328
+ // event log next turn. Gated to json output — plain stdout must stay
329
+ // byte-for-byte identical to the pre-spine behavior.
330
+ if (opts.output === 'json')
331
+ emitEvent(sessionStartedEvent(spine));
332
+ spine.userMessage(prompt);
290
333
  const { getMemoryService, isMemoryAutoWriteEnabled, isMemoryV2Enabled } = await import('./memory/serviceFactory.js');
291
334
  const graphMemory = isMemoryV2Enabled()
292
- ? await getMemoryService(cwd, process.env)
335
+ ? await getMemoryService(cwd, process.env, {
336
+ // W2: the spine is already open here — memory events are noted
337
+ // directly (context.projection / memory_event state-only payloads).
338
+ onEvent: (event) => spineMemoryEventNote(spine, event),
339
+ })
293
340
  : undefined;
294
341
  const log = (message) => {
295
342
  if (opts.output === 'json') {
@@ -310,6 +357,9 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
310
357
  abort.abort();
311
358
  };
312
359
  process.once('SIGINT', onSigint);
360
+ // W1: every return below records its code first so the finally can close
361
+ // the spine with the matching status (completed / error / cancelled).
362
+ let exitCode = 0;
313
363
  try {
314
364
  // ---- Pre-flight: run a pre-built plan from disk, skipping the planner.
315
365
  let preflightGraph;
@@ -322,7 +372,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
322
372
  }
323
373
  catch (e) {
324
374
  log(`plan file not found: ${planPath} (${e.message})`);
325
- return 1;
375
+ exitCode = 1;
376
+ return exitCode;
326
377
  }
327
378
  let planJson;
328
379
  try {
@@ -330,11 +381,13 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
330
381
  }
331
382
  catch (e) {
332
383
  log(`plan file is malformed JSON: ${e.message}`);
333
- return 1;
384
+ exitCode = 1;
385
+ return exitCode;
334
386
  }
335
387
  if (!planJson || !Array.isArray(planJson.nodes)) {
336
388
  log(`plan file is malformed: missing "nodes" array`);
337
- return 1;
389
+ exitCode = 1;
390
+ return exitCode;
338
391
  }
339
392
  const { createGraph, validateGraph } = await import('@zelari/core');
340
393
  const validated = createGraph(planJson.graphId ?? opts.runPlan, planJson.nodes);
@@ -373,7 +426,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
373
426
  emitEvent({ type: 'log', message: `plan_only_id=${planId}` });
374
427
  emitEvent({ type: 'log', message: `plan_only_path=${planPath}` });
375
428
  }
376
- return 0;
429
+ exitCode = 0;
430
+ return exitCode;
377
431
  }
378
432
  const audit = new AuditLogger();
379
433
  const executor = new KrakenGraphExecutor({
@@ -438,7 +492,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
438
492
  else {
439
493
  process.stdout.write(`${finalAscii}\n`);
440
494
  }
441
- return summary.converged ? 0 : 3;
495
+ exitCode = summary.converged ? 0 : 3;
496
+ return exitCode;
442
497
  }
443
498
  catch (err) {
444
499
  const message = err instanceof Error ? err.message : String(err);
@@ -448,18 +503,39 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
448
503
  else {
449
504
  process.stderr.write(`[zelari-code --headless] kraken graph failed: ${message}\n`);
450
505
  }
451
- return 2;
506
+ exitCode = 2;
507
+ return exitCode;
452
508
  }
453
509
  finally {
454
510
  process.off('SIGINT', onSigint);
455
511
  await graphMemory?.close().catch(() => undefined);
512
+ // W1: the spine closes on EVERY exit (completed / error / cancelled) and
513
+ // must never change the exit code.
514
+ try {
515
+ const closeReason = abort.signal.aborted
516
+ ? 'cancelled'
517
+ : exitCode === 0
518
+ ? 'completed'
519
+ : 'error';
520
+ await spine.close(closeReason);
521
+ }
522
+ catch { /* spine never fails the run */ }
523
+ // HarnessState inc.3: final read-model event for JSON hosts (best-effort).
524
+ await emitHarnessStateEvent({ spine, workspaceRoot: cwd, output: opts.output, emitEvent });
456
525
  }
457
526
  }
458
- async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAutoWrite = false) {
527
+ async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAutoWrite = false,
528
+ // t37: the council/zelari parent registry honors the same TurnExtras as
529
+ // the single-agent path. TODO(t37): the deeper council mission-slice
530
+ // per-agent registries (~runAgentMissionSlice below) still resolve via
531
+ // the shared per-root map — same anti-thrash behavior (one manager per
532
+ // root, no cross-workspace kills), just not the kernel-owned instance.
533
+ extras) {
459
534
  const cwd = opts ? resolveHeadlessCwd(opts) : process.cwd();
460
535
  const { registry: toolRegistry } = createBuiltinToolRegistry({
461
536
  root: cwd,
462
537
  planMode,
538
+ ...(extras?.lspProvider ? { lspProvider: extras.lspProvider } : {}),
463
539
  permissionPolicy: {
464
540
  read: 'allow',
465
541
  write: 'allow',
@@ -487,13 +563,18 @@ async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAut
487
563
  }
488
564
  return { toolRegistry, workspaceCtx: realCtx };
489
565
  }
490
- async function runHeadlessCouncil(opts, provider, model, providerStream) {
566
+ async function runHeadlessCouncil(opts, provider, model, providerStream, extras) {
491
567
  const { dispatchCouncil } = await import('./councilDispatcher.js');
492
568
  const sessionId = crypto.randomUUID();
493
569
  const cwd = resolveHeadlessCwd(opts);
494
570
  const memoryFactory = await import('./memory/serviceFactory.js');
571
+ // W2: memory events project onto the session spine (late-binding holder —
572
+ // the spine opens below; events before assignment are dropped, advisory).
573
+ const spineHolder = {};
495
574
  const nativeMemory = memoryFactory.isMemoryV2Enabled()
496
- ? await memoryFactory.getMemoryService(cwd, process.env)
575
+ ? await memoryFactory.getMemoryService(cwd, process.env, {
576
+ onEvent: memorySinkFor(spineHolder),
577
+ })
497
578
  : undefined;
498
579
  const memoryAutoWrite = memoryFactory.isMemoryAutoWriteEnabled();
499
580
  const spine = await openHeadlessSpine({
@@ -502,6 +583,8 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
502
583
  profile: opts.profile,
503
584
  workspace: cwd,
504
585
  });
586
+ // W2: bind the memory telemetry sink to the now-open spine.
587
+ spineHolder.current = spine;
505
588
  // Exit-1/E1.2: the session spine is the model-context source of truth.
506
589
  // Legacy `--history` is imported one-shot into a fresh log; prior turns
507
590
  // are then derived from events. The 1.x rolling history no longer feeds
@@ -528,7 +611,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
528
611
  process.stderr.write('[zelari-code --headless] council build soft-gate: forced design-phase ' +
529
612
  '(set ZELARI_COUNCIL_CAN_BUILD=1 to allow Lucifero implement)\n');
530
613
  }
531
- const { toolRegistry } = await buildCouncilToolRegistry(planModeFromOpts(opts) || softGated, opts, nativeMemory, memoryAutoWrite);
614
+ const { toolRegistry } = await buildCouncilToolRegistry(planModeFromOpts(opts) || softGated, opts, nativeMemory, memoryAutoWrite, extras);
532
615
  const { FeedbackStore } = await import('./councilFeedback.js');
533
616
  const feedbackStore = new FeedbackStore();
534
617
  // Multi-turn: Desktop passes --history, but council used to ignore it →
@@ -678,6 +761,8 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
678
761
  await spine.close(exitCode === 0 ? 'completed' : 'error');
679
762
  }
680
763
  catch { /* spine never fails the run */ }
764
+ // HarnessState inc.3: final read-model event for JSON hosts (best-effort).
765
+ await emitHarnessStateEvent({ spine, workspaceRoot: cwd, output: opts.output, emitEvent });
681
766
  if (opts.exportSessionPath) {
682
767
  try {
683
768
  const json = await spine.exportJson();
@@ -723,7 +808,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
723
808
  /**
724
809
  * Zelari mission loop (headless). Streams progress as `log` events + council BrainEvents.
725
810
  */
726
- async function runHeadlessZelari(opts, provider, model, providerStream) {
811
+ async function runHeadlessZelari(opts, provider, model, providerStream, extras) {
727
812
  const projectRoot = resolveHeadlessCwd(opts);
728
813
  const sessionId = opts.resumeSessionId ?? crypto.randomUUID();
729
814
  const spine = await openHeadlessSpine({
@@ -752,9 +837,11 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
752
837
  userMessage: opts.task,
753
838
  hasPlan: hasWorkspacePlan(projectRoot),
754
839
  });
755
- const memory = await getMemoryBackend(projectRoot);
840
+ // W2: the mission spine is already open here — mission memory events are
841
+ // noted directly (context.projection / memory_event state-only payloads).
842
+ const memory = await getMemoryBackend(projectRoot, process.env, (event) => spineMemoryEventNote(spine, event));
756
843
  const nativeMissionMemory = memory.service;
757
- const { toolRegistry, workspaceCtx } = await buildCouncilToolRegistry(planModeFromOpts(opts), opts, nativeMissionMemory, Boolean(nativeMissionMemory) && process.env.ZELARI_MEMORY_AUTO_WRITE !== '0');
844
+ const { toolRegistry, workspaceCtx } = await buildCouncilToolRegistry(planModeFromOpts(opts), opts, nativeMissionMemory, Boolean(nativeMissionMemory) && process.env.ZELARI_MEMORY_AUTO_WRITE !== '0', extras);
758
845
  const feedbackStore = new FeedbackStore();
759
846
  const chairmanBudget = envNumber(process.env.ZELARI_MODE_MAX_TOOLS_LUCIFER, {
760
847
  default: 30,
@@ -1079,12 +1166,15 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
1079
1166
  exitCode = exitCode || 3;
1080
1167
  else if (state.status === 'success') {
1081
1168
  // ADR-0025: missions close under the strict evidence gate by default
1082
- // (opt-out: ZELARI_MISSION_STRICT=0). A blocked gate never exits 0 —
1169
+ // (opt-out: ZELARI_MISSION_STRICT=0, or the per-run --no-mission-strict /
1170
+ // --no-strict-done overlay). A blocked gate never exits 0 —
1083
1171
  // the mission "success" becomes the strict exit code and the spine
1084
1172
  // records mission-strict-blocked instead of mission-success.
1085
1173
  const missionGate = await evaluateStrictBuildGate('build', {
1086
1174
  emit: (input) => spine.appendEvent(input),
1087
1175
  surface: 'mission',
1176
+ // H10-fix1: per-invocation env overlay — never process.env.
1177
+ env: strictEnvOverlay(opts),
1088
1178
  cwd: projectRoot,
1089
1179
  });
1090
1180
  const missionVerificationPayload = strictGateEventPayload(missionGate);
@@ -1126,6 +1216,8 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
1126
1216
  await spine.close(exitCode === 2 ? 'error' : 'stopped');
1127
1217
  }
1128
1218
  catch { /* spine never fails the run */ }
1219
+ // HarnessState inc.3: final read-model event for JSON hosts (best-effort).
1220
+ await emitHarnessStateEvent({ spine, workspaceRoot: projectRoot, output: opts.output, emitEvent });
1129
1221
  if (opts.exportSessionPath) {
1130
1222
  try {
1131
1223
  const json = await spine.exportJson();