zelari-code 1.48.1 → 2.0.0-alpha.2

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 (77) hide show
  1. package/dist/cli/app.js +4 -1
  2. package/dist/cli/app.js.map +1 -1
  3. package/dist/cli/desktopConfig.js +51 -4
  4. package/dist/cli/desktopConfig.js.map +1 -1
  5. package/dist/cli/headless.js +65 -0
  6. package/dist/cli/headless.js.map +1 -1
  7. package/dist/cli/headlessSpine.js +102 -0
  8. package/dist/cli/headlessSpine.js.map +1 -0
  9. package/dist/cli/headlessSpine.test.js +153 -0
  10. package/dist/cli/headlessSpine.test.js.map +1 -0
  11. package/dist/cli/hooks/useChatTurn.js +85 -0
  12. package/dist/cli/hooks/useChatTurn.js.map +1 -1
  13. package/dist/cli/hooks/useSession.js +16 -2
  14. package/dist/cli/hooks/useSession.js.map +1 -1
  15. package/dist/cli/kraken/candidateRegistry.js +195 -0
  16. package/dist/cli/kraken/candidateRegistry.js.map +1 -0
  17. package/dist/cli/kraken/candidateRegistry.test.js +211 -0
  18. package/dist/cli/kraken/candidateRegistry.test.js.map +1 -0
  19. package/dist/cli/kraken/completionGate.js +116 -0
  20. package/dist/cli/kraken/completionGate.js.map +1 -0
  21. package/dist/cli/kraken/completionGate.test.js +172 -0
  22. package/dist/cli/kraken/completionGate.test.js.map +1 -0
  23. package/dist/cli/kraken/metrics.js +96 -0
  24. package/dist/cli/kraken/metrics.js.map +1 -0
  25. package/dist/cli/kraken/metrics.test.js +145 -0
  26. package/dist/cli/kraken/metrics.test.js.map +1 -0
  27. package/dist/cli/kraken/selectionPlaybook.js +23 -0
  28. package/dist/cli/kraken/selectionPlaybook.js.map +1 -0
  29. package/dist/cli/kraken/selectionPlaybook.test.js +55 -0
  30. package/dist/cli/kraken/selectionPlaybook.test.js.map +1 -0
  31. package/dist/cli/kraken/turnRuntime.js +207 -0
  32. package/dist/cli/kraken/turnRuntime.js.map +1 -0
  33. package/dist/cli/kraken/turnRuntime.test.js +377 -0
  34. package/dist/cli/kraken/turnRuntime.test.js.map +1 -0
  35. package/dist/cli/kraken/verificationBridge.js +153 -0
  36. package/dist/cli/kraken/verificationBridge.js.map +1 -0
  37. package/dist/cli/kraken/verificationBridge.test.js +131 -0
  38. package/dist/cli/kraken/verificationBridge.test.js.map +1 -0
  39. package/dist/cli/kraken/verifier.js +233 -0
  40. package/dist/cli/kraken/verifier.js.map +1 -0
  41. package/dist/cli/kraken/verifier.test.js +232 -0
  42. package/dist/cli/kraken/verifier.test.js.map +1 -0
  43. package/dist/cli/kraken/verifierSettings.test.js +237 -0
  44. package/dist/cli/kraken/verifierSettings.test.js.map +1 -0
  45. package/dist/cli/kraken/verifyReport.js +115 -0
  46. package/dist/cli/kraken/verifyReport.js.map +1 -0
  47. package/dist/cli/kraken/verifyReport.test.js +111 -0
  48. package/dist/cli/kraken/verifyReport.test.js.map +1 -0
  49. package/dist/cli/main.bundled.js +4676 -943
  50. package/dist/cli/main.bundled.js.map +4 -4
  51. package/dist/cli/main.js +29 -0
  52. package/dist/cli/main.js.map +1 -1
  53. package/dist/cli/providerConfig.js +46 -0
  54. package/dist/cli/providerConfig.js.map +1 -1
  55. package/dist/cli/runHeadless.js +198 -4
  56. package/dist/cli/runHeadless.js.map +1 -1
  57. package/dist/cli/sessionSpine.js +272 -0
  58. package/dist/cli/sessionSpine.js.map +1 -0
  59. package/dist/cli/sessionSpine.test.js +171 -0
  60. package/dist/cli/sessionSpine.test.js.map +1 -0
  61. package/dist/cli/toolRegistry.js +50 -6
  62. package/dist/cli/toolRegistry.js.map +1 -1
  63. package/dist/cli/tools/krakenSelectTool.js +165 -0
  64. package/dist/cli/tools/krakenSelectTool.js.map +1 -0
  65. package/dist/cli/tools/krakenSelectTool.test.js +246 -0
  66. package/dist/cli/tools/krakenSelectTool.test.js.map +1 -0
  67. package/dist/cli/tools/taskTool.dynamicChecks.test.js +156 -0
  68. package/dist/cli/tools/taskTool.dynamicChecks.test.js.map +1 -0
  69. package/dist/cli/tools/taskTool.js +174 -6
  70. package/dist/cli/tools/taskTool.js.map +1 -1
  71. package/dist/cli/tools/taskTool.planSafety.test.js +280 -0
  72. package/dist/cli/tools/taskTool.planSafety.test.js.map +1 -0
  73. package/dist/cli/tools/taskTool.verifyReport.test.js +140 -0
  74. package/dist/cli/tools/taskTool.verifyReport.test.js.map +1 -0
  75. package/dist/cli/zelariMission.js +18 -8
  76. package/dist/cli/zelariMission.js.map +1 -1
  77. package/package.json +3 -2
