zelari-code 2.16.5 → 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 +848 -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 +118 -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);
@@ -200,9 +221,17 @@ export async function dispatchHeadlessTurn(opts, provider, model, providerStream
200
221
  catch {
201
222
  /* non-fatal */
202
223
  }
203
- const policyBlock = await applyHeadlessPolicyGate(opts);
204
- if (policyBlock !== undefined)
205
- 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
+ }
206
235
  // P1.1 (t12) -> t23 (P1.E): `--mode auto` resolves ONCE here, before any
207
236
  // dispatch check. Pure classifier (./orchestration/policy.js); the only
208
237
  // I/O is the CHEAP FACTS collection (bounded repo walk + active-contract
@@ -261,12 +290,12 @@ export async function dispatchHeadlessTurn(opts, provider, model, providerStream
261
290
  process.stderr.write(`[zelari-code --headless] ${line}\n`);
262
291
  }
263
292
  if (mode === 'zelari') {
264
- return runHeadlessZelari(opts, provider, model, providerStream);
293
+ return runHeadlessZelari(opts, provider, model, providerStream, extras);
265
294
  }
266
295
  if (mode === 'council' || opts.useCouncil) {
267
- return runHeadlessCouncil(opts, provider, model, providerStream);
296
+ return runHeadlessCouncil(opts, provider, model, providerStream, extras);
268
297
  }
269
- return runOneTurn(opts, provider, model, providerStream);
298
+ return runOneTurn(opts, provider, model, providerStream, extras);
270
299
  }
271
300
  /**
272
301
  * `--kraken-graph <goal>`: plan (F4) + execute (F3) a Kraken task graph,
@@ -291,9 +320,23 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
291
320
  const { createKrakenSubAgentContextFactory } = await import('./toolRegistry.js');
292
321
  const cwd = resolveHeadlessCwd(opts);
293
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);
294
333
  const { getMemoryService, isMemoryAutoWriteEnabled, isMemoryV2Enabled } = await import('./memory/serviceFactory.js');
295
334
  const graphMemory = isMemoryV2Enabled()
296
- ? 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
+ })
297
340
  : undefined;
298
341
  const log = (message) => {
299
342
  if (opts.output === 'json') {
@@ -314,6 +357,9 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
314
357
  abort.abort();
315
358
  };
316
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;
317
363
  try {
318
364
  // ---- Pre-flight: run a pre-built plan from disk, skipping the planner.
319
365
  let preflightGraph;
@@ -326,7 +372,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
326
372
  }
327
373
  catch (e) {
328
374
  log(`plan file not found: ${planPath} (${e.message})`);
329
- return 1;
375
+ exitCode = 1;
376
+ return exitCode;
330
377
  }
331
378
  let planJson;
332
379
  try {
@@ -334,11 +381,13 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
334
381
  }
335
382
  catch (e) {
336
383
  log(`plan file is malformed JSON: ${e.message}`);
337
- return 1;
384
+ exitCode = 1;
385
+ return exitCode;
338
386
  }
339
387
  if (!planJson || !Array.isArray(planJson.nodes)) {
340
388
  log(`plan file is malformed: missing "nodes" array`);
341
- return 1;
389
+ exitCode = 1;
390
+ return exitCode;
342
391
  }
343
392
  const { createGraph, validateGraph } = await import('@zelari/core');
344
393
  const validated = createGraph(planJson.graphId ?? opts.runPlan, planJson.nodes);
@@ -377,7 +426,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
377
426
  emitEvent({ type: 'log', message: `plan_only_id=${planId}` });
378
427
  emitEvent({ type: 'log', message: `plan_only_path=${planPath}` });
379
428
  }
380
- return 0;
429
+ exitCode = 0;
430
+ return exitCode;
381
431
  }
382
432
  const audit = new AuditLogger();
383
433
  const executor = new KrakenGraphExecutor({
@@ -442,7 +492,8 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
442
492
  else {
443
493
  process.stdout.write(`${finalAscii}\n`);
444
494
  }
445
- return summary.converged ? 0 : 3;
495
+ exitCode = summary.converged ? 0 : 3;
496
+ return exitCode;
446
497
  }
447
498
  catch (err) {
448
499
  const message = err instanceof Error ? err.message : String(err);
@@ -452,18 +503,39 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
452
503
  else {
453
504
  process.stderr.write(`[zelari-code --headless] kraken graph failed: ${message}\n`);
454
505
  }
455
- return 2;
506
+ exitCode = 2;
507
+ return exitCode;
456
508
  }
457
509
  finally {
458
510
  process.off('SIGINT', onSigint);
459
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 });
460
525
  }
461
526
  }
462
- 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) {
463
534
  const cwd = opts ? resolveHeadlessCwd(opts) : process.cwd();
464
535
  const { registry: toolRegistry } = createBuiltinToolRegistry({
465
536
  root: cwd,
466
537
  planMode,
538
+ ...(extras?.lspProvider ? { lspProvider: extras.lspProvider } : {}),
467
539
  permissionPolicy: {
468
540
  read: 'allow',
469
541
  write: 'allow',
@@ -491,13 +563,18 @@ async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAut
491
563
  }
492
564
  return { toolRegistry, workspaceCtx: realCtx };
493
565
  }
494
- async function runHeadlessCouncil(opts, provider, model, providerStream) {
566
+ async function runHeadlessCouncil(opts, provider, model, providerStream, extras) {
495
567
  const { dispatchCouncil } = await import('./councilDispatcher.js');
496
568
  const sessionId = crypto.randomUUID();
497
569
  const cwd = resolveHeadlessCwd(opts);
498
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 = {};
499
574
  const nativeMemory = memoryFactory.isMemoryV2Enabled()
500
- ? await memoryFactory.getMemoryService(cwd, process.env)
575
+ ? await memoryFactory.getMemoryService(cwd, process.env, {
576
+ onEvent: memorySinkFor(spineHolder),
577
+ })
501
578
  : undefined;
502
579
  const memoryAutoWrite = memoryFactory.isMemoryAutoWriteEnabled();
503
580
  const spine = await openHeadlessSpine({
@@ -506,6 +583,8 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
506
583
  profile: opts.profile,
507
584
  workspace: cwd,
508
585
  });
586
+ // W2: bind the memory telemetry sink to the now-open spine.
587
+ spineHolder.current = spine;
509
588
  // Exit-1/E1.2: the session spine is the model-context source of truth.
510
589
  // Legacy `--history` is imported one-shot into a fresh log; prior turns
511
590
  // are then derived from events. The 1.x rolling history no longer feeds
@@ -532,7 +611,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
532
611
  process.stderr.write('[zelari-code --headless] council build soft-gate: forced design-phase ' +
533
612
  '(set ZELARI_COUNCIL_CAN_BUILD=1 to allow Lucifero implement)\n');
534
613
  }
535
- const { toolRegistry } = await buildCouncilToolRegistry(planModeFromOpts(opts) || softGated, opts, nativeMemory, memoryAutoWrite);
614
+ const { toolRegistry } = await buildCouncilToolRegistry(planModeFromOpts(opts) || softGated, opts, nativeMemory, memoryAutoWrite, extras);
536
615
  const { FeedbackStore } = await import('./councilFeedback.js');
537
616
  const feedbackStore = new FeedbackStore();
538
617
  // Multi-turn: Desktop passes --history, but council used to ignore it →
@@ -682,6 +761,8 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
682
761
  await spine.close(exitCode === 0 ? 'completed' : 'error');
683
762
  }
684
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 });
685
766
  if (opts.exportSessionPath) {
686
767
  try {
687
768
  const json = await spine.exportJson();
@@ -727,7 +808,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
727
808
  /**
728
809
  * Zelari mission loop (headless). Streams progress as `log` events + council BrainEvents.
729
810
  */
