zelari-code 2.0.0-alpha.4 → 2.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/headlessHistorySeed.test.js +158 -0
- package/dist/cli/headlessHistorySeed.test.js.map +1 -0
- package/dist/cli/headlessSessionEvent.test.js +83 -0
- package/dist/cli/headlessSessionEvent.test.js.map +1 -0
- package/dist/cli/headlessSpine.js +106 -1
- package/dist/cli/headlessSpine.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +58 -7
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/kraken/verificationBridge.js +38 -3
- package/dist/cli/kraken/verificationBridge.js.map +1 -1
- package/dist/cli/kraken/verificationBridge.session.test.js +104 -0
- package/dist/cli/kraken/verificationBridge.session.test.js.map +1 -0
- package/dist/cli/kraken/verificationBridge.test.js +31 -1
- package/dist/cli/kraken/verificationBridge.test.js.map +1 -1
- package/dist/cli/legacyContextIsolation.test.js +64 -0
- package/dist/cli/legacyContextIsolation.test.js.map +1 -0
- package/dist/cli/main.bundled.js +853 -580
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/providerConfig.js +58 -49
- package/dist/cli/providerConfig.js.map +1 -1
- package/dist/cli/providerConfig.test.js +85 -0
- package/dist/cli/providerConfig.test.js.map +1 -0
- package/dist/cli/runHeadless.js +57 -45
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/sessionManager.js +7 -0
- package/dist/cli/sessionManager.js.map +1 -1
- package/dist/cli/sessionReplayInvariant.test.js +187 -0
- package/dist/cli/sessionReplayInvariant.test.js.map +1 -0
- package/dist/cli/sessionSpine.js +42 -0
- package/dist/cli/sessionSpine.js.map +1 -1
- package/package.json +3 -2
|
@@ -52,46 +52,64 @@ export function getProviderConfigPath() {
|
|
|
52
52
|
return process.env.ANATHEMA_PROVIDER_CONFIG_FILE
|
|
53
53
|
?? path.join(os.homedir(), '.tmp', 'zelari-code', 'provider.json');
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
customEndpoints: mergeCustomEndpoints(parsed.customEndpoints),
|
|
73
|
-
krakenVerifier: mergeKrakenVerifier(parsed.krakenVerifier),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// Corrupt file — fall through to defaults.
|
|
79
|
-
}
|
|
55
|
+
/**
|
|
56
|
+
* Sanitize a parsed (partial) on-disk config blob into a full ProviderConfig,
|
|
57
|
+
* or clone the defaults when the blob is unusable. Shared by the sync
|
|
58
|
+
* (getProviderConfig) and async (loadProviderConfig) paths so the two can
|
|
59
|
+
* never diverge again (Exit-0 E0.1: the async path used to silently drop
|
|
60
|
+
* krakenVerifier and skip env overrides on the fallback).
|
|
61
|
+
*/
|
|
62
|
+
function mergeStoredProviderConfig(parsed) {
|
|
63
|
+
if (parsed && typeof parsed === 'object' && typeof parsed.activeProviderId === 'string'
|
|
64
|
+
&& parsed.modelByProvider && typeof parsed.modelByProvider === 'object') {
|
|
65
|
+
return {
|
|
66
|
+
activeProviderId: parsed.activeProviderId,
|
|
67
|
+
modelByProvider: { ...DEFAULTS.modelByProvider, ...parsed.modelByProvider },
|
|
68
|
+
thinkingByProvider: { ...DEFAULTS.thinkingByProvider, ...parsed.thinkingByProvider },
|
|
69
|
+
customEndpoints: mergeCustomEndpoints(parsed.customEndpoints),
|
|
70
|
+
krakenVerifier: mergeKrakenVerifier(parsed.krakenVerifier),
|
|
71
|
+
};
|
|
80
72
|
}
|
|
81
|
-
|
|
73
|
+
return cloneDefaults();
|
|
74
|
+
}
|
|
75
|
+
/** Deep-enough clone of DEFAULTS (nested records are copied, never shared). */
|
|
76
|
+
function cloneDefaults() {
|
|
77
|
+
return {
|
|
82
78
|
...DEFAULTS,
|
|
83
79
|
modelByProvider: { ...DEFAULTS.modelByProvider },
|
|
84
80
|
thinkingByProvider: { ...DEFAULTS.thinkingByProvider },
|
|
85
81
|
customEndpoints: { ...DEFAULTS.customEndpoints },
|
|
86
82
|
};
|
|
87
|
-
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Apply env overrides (ANATHEMA_ACTIVE_PROVIDER, OPENAI_MODEL) on top of a
|
|
86
|
+
* resolved config — always last, so env wins over both file and defaults.
|
|
87
|
+
* Shared by sync and async paths (Exit-0 E0.1).
|
|
88
|
+
*/
|
|
89
|
+
function applyEnvOverrides(config) {
|
|
90
|
+
const envActive = process.env.ANATHEMA_ACTIVE_PROVIDER;
|
|
91
|
+
const envModel = process.env.OPENAI_MODEL;
|
|
88
92
|
if (envActive && PROVIDERS.some((p) => p.id === envActive)) {
|
|
89
|
-
|
|
93
|
+
config.activeProviderId = envActive;
|
|
90
94
|
}
|
|
91
95
|
if (envModel && envModel.trim().length > 0) {
|
|
92
|
-
|
|
96
|
+
config.modelByProvider[config.activeProviderId] = envModel;
|
|
93
97
|
}
|
|
94
|
-
return
|
|
98
|
+
return config;
|
|
99
|
+
}
|
|
100
|
+
/** Return the resolved ProviderConfig (env override > on-disk > defaults). */
|
|
101
|
+
export function getProviderConfig() {
|
|
102
|
+
const file = getProviderConfigPath();
|
|
103
|
+
let parsed = null;
|
|
104
|
+
if (existsSync(file)) {
|
|
105
|
+
try {
|
|
106
|
+
parsed = JSON.parse(readFileSync(file, 'utf-8'));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Corrupt file — fall through to defaults.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return applyEnvOverrides(mergeStoredProviderConfig(parsed));
|
|
95
113
|
}
|
|
96
114
|
function writeProviderConfig(config) {
|
|
97
115
|
const file = getProviderConfigPath();
|
|
@@ -261,30 +279,21 @@ export function getActiveModel() {
|
|
|
261
279
|
const config = getProviderConfig();
|
|
262
280
|
return config.modelByProvider[config.activeProviderId] ?? DEFAULTS.modelByProvider[config.activeProviderId] ?? '';
|
|
263
281
|
}
|
|
264
|
-
/**
|
|
282
|
+
/**
|
|
283
|
+
* Async variant of getProviderConfig (used by tests + CLI startup).
|
|
284
|
+
* Delegates to the exact same merge/env logic as the sync path (Exit-0 E0.1):
|
|
285
|
+
* round-trip parity is guaranteed — krakenVerifier, custom endpoints and
|
|
286
|
+
* env overrides survive an async load exactly like a sync one.
|
|
287
|
+
*/
|
|
265
288
|
export async function loadProviderConfig() {
|
|
266
289
|
const file = getProviderConfigPath();
|
|
290
|
+
let parsed = null;
|
|
267
291
|
try {
|
|
268
|
-
|
|
269
|
-
const parsed = JSON.parse(raw);
|
|
270
|
-
if (parsed && typeof parsed === 'object' && typeof parsed.activeProviderId === 'string'
|
|
271
|
-
&& parsed.modelByProvider && typeof parsed.modelByProvider === 'object') {
|
|
272
|
-
return {
|
|
273
|
-
activeProviderId: parsed.activeProviderId,
|
|
274
|
-
modelByProvider: { ...DEFAULTS.modelByProvider, ...parsed.modelByProvider },
|
|
275
|
-
thinkingByProvider: { ...DEFAULTS.thinkingByProvider, ...parsed.thinkingByProvider },
|
|
276
|
-
customEndpoints: mergeCustomEndpoints(parsed.customEndpoints),
|
|
277
|
-
};
|
|
278
|
-
}
|
|
292
|
+
parsed = JSON.parse(await fs.readFile(file, 'utf-8'));
|
|
279
293
|
}
|
|
280
294
|
catch {
|
|
281
|
-
// ENOENT or JSON parse failure — fall back.
|
|
295
|
+
// ENOENT or JSON parse failure — fall back to defaults.
|
|
282
296
|
}
|
|
283
|
-
return
|
|
284
|
-
...DEFAULTS,
|
|
285
|
-
modelByProvider: { ...DEFAULTS.modelByProvider },
|
|
286
|
-
thinkingByProvider: { ...DEFAULTS.thinkingByProvider },
|
|
287
|
-
customEndpoints: { ...DEFAULTS.customEndpoints },
|
|
288
|
-
};
|
|
297
|
+
return applyEnvOverrides(mergeStoredProviderConfig(parsed));
|
|
289
298
|
}
|
|
290
299
|
//# sourceMappingURL=providerConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providerConfig.js","sourceRoot":"","sources":["../../src/cli/providerConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAwC,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAqB,MAAM,eAAe,CAAC;AA+B5F,MAAM,QAAQ,GAAmB;IAC/B,gBAAgB,EAAE,mBAAmB;IACrC,eAAe,EAAE;QACf,6DAA6D;QAC7D,mBAAmB,EAAE,UAAU;QAC/B,SAAS,EAAE,cAAc;QACzB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,iBAAiB;QAC7B,SAAS,EAAE,eAAe;QAC1B,WAAW,EAAE,mBAAmB;QAChC,QAAQ,EAAE,EAAE;KACb;IACD,kBAAkB,EAAE;QAClB,mBAAmB,EAAE,MAAM;QAC3B,SAAS,EAAE,MAAM;QACjB,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,MAAM;QACnB,QAAQ,EAAE,MAAM;KACjB;IACD,eAAe,EAAE,EAAE;CACpB,CAAC;AAEF,MAAM,UAAU,qBAAqB;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,6BAA6B;WAC3C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;AACvE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"providerConfig.js","sourceRoot":"","sources":["../../src/cli/providerConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { getProviderConfig, loadProviderConfig, setKrakenVerifier, clearKrakenVerifier, setCustomEndpoint, setActiveProviderId, } from './providerConfig.js';
|
|
6
|
+
const ENV_KEYS = ['ANATHEMA_PROVIDER_CONFIG_FILE', 'ANATHEMA_ACTIVE_PROVIDER', 'OPENAI_MODEL'];
|
|
7
|
+
/**
|
|
8
|
+
* Exit-0 E0.2 — sync/async round-trip parity for provider config.
|
|
9
|
+
* Regression guard for E0.1: `loadProviderConfig()` (async) used to silently
|
|
10
|
+
* drop `krakenVerifier` and skip env overrides on the fallback path.
|
|
11
|
+
*/
|
|
12
|
+
describe('providerConfig sync/async round-trip (E0.1/E0.2)', () => {
|
|
13
|
+
let tmpDir;
|
|
14
|
+
let savedEnv;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'zelari-providerconfig-'));
|
|
17
|
+
savedEnv = {};
|
|
18
|
+
for (const key of ENV_KEYS)
|
|
19
|
+
savedEnv[key] = process.env[key];
|
|
20
|
+
process.env.ANATHEMA_PROVIDER_CONFIG_FILE = path.join(tmpDir, 'provider.json');
|
|
21
|
+
delete process.env.ANATHEMA_ACTIVE_PROVIDER;
|
|
22
|
+
delete process.env.OPENAI_MODEL;
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
for (const key of ENV_KEYS) {
|
|
26
|
+
if (savedEnv[key] === undefined)
|
|
27
|
+
delete process.env[key];
|
|
28
|
+
else
|
|
29
|
+
process.env[key] = savedEnv[key];
|
|
30
|
+
}
|
|
31
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
it('a dedicated krakenVerifier survives an async load (E0.1 regression)', async () => {
|
|
34
|
+
setKrakenVerifier('anthropic', 'claude-sonnet-4-6');
|
|
35
|
+
const asyncCfg = await loadProviderConfig();
|
|
36
|
+
expect(asyncCfg.krakenVerifier).toEqual({ provider: 'anthropic', model: 'claude-sonnet-4-6' });
|
|
37
|
+
});
|
|
38
|
+
it('clearKrakenVerifier → back to inherit on both sync and async paths', async () => {
|
|
39
|
+
setKrakenVerifier('grok', 'grok-4.6');
|
|
40
|
+
clearKrakenVerifier();
|
|
41
|
+
expect(getProviderConfig().krakenVerifier).toBeUndefined();
|
|
42
|
+
expect((await loadProviderConfig()).krakenVerifier).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
it('sync and async loads are deep-equal for a populated config', async () => {
|
|
45
|
+
setActiveProviderId('grok');
|
|
46
|
+
setCustomEndpoint('openai-compatible', 'http://localhost:11434/v1');
|
|
47
|
+
setKrakenVerifier('anthropic', 'claude-sonnet-4-6');
|
|
48
|
+
expect(await loadProviderConfig()).toEqual(getProviderConfig());
|
|
49
|
+
});
|
|
50
|
+
it('env overrides win on both paths (async fallback included)', async () => {
|
|
51
|
+
setActiveProviderId('grok');
|
|
52
|
+
process.env.ANATHEMA_ACTIVE_PROVIDER = 'anthropic';
|
|
53
|
+
process.env.OPENAI_MODEL = 'claude-opus-4-6';
|
|
54
|
+
const syncCfg = getProviderConfig();
|
|
55
|
+
const asyncCfg = await loadProviderConfig();
|
|
56
|
+
for (const cfg of [syncCfg, asyncCfg]) {
|
|
57
|
+
expect(cfg.activeProviderId).toBe('anthropic');
|
|
58
|
+
expect(cfg.modelByProvider['anthropic']).toBe('claude-opus-4-6');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
it('missing file → cloned defaults, no shared mutable state across loads', async () => {
|
|
62
|
+
const first = await loadProviderConfig();
|
|
63
|
+
first.modelByProvider['grok'] = 'MUTATED';
|
|
64
|
+
const second = getProviderConfig();
|
|
65
|
+
expect(second.modelByProvider['grok']).not.toBe('MUTATED');
|
|
66
|
+
});
|
|
67
|
+
it('corrupt file → defaults on both paths', async () => {
|
|
68
|
+
writeFileSync(path.join(tmpDir, 'provider.json'), '{ not json', 'utf-8');
|
|
69
|
+
const asyncCfg = await loadProviderConfig();
|
|
70
|
+
const syncCfg = getProviderConfig();
|
|
71
|
+
expect(asyncCfg.activeProviderId).toBe(syncCfg.activeProviderId);
|
|
72
|
+
expect(asyncCfg.modelByProvider).toEqual(syncCfg.modelByProvider);
|
|
73
|
+
expect(asyncCfg.krakenVerifier).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
it('hand-edited partial krakenVerifier blob is dropped by both paths', async () => {
|
|
76
|
+
writeFileSync(path.join(tmpDir, 'provider.json'), JSON.stringify({
|
|
77
|
+
activeProviderId: 'grok',
|
|
78
|
+
modelByProvider: { grok: 'grok-4.6' },
|
|
79
|
+
krakenVerifier: { provider: 'anthropic' }, // model missing → invalid, must fall back to inherit
|
|
80
|
+
}), 'utf-8');
|
|
81
|
+
expect(getProviderConfig().krakenVerifier).toBeUndefined();
|
|
82
|
+
expect((await loadProviderConfig()).krakenVerifier).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=providerConfig.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providerConfig.test.js","sourceRoot":"","sources":["../../src/cli/providerConfig.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/runHeadless.js
CHANGED
|
@@ -37,8 +37,8 @@ import { writeSessionTodos } from './sessionTodos.js';
|
|
|
37
37
|
import { promises as fs } from 'node:fs';
|
|
38
38
|
import path from 'node:path';
|
|
39
39
|
import { randomUUID } from 'node:crypto';
|
|
40
|
-
import { evaluateStrictBuildGate, strictGateEventPayload } from './kraken/verificationBridge.js';
|
|
41
|
-
import { openHeadlessSpine, resolveHeadlessProfileId, } from './headlessSpine.js';
|
|
40
|
+
import { evaluateStrictBuildGate, strictGateEventPayload, strictGateExitCode } from './kraken/verificationBridge.js';
|
|
41
|
+
import { openHeadlessSpine, resolveHeadlessProfileId, seedHeadlessModelHistory, sessionStartedEvent, } from './headlessSpine.js';
|
|
42
42
|
export async function runHeadless(opts) {
|
|
43
43
|
resetTaskSpawnCount();
|
|
44
44
|
// Desktop multi-turn todo persistence: each message spawns a fresh process,
|
|
@@ -412,6 +412,14 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
412
412
|
profile: opts.profile,
|
|
413
413
|
workspace: process.cwd(),
|
|
414
414
|
});
|
|
415
|
+
// Exit-1/E1.2: the session spine is the model-context source of truth.
|
|
416
|
+
// Legacy `--history` is imported one-shot into a fresh log; prior turns
|
|
417
|
+
// are then derived from events. The 1.x rolling history no longer feeds
|
|
418
|
+
// the harness messages directly (degraded spine falls back to it).
|
|
419
|
+
const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
|
|
420
|
+
// E1.4: advertise the spine session id so hosts (Desktop) resume the
|
|
421
|
+
// same event log next turn instead of replaying 1.x history JSON.
|
|
422
|
+
emitEvent(sessionStartedEvent(spine));
|
|
415
423
|
if (opts.task)
|
|
416
424
|
spine.userMessage(opts.task);
|
|
417
425
|
// Fase 3 (ADR-0020): fresh per-run candidate registry (each headless run
|
|
@@ -582,29 +590,13 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
582
590
|
},
|
|
583
591
|
];
|
|
584
592
|
}
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
//
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
//
|
|
591
|
-
|
|
592
|
-
// Prefer user/assistant only in the seed: tool call tails from prior
|
|
593
|
-
// processes often fail provider validation or blow the slice budget and
|
|
594
|
-
// drop the actual plan text (Desktop plan→build amnesia).
|
|
595
|
-
// Preserve <think> for MiniMax-M3 multi-turn tool use (provider history).
|
|
596
|
-
const historySeed = (opts.history ?? [])
|
|
597
|
-
.filter((m) => m.role === 'user' || m.role === 'assistant')
|
|
598
|
-
.map((m) => m.role === 'assistant' && m.content
|
|
599
|
-
? {
|
|
600
|
-
role: 'assistant',
|
|
601
|
-
content: cleanAgentContent(m.content, {
|
|
602
|
-
stripQuestion: false,
|
|
603
|
-
stripThink: false,
|
|
604
|
-
}),
|
|
605
|
-
}
|
|
606
|
-
: { role: m.role, content: m.content ?? '' })
|
|
607
|
-
.filter((m) => (m.content ?? '').trim().length > 0);
|
|
593
|
+
// Exit-1/E1.2: prior turns come from the session spine (see
|
|
594
|
+
// seedHeadlessModelHistory above) — user/assistant only, assistant
|
|
595
|
+
// content scrubbed with cleanAgentContent(stripQuestion: false,
|
|
596
|
+
// stripThink: false) so ---QUESTION--- blocks and <think> survive for
|
|
597
|
+
// multi-turn binding. The legacy --history JSON is only the one-shot
|
|
598
|
+
// import source (or the declared fallback when the spine is degraded).
|
|
599
|
+
const historySeed = seededHistory.history;
|
|
608
600
|
// Short continues ("procedi", "conferma", phase plan→build) re-anchor the
|
|
609
601
|
// prior assistant output into the user message — module lastClarification
|
|
610
602
|
// is empty in a fresh headless process.
|
|
@@ -796,7 +788,10 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
796
788
|
emittedWrites: pass.emittedWrites + retry.emittedWrites,
|
|
797
789
|
};
|
|
798
790
|
}
|
|
799
|
-
//
|
|
791
|
+
// E2.2: when strict mode is on and the gate stays blocked after the repair
|
|
792
|
+
// pass, the run closes non-success (dedicated exit code + session status).
|
|
793
|
+
let strictExit = 0;
|
|
794
|
+
// Fase 8 (ADR-0020): completion gate — a BUILD turn that used selection // Fase 8 (ADR-0020): completion gate — a BUILD turn that used selection
|
|
800
795
|
// cannot cleanly finish while required checks are unresolved (fail OR
|
|
801
796
|
// unknown — a degraded observation is never proof). One automatic
|
|
802
797
|
// repair pass (budget = 1, structural), reusing the same recovery
|
|
@@ -848,6 +843,15 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
848
843
|
}
|
|
849
844
|
if (!after.blocked)
|
|
850
845
|
markRepairSucceeded();
|
|
846
|
+
else {
|
|
847
|
+
strictExit = strictGateExitCode(after);
|
|
848
|
+
const gateMsg = `[headless] Kraken BUILD: strict completion gate still blocked after repair pass — ` +
|
|
849
|
+
`closing non-success (exit ${strictExit}): ${after.summary}`;
|
|
850
|
+
if (opts.output === 'json')
|
|
851
|
+
emitEvent({ type: 'log', message: gateMsg });
|
|
852
|
+
else
|
|
853
|
+
process.stderr.write(`[zelari-code --headless] ${gateMsg}\n`);
|
|
854
|
+
}
|
|
851
855
|
}
|
|
852
856
|
}
|
|
853
857
|
progressRuntime.finish(pass.finalReason);
|
|
@@ -907,7 +911,8 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
907
911
|
}
|
|
908
912
|
}
|
|
909
913
|
try {
|
|
910
|
-
|
|
914
|
+
const closeStatus = pass.finalReason === 'error' ? 'error' : strictExit !== 0 ? 'stopped' : 'completed';
|
|
915
|
+
await spine.close(closeStatus);
|
|
911
916
|
}
|
|
912
917
|
catch { /* spine never fails the run */ }
|
|
913
918
|
if (opts.exportSessionPath) {
|
|
@@ -926,6 +931,9 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
926
931
|
}
|
|
927
932
|
if (pass.finalReason === 'error')
|
|
928
933
|
return 3;
|
|
934
|
+
// E2.2: strict done gate — a blocked verdict overrides a clean pass exit.
|
|
935
|
+
if (strictExit !== 0)
|
|
936
|
+
return strictExit;
|
|
929
937
|
return pass.exitCode;
|
|
930
938
|
}
|
|
931
939
|
async function buildCouncilToolRegistry(planMode, opts) {
|
|
@@ -965,6 +973,14 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
965
973
|
profile: opts.profile,
|
|
966
974
|
workspace: process.cwd(),
|
|
967
975
|
});
|
|
976
|
+
// Exit-1/E1.2: the session spine is the model-context source of truth.
|
|
977
|
+
// Legacy `--history` is imported one-shot into a fresh log; prior turns
|
|
978
|
+
// are then derived from events. The 1.x rolling history no longer feeds
|
|
979
|
+
// the harness messages directly (degraded spine falls back to it).
|
|
980
|
+
const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
|
|
981
|
+
// E1.4: advertise the spine session id so hosts (Desktop) resume the
|
|
982
|
+
// same event log next turn instead of replaying 1.x history JSON.
|
|
983
|
+
emitEvent(sessionStartedEvent(spine));
|
|
968
984
|
if (opts.task)
|
|
969
985
|
spine.userMessage(opts.task);
|
|
970
986
|
// Experiment: free-form council+build soft-gated to design-phase unless
|
|
@@ -986,15 +1002,9 @@ async function runHeadlessCouncil(opts, provider, model, providerStream) {
|
|
|
986
1002
|
// Multi-turn: Desktop passes --history, but council used to ignore it →
|
|
987
1003
|
// "procedi" looked like a brand-new empty request. Inject prior transcript
|
|
988
1004
|
// into the user task and emit history_snapshot for the next turn.
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
content: cleanAgentContent(m.content, {
|
|
993
|
-
stripQuestion: false,
|
|
994
|
-
stripThink: false,
|
|
995
|
-
}),
|
|
996
|
-
}
|
|
997
|
-
: m);
|
|
1005
|
+
// Exit-1/E1.2: spine-derived prior turns (legacy --history is the
|
|
1006
|
+
// one-shot import source, not the model-context brain).
|
|
1007
|
+
const historySeed = seededHistory.history;
|
|
998
1008
|
const effectiveTask = buildCouncilTaskWithHistory(opts.task, historySeed);
|
|
999
1009
|
let exitCode = 0;
|
|
1000
1010
|
const scrub = createStreamScrubber();
|
|
@@ -1128,6 +1138,14 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
|
1128
1138
|
profile: opts.profile ?? 'mission/v1',
|
|
1129
1139
|
workspace: projectRoot,
|
|
1130
1140
|
});
|
|
1141
|
+
// Exit-1/E1.2: the session spine is the model-context source of truth.
|
|
1142
|
+
// Legacy `--history` is imported one-shot into a fresh log; prior turns
|
|
1143
|
+
// are then derived from events. The 1.x rolling history no longer feeds
|
|
1144
|
+
// the harness messages directly (degraded spine falls back to it).
|
|
1145
|
+
const seededHistory = await seedHeadlessModelHistory(spine, opts.history);
|
|
1146
|
+
// E1.4: advertise the spine session id so hosts (Desktop) resume the
|
|
1147
|
+
// same event log next turn instead of replaying 1.x history JSON.
|
|
1148
|
+
emitEvent(sessionStartedEvent(spine));
|
|
1131
1149
|
if (opts.task)
|
|
1132
1150
|
spine.userMessage(opts.task);
|
|
1133
1151
|
spine.missionPhase('design', 'mission-start');
|
|
@@ -1159,15 +1177,9 @@ async function runHeadlessZelari(opts, provider, model, providerStream) {
|
|
|
1159
1177
|
process.stderr.write(message + '\n');
|
|
1160
1178
|
}
|
|
1161
1179
|
};
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
content: cleanAgentContent(m.content, {
|
|
1166
|
-
stripQuestion: false,
|
|
1167
|
-
stripThink: false,
|
|
1168
|
-
}),
|
|
1169
|
-
}
|
|
1170
|
-
: m);
|
|
1180
|
+
// Exit-1/E1.2: spine-derived prior turns (legacy --history is the
|
|
1181
|
+
// one-shot import source, not the model-context brain).
|
|
1182
|
+
const historySeed = seededHistory.history;
|
|
1171
1183
|
const missionTask = buildCouncilTaskWithHistory(opts.task, historySeed);
|
|
1172
1184
|
// Surface the brief once so the desktop UI is not blank.
|
|
1173
1185
|
emit(`[zelari] mission brief\n${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMvp?.title }, null, 0)}`);
|