@@ -0,0 +1,272 @@
1
+ import { SessionLogWriter, SessionLogLockedError, resolveSessionsDir, readSessionLog, buildProjection, deriveMessages, } from '@zelari/core/session';
2
+ import { ACTOR_AGENT, ACTOR_SYSTEM, ACTOR_USER } from '@zelari/core/session';
3
+ import path from 'node:path';
4
+ /** Kill switch — default ON in the 2.0 alpha. */
5
+ export function spineEnabled() {
6
+ return process.env.ZELARI_SESSION_SPINE !== '0';
7
+ }
8
+ /** Map one BrainEvent onto a spine event; null when not spine-relevant. */
9
+ export function mapBrainEventToSpine(ev) {
10
+ switch (ev.type) {
11
+ case 'tool_execution_start':
12
+ return {
13
+ kind: 'tool.call',
14
+ actor: ACTOR_AGENT,
15
+ data: { tool: ev.toolName, args: ev.args ?? {}, callId: ev.toolCallId },
16
+ };
17
+ case 'tool_execution_end':
18
+ return {
19
+ kind: 'tool.result',
20
+ actor: { type: 'tool' },
21
+ data: {
22
+ callId: ev.toolCallId,
23
+ output: ev.result,
24
+ ok: !ev.isError,
25
+ durationMs: ev.durationMs,
26
+ },
27
+ };
28
+ case 'session_compacted':
29
+ return {
30
+ kind: 'session.compacted',
31
+ actor: ACTOR_SYSTEM,
32
+ data: { summary: ev.summary ?? '' },
33
+ };
34
+ case 'agent_start':
35
+ return {
36
+ kind: 'note',
37
+ actor: ACTOR_SYSTEM,
38
+ data: {
39
+ note: 'agent_start',
40
+ model: ev.model,
41
+ provider: ev.provider,
42
+ ...(ev.memberName ? { member: ev.memberName } : {}),
43
+ },
44
+ };
45
+ default:
46
+ // message deltas are coalesced at message_end by the mirror;
47
+ // ui/progress/metrics events are not part of the spine vocabulary.
48
+ return null;
49
+ }
50
+ }
51
+ const MAX_STREAM_BUFFERS = 32;
52
+ /**
53
+ * Owns the spine writer for one session. All appends are serialized; every
54
+ * public method swallows errors (degrade-and-stop) — the spine must never
55
+ * take the interactive loop down with it.
56
+ */
57
+ export class SessionSpineMirror {
58
+ sessionId;
59
+ options;
60
+ writer = null;
61
+ chain = Promise.resolve();
62
+ streamBuffers = new Map();
63
+ warned = false;
64
+ status = 'disabled';
65
+ /** Seq the log continued from when adopting an existing session. */
66
+ resumedFromSeq;
67
+ sessionsDir;
68
+ constructor(sessionId, options) {
69
+ this.sessionId = sessionId;
70
+ this.options = options;
71
+ this.sessionsDir = resolveSessionsDir({ baseDir: options.baseDir });
72
+ }
73
+ /**
74
+ * Adopt a 1.x sessionId into the spine: continue the seq when the log
75
+ * already exists (resume), otherwise start fresh with `session.started`.
76
+ * Returns the mirror (never throws — check `.status`).
77
+ */
78
+ static async adopt(sessionId, options = {}) {
79
+ const mirror = new SessionSpineMirror(sessionId, options);
80
+ if (!spineEnabled())
81
+ return mirror;
82
+ try {
83
+ const sessionDir = path.join(mirror.sessionsDir, sessionId);
84
+ const report = await readSessionLog(path.join(sessionDir, 'events.jsonl'));
85
+ const existed = report.events.length > 0 || report.issues.length > 0;
86
+ const lastSeq = report.events[report.events.length - 1]?.seq ?? 0;
87
+ mirror.writer = await SessionLogWriter.open(sessionDir, sessionId, lastSeq + 1, {
88
+ now: options.now,
89
+ });
90
+ mirror.resumedFromSeq = existed ? lastSeq : undefined;
91
+ await mirror.append({
92
+ kind: existed ? 'session.resumed' : 'session.started',
93
+ actor: ACTOR_SYSTEM,
94
+ data: {
95
+ reason: existed ? 'host-resume' : 'host-bootstrap',
96
+ bridge: 'cli-dual-write',
97
+ continuedFromSeq: existed ? lastSeq : undefined,
98
+ replayIssues: report.issues.length || undefined,
99
+ ...options.extraStarted,
100
+ },
101
+ });
102
+ mirror.status = 'active';
103
+ }
104
+ catch (err) {
105
+ if (err instanceof SessionLogLockedError) {
106
+ mirror.status = 'locked';
107
+ }
108
+ else {
109
+ mirror.status = 'degraded';
110
+ mirror.warnOnce(err);
111
+ }
112
+ mirror.writer = null;
113
+ }
114
+ return mirror;
115
+ }
116
+ /** Log the user prompt — the P1 gap the 1.x log never closed. */
117
+ userMessage(text) {
118
+ void this.append({ kind: 'user.message', actor: ACTOR_USER, data: { text } });
119
+ }
120
+ /** Mirror one BrainEvent (coalescing message deltas until message_end). */
121
+ mirrorBrainEvent(ev) {
122
+ if (this.status !== 'active' || !this.writer)
123
+ return;
124
+ if (ev.type === 'message_delta') {
125
+ const key = ev.messageId;
126
+ const prev = this.streamBuffers.get(key) ?? '';
127
+ this.streamBuffers.set(key, prev + ev.delta);
128
+ return;
129
+ }
130
+ if (ev.type === 'message_end') {
131
+ const text = this.streamBuffers.get(ev.messageId);
132
+ this.streamBuffers.delete(ev.messageId);
133
+ if (this.streamBuffers.size > MAX_STREAM_BUFFERS) {
134
+ this.streamBuffers.clear(); // defensive — bounded memory
135
+ }
136
+ // The buffered text is the full body; when the buffer is missing
137
+ // (e.g. mirror started mid-stream) fall back to nothing rather than
138
+ // logging a truncated claim.
139
+ if (text !== undefined && text.length > 0) {
140
+ void this.append({
141
+ kind: 'assistant.message',
142
+ actor: ACTOR_AGENT,
143
+ data: {
144
+ text,
145
+ messageId: ev.messageId,
146
+ finishReason: ev.finishReason,
147
+ ...(ev.memberName ? { member: ev.memberName } : {}),
148
+ },
149
+ });
150
+ }
151
+ return;
152
+ }
153
+ const mapped = mapBrainEventToSpine(ev);
154
+ if (mapped)
155
+ void this.append(mapped);
156
+ }
157
+ /** Append a `verification.run` event (machine-readable completion evidence). */
158
+ verificationRun(payload) {
159
+ void this.append({ kind: 'verification.run', actor: ACTOR_SYSTEM, data: payload });
160
+ }
161
+ /** Append a `mission.phase` transition (Fase 4 mission reliability). */
162
+ missionPhase(phase, note) {
163
+ void this.append({
164
+ kind: 'mission.phase',
165
+ actor: ACTOR_SYSTEM,
166
+ data: { phase, ...(note ? { note } : {}) },
167
+ });
168
+ }
169
+ note(text, data) {
170
+ void this.append({ kind: 'note', actor: ACTOR_SYSTEM, data: { note: text, ...data } });
171
+ }
172
+ append(input) {
173
+ if (!this.writer || this.status === 'closed')
174
+ return Promise.resolve();
175
+ this.chain = this.chain
176
+ .then(() => this.writer.append(input))
177
+ .then(() => undefined)
178
+ .catch((err) => {
179
+ this.status = 'degraded';
180
+ this.writer = null;
181
+ this.warnOnce(err);
182
+ });
183
+ return this.chain;
184
+ }
185
+ warnOnce(err) {
186
+ if (this.warned || this.options.quiet)
187
+ return;
188
+ this.warned = true;
189
+ const msg = err instanceof Error ? err.message : String(err);
190
+ try {
191
+ process.stderr.write(`[zelari] session spine degraded for ${this.sessionId.slice(0, 8)}…: ${msg} (transcript continues on the 1.x log)\n`);
192
+ }
193
+ catch {
194
+ /* stderr gone — nothing more to do */
195
+ }
196
+ }
197
+ /** Flush pending appends; append `session.ended` and release the lock. */
198
+ async close(reason = 'host-exit') {
199
+ if (this.status === 'active' && this.writer) {
200
+ await this.append({ kind: 'session.ended', actor: ACTOR_SYSTEM, data: { reason } });
201
+ }
202
+ await this.chain;
203
+ const writer = this.writer;
204
+ this.writer = null;
205
+ if (this.status !== 'closed')
206
+ this.status = 'closed';
207
+ if (writer)
208
+ await writer.close().catch(() => undefined);
209
+ }
210
+ /**
211
+ * Release the lock WITHOUT appending `session.ended`.
212
+ * Used on SIGINT / abort so deriveMissionState can project
213
+ * `interrupted: true` and a later adopt continues the seq.
214
+ */
215
+ async release() {
216
+ await this.chain;
217
+ const writer = this.writer;
218
+ this.writer = null;
219
+ if (this.status === 'active')
220
+ this.status = 'closed';
221
+ if (writer)
222
+ await writer.close().catch(() => undefined);
223
+ }
224
+ }
225
+ /** Writer that dual-writes: 1.x sidecar + spine mirror. */
226
+ export class SpineMirroringWriter {
227
+ inner;
228
+ spine;
229
+ constructor(inner, spine) {
230
+ this.inner = inner;
231
+ this.spine = spine;
232
+ }
233
+ get path() {
234
+ return this.inner.path;
235
+ }
236
+ append(ev) {
237
+ const result = this.inner.append(ev);
238
+ this.spine?.mirrorBrainEvent(ev);
239
+ return result;
240
+ }
241
+ async flush() {
242
+ await this.inner.flush?.();
243
+ }
244
+ async close() {
245
+ await this.inner.close();
246
+ await this.spine?.close();
247
+ }
248
+ }
249
+ /**
250
+ * Bootstrap the dual-write writer for a TUI session: opens the spine mirror
251
+ * (best-effort) and wraps the 1.x writer so the chat loop needs no changes.
252
+ */
253
+ export async function wrapSessionWriter(inner, sessionId, options = {}) {
254
+ const spine = await SessionSpineMirror.adopt(sessionId, options);
255
+ return new SpineMirroringWriter(inner, spine.status === 'active' ? spine : null);
256
+ }
257
+ /**
258
+ * Replay the spine for resume UX (headless + TUI status line). Returns null
259
+ * when the session has no spine log yet.
260
+ */
261
+ export async function resumeSpineContext(sessionId, baseDir) {
262
+ const sessionsDir = resolveSessionsDir({ baseDir });
263
+ const eventsPath = path.join(sessionsDir, sessionId, 'events.jsonl');
264
+ const report = await readSessionLog(eventsPath).catch(() => null);
265
+ if (!report || (report.events.length === 0 && report.issues.length === 0))
266
+ return null;
267
+ return {
268
+ projection: buildProjection(report.events, report.issues),
269
+ derived: deriveMessages(report.events),
270
+ };
271
+ }
272
+ //# sourceMappingURL=sessionSpine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionSpine.js","sourceRoot":"","sources":["../../src/cli/sessionSpine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * sessionSpine tests — dual-write bridge onto the 2.0 session spine.
3
+ *
4
+ * Covers: BrainEvent→spine mapping, adopt/resume seq continuation, ownership
5
+ * lock, message-delta coalescing, user.message logging (the P1 gap), the
6
+ * declared-discrete-fallback degradation, and the kill switch.
7
+ */
8
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
9
+ import { promises as fs } from 'node:fs';
10
+ import path from 'node:path';
11
+ import os from 'node:os';
12
+ import { SessionSpineMirror, mapBrainEventToSpine, resumeSpineContext, spineEnabled, } from './sessionSpine.js';
13
+ import { readSessionLog } from '@zelari/core/session';
14
+ let tmp;
15
+ beforeEach(async () => {
16
+ tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'zelari-spine-'));
17
+ });
18
+ afterEach(async () => {
19
+ await fs.rm(tmp, { recursive: true, force: true });
20
+ });
21
+ function ev(type, rest = {}) {
22
+ return { type, id: `id-${type}`, ts: 1_755_000_000_000, sessionId: 's1', ...rest };
23
+ }
24
+ describe('mapBrainEventToSpine', () => {
25
+ it('maps tool_execution_start → tool.call with tool/args/callId', () => {
26
+ const mapped = mapBrainEventToSpine(ev('tool_execution_start', { toolCallId: 'c1', toolName: 'bash', args: { command: 'ls' } }));
27
+ expect(mapped).not.toBeNull();
28
+ expect(mapped.kind).toBe('tool.call');
29
+ expect(mapped.data).toMatchObject({ tool: 'bash', callId: 'c1' });
30
+ });
31
+ it('maps tool_execution_end → tool.result with ok inverted from isError', () => {
32
+ const mapped = mapBrainEventToSpine(ev('tool_execution_end', { toolCallId: 'c1', result: 'err', isError: true, durationMs: 5 }));
33
+ expect(mapped.kind).toBe('tool.result');
34
+ expect(mapped.data).toMatchObject({ callId: 'c1', ok: false });
35
+ });
36
+ it('drops ui/progress events (queue_update, message_start, thinking_delta)', () => {
37
+ expect(mapBrainEventToSpine(ev('queue_update', { queuedCount: 1 }))).toBeNull();
38
+ expect(mapBrainEventToSpine(ev('message_start', { messageId: 'm', role: 'assistant' }))).toBeNull();
39
+ expect(mapBrainEventToSpine(ev('thinking_delta', { messageId: 'm', delta: 'x' }))).toBeNull();
40
+ });
41
+ });
42
+ describe('SessionSpineMirror.adopt', () => {
43
+ it('bootstraps a fresh session with session.started at seq 1', async () => {
44
+ const mirror = await SessionSpineMirror.adopt('sess-a', { baseDir: tmp, quiet: true });
45
+ expect(mirror.status).toBe('active');
46
+ await mirror.close();
47
+ const report = await readSessionLog(path.join(tmp, 'sess-a', 'events.jsonl'));
48
+ expect(report.events.map((e) => e.kind)).toEqual(['session.started', 'session.ended']);
49
+ expect(report.events[0].seq).toBe(1);
50
+ });
51
+ it('resumes an existing session: session.resumed + seq continues after lastSeq', async () => {
52
+ const first = await SessionSpineMirror.adopt('sess-b', { baseDir: tmp, quiet: true });
53
+ first.userMessage('hello');
54
+ await first.close();
55
+ const second = await SessionSpineMirror.adopt('sess-b', { baseDir: tmp, quiet: true });
56
+ second.userMessage('again');
57
+ await second.close('done');
58
+ const report = await readSessionLog(path.join(tmp, 'sess-b', 'events.jsonl'));
59
+ const kinds = report.events.map((e) => e.kind);
60
+ expect(kinds).toEqual([
61
+ 'session.started',
62
+ 'user.message',
63
+ 'session.ended',
64
+ 'session.resumed',
65
+ 'user.message',
66
+ 'session.ended',
67
+ ]);
68
+ // seq must be monotonic and gap-free across the reopen
69
+ report.events.forEach((e, i) => expect(e.seq).toBe(i + 1));
70
+ });
71
+ it('a second live writer is rejected as locked (ownership)', async () => {
72
+ const first = await SessionSpineMirror.adopt('sess-c', { baseDir: tmp, quiet: true });
73
+ const second = await SessionSpineMirror.adopt('sess-c', { baseDir: tmp, quiet: true });
74
+ expect(second.status).toBe('locked');
75
+ // the locked mirror is inert, the first keeps writing
76
+ first.userMessage('still mine');
77
+ await first.close();
78
+ const report = await readSessionLog(path.join(tmp, 'sess-c', 'events.jsonl'));
79
+ expect(report.events.some((e) => e.kind === 'user.message')).toBe(true);
80
+ });
81
+ it('degrades silently when the sessions dir cannot be created', async () => {
82
+ // a FILE at the path where the directory should go → mkdir fails
83
+ const blocker = path.join(tmp, 'blocker');
84
+ await fs.writeFile(blocker, 'x', 'utf-8');
85
+ const mirror = await SessionSpineMirror.adopt('sess-d', { baseDir: blocker, quiet: true });
86
+ expect(mirror.status).toBe('degraded');
87
+ // never throws, stays inert
88
+ expect(() => mirror.userMessage('nope')).not.toThrow();
89
+ await mirror.close();
90
+ });
91
+ it('ZELARI_SESSION_SPINE=0 disables the mirror entirely', async () => {
92
+ const prev = process.env.ZELARI_SESSION_SPINE;
93
+ process.env.ZELARI_SESSION_SPINE = '0';
94
+ try {
95
+ expect(spineEnabled()).toBe(false);
96
+ const mirror = await SessionSpineMirror.adopt('sess-e', { baseDir: tmp, quiet: true });
97
+ expect(mirror.status).toBe('disabled');
98
+ await mirror.close();
99
+ await expect(fs.access(path.join(tmp, 'sess-e'))).rejects.toThrow();
100
+ }
101
+ finally {
102
+ if (prev === undefined)
103
+ delete process.env.ZELARI_SESSION_SPINE;
104
+ else
105
+ process.env.ZELARI_SESSION_SPINE = prev;
106
+ }
107
+ });
108
+ });
109
+ describe('SessionSpineMirror.mirrorBrainEvent', () => {
110
+ it('coalesces message_delta into ONE assistant.message at message_end', async () => {
111
+ const mirror = await SessionSpineMirror.adopt('sess-f', { baseDir: tmp, quiet: true });
112
+ mirror.mirrorBrainEvent(ev('message_start', { messageId: 'm1', role: 'assistant' }));
113
+ mirror.mirrorBrainEvent(ev('message_delta', { messageId: 'm1', delta: 'Hello ' }));
114
+ mirror.mirrorBrainEvent(ev('message_delta', { messageId: 'm1', delta: 'world' }));
115
+ mirror.mirrorBrainEvent(ev('message_end', { messageId: 'm1', totalLength: 11, finishReason: 'stop' }));
116
+ await mirror.close();
117
+ const report = await readSessionLog(path.join(tmp, 'sess-f', 'events.jsonl'));
118
+ const assistant = report.events.filter((e) => e.kind === 'assistant.message');
119
+ expect(assistant).toHaveLength(1);
120
+ expect(assistant[0].data.text).toBe('Hello world');
121
+ });
122
+ it('message_end without buffered deltas logs nothing (no truncated claim)', async () => {
123
+ const mirror = await SessionSpineMirror.adopt('sess-g', { baseDir: tmp, quiet: true });
124
+ mirror.mirrorBrainEvent(ev('message_end', { messageId: 'orphan', totalLength: 0, finishReason: 'stop' }));
125
+ await mirror.close();
126
+ const report = await readSessionLog(path.join(tmp, 'sess-g', 'events.jsonl'));
127
+ expect(report.events.filter((e) => e.kind === 'assistant.message')).toHaveLength(0);
128
+ });
129
+ it('mirrors tool call+result pair', async () => {
130
+ const mirror = await SessionSpineMirror.adopt('sess-h', { baseDir: tmp, quiet: true });
131
+ mirror.mirrorBrainEvent(ev('tool_execution_start', { toolCallId: 'c9', toolName: 'bash', args: {} }));
132
+ mirror.mirrorBrainEvent(ev('tool_execution_end', { toolCallId: 'c9', result: 'ok', isError: false, durationMs: 12 }));
133
+ await mirror.close();
134
+ const report = await readSessionLog(path.join(tmp, 'sess-h', 'events.jsonl'));
135
+ const kinds = report.events.map((e) => e.kind);
136
+ expect(kinds).toContain('tool.call');
137
+ expect(kinds).toContain('tool.result');
138
+ });
139
+ });
140
+ describe('resumeSpineContext', () => {
141
+ it('returns null for an unknown session and a projection for a logged one', async () => {
142
+ expect(await resumeSpineContext('nope', tmp)).toBeNull();
143
+ const mirror = await SessionSpineMirror.adopt('sess-i', { baseDir: tmp, quiet: true });
144
+ mirror.userMessage('write a test');
145
+ mirror.mirrorBrainEvent(ev('message_end', { messageId: 'm', totalLength: 2, finishReason: 'stop' }));
146
+ // note: no deltas buffered → no assistant message; add one properly
147
+ mirror.mirrorBrainEvent(ev('message_delta', { messageId: 'm2', delta: 'done' }));
148
+ mirror.mirrorBrainEvent(ev('message_end', { messageId: 'm2', totalLength: 4, finishReason: 'stop' }));
149
+ await mirror.close();
150
+ const ctx = await resumeSpineContext('sess-i', tmp);
151
+ expect(ctx).not.toBeNull();
152
+ expect(ctx.derived.map((m) => m.role)).toEqual(['user', 'assistant']);
153
+ expect(ctx.derived[0].content).toBe('write a test');
154
+ expect(ctx.projection.lastSeq).toBeGreaterThan(2);
155
+ });
156
+ });
157
+ describe('SessionSpineMirror.release', () => {
158
+ it('leaves the log without session.ended so resume can continue the seq', async () => {
159
+ const mirror = await SessionSpineMirror.adopt('sess-int', { baseDir: tmp, quiet: true });
160
+ mirror.userMessage('partial');
161
+ await mirror.release();
162
+ const { readSessionLog } = await import('@zelari/core/session');
163
+ const report = await readSessionLog(path.join(tmp, 'sess-int', 'events.jsonl'));
164
+ expect(report.events.some((e) => e.kind === 'session.ended')).toBe(false);
165
+ const again = await SessionSpineMirror.adopt('sess-int', { baseDir: tmp, quiet: true });
166
+ expect(again.status).toBe('active');
167
+ expect(again.resumedFromSeq).toBeGreaterThan(0);
168
+ await again.close('done');
169
+ });
170
+ });
171
+ //# sourceMappingURL=sessionSpine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionSpine.test.js","sourceRoot":"","sources":["../../src/cli/sessionSpine.test.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,7 @@ import { assertShellAllowed, ShellBlockedError } from './safety/shellBlocklist.j
25
25
  import { AuditLogger } from './safety/auditLogger.js';