730
- async function runHeadlessZelari(opts, provider, model, providerStream) {
811
+ async function runHeadlessZelari(opts, provider, model, providerStream, extras) {
731
812
  const projectRoot = resolveHeadlessCwd(opts);
732
813
  const sessionId = opts.resumeSessionId ?? crypto.randomUUID();
733
814
  const spine = await openHeadlessSpine({
@@ -756,9 +837,11 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
756
837
  userMessage: opts.task,
757
838
  hasPlan: hasWorkspacePlan(projectRoot),
758
839
  });
759
- 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));
760
843
  const nativeMissionMemory = memory.service;
761
- 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);
762
845
  const feedbackStore = new FeedbackStore();
763
846
  const chairmanBudget = envNumber(process.env.ZELARI_MODE_MAX_TOOLS_LUCIFER, {
764
847
  default: 30,
@@ -1083,12 +1166,15 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
1083
1166
  exitCode = exitCode || 3;
1084
1167
  else if (state.status === 'success') {
1085
1168
  // ADR-0025: missions close under the strict evidence gate by default
1086
- // (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 —
1087
1171
  // the mission "success" becomes the strict exit code and the spine
1088
1172
  // records mission-strict-blocked instead of mission-success.
1089
1173
  const missionGate = await evaluateStrictBuildGate('build', {
1090
1174
  emit: (input) => spine.appendEvent(input),
1091
1175
  surface: 'mission',
1176
+ // H10-fix1: per-invocation env overlay — never process.env.
1177
+ env: strictEnvOverlay(opts),
1092
1178
  cwd: projectRoot,
1093
1179
  });
1094
1180
  const missionVerificationPayload = strictGateEventPayload(missionGate);
@@ -1130,6 +1216,8 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
1130
1216
  await spine.close(exitCode === 2 ? 'error' : 'stopped');
1131
1217
  }
1132
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 });
1133
1221
  if (opts.exportSessionPath) {
1134
1222
  try {
1135
1223
  const json = await spine.exportJson();