26
26
  import { runDiagnosticsForFile, formatDiagnostics } from './diagnostics/engine.js';
27
27
  import { createTaskTool } from './tools/taskTool.js';
28
+ import { createKrakenSelectTool } from './tools/krakenSelectTool.js';
28
29
  import { createAskUserTool } from './tools/askUser.js';
29
30
  import { createSkillTool } from './tools/skillTool.js';
30
31
  import { createTodoReadTool, createTodoWriteTool } from './tools/todoTools.js';
@@ -41,6 +42,7 @@ import { createSshTools } from './ssh/tools.js';
41
42
  import { createWorldModelTools } from './workspace/worldModel.js';
42
43
  import { providerFromEnv, providerConfigFor, } from './provider/openai-compatible.js';
43
44
  import { buildProviderStream } from './provider/resolveStream.js';
45
+ import { getKrakenVerifierOverride } from './providerConfig.js';
44
46
  import { defaultPermissionPolicy, resolveToolPermission, } from './safety/toolPermissions.js';
45
47
  import { createDefaultLifecycleHooks } from './safety/lifecycleHooks.js';
46
48
  import { withResultCache } from './toolResultCache.js';
@@ -275,17 +277,26 @@ export function createBuiltinToolRegistry(options = {}) {
275
277
  });
276
278
  }
277
279
  }
278
- // The `task` sub-agent tool — parent full registry only (not plan/explore/verify/general).
279
- // Sub-agents get a profile-specific registry and never nest another `task`.
280
+ // The `task` sub-agent tool — parent full registry only (not explore/verify/general
281
+ // sub-agents). Sub-agents get a profile-specific registry and never nest another
282
+ // `task`. Fase 1 (ADR-0020): PLAN keeps the `task` tool restricted to
283
+ // explore-only (plan-safe) tentacles unless planExploreTask=false; the explore
284
+ // profile is read-only, so PLAN gains parallel research without write/execute reach.
280
285
  const enableTask = options.enableTask !== false &&
281
- !readOnly &&
286
+ options.readOnly !== true &&
282
287
  !verifyMode &&
283
288
  profile === 'full' &&
284
- !options.planMode;
289
+ (!options.planMode || options.planExploreTask !== false);
285
290
  if (enableTask) {
286
291
  const taskTool = createTaskTool({
287
- createSubAgentContext: createKrakenSubAgentContextFactory({ root, audit, sessionId }),
288
- });
292
+ createSubAgentContext: createKrakenSubAgentContextFactory({
293
+ root,
294
+ audit,
295
+ sessionId,
296
+ ...(options.subAgentProvider ? { provider: options.subAgentProvider } : {}),
297
+ ...(options.subAgentModel ? { model: options.subAgentModel } : {}),
298
+ }),
299
+ }, options.planMode === true ? { allowedAgents: ['explore'] } : undefined);
289
300
  registry.register(withPerm(taskTool));
290
301
  tools.push({
291
302
  name: taskTool.name,
@@ -293,6 +304,39 @@ export function createBuiltinToolRegistry(options = {}) {
293
304
  permissions: taskTool.permissions ?? [],
294
305
  });
295
306
  }
307
+ // Fase 4 (ADR-0020): `kraken_select` — parent Kraken ONLY. Gated on
308
+ // enableTask (full profile, not read-only, not verifyMode) AND the
309
+ // explicit krakenSelect option, so tentacles/council/zelari never see
310
+ // it. The verifier defaults to the EXACT parent provider/model
311
+ // (subAgentProvider/subAgentModel of this turn/run).
312
+ if (options.krakenSelect === true && enableTask) {
313
+ const selectTool = createKrakenSelectTool({
314
+ loadParentIdentity: async () => {
315
+ const cfg = options.subAgentProvider
316
+ ? await providerConfigFor(options.subAgentProvider)
317
+ : await providerFromEnv();
318
+ if (!cfg)
319
+ return null;
320
+ return {
321
+ provider: cfg.providerId,
322
+ model: options.subAgentModel || cfg.model,
323
+ };
324
+ },
325
+ loadStream: async (provider) => {
326
+ const cfg = await providerConfigFor(provider);
327
+ return cfg ? buildProviderStream(cfg) : null;
328
+ },
329
+ // Fase 9 (ADR-0020): persisted verifier override (provider.json
330
+ // `krakenVerifier`). undefined = inherit the EXACT parent model.
331
+ loadVerifierOverride: () => getKrakenVerifierOverride(),
332
+ });
333
+ registry.register(withPerm(selectTool));
334
+ tools.push({
335
+ name: selectTool.name,
336
+ description: selectTool.description,
337
+ permissions: selectTool.permissions ?? [],
338
+ });
339
+ }
296
340
  // LSP navigation tools (go_to_definition, find_references, hover_type,
297
341
  // document_symbols, rename_symbol). Full + plan (read-only) registry,
298
342
  // gated by ZELARI_LSP. All five tools are permissions: ['read']