zelari-code 2.49.0 → 2.51.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.
- package/README.md +1 -0
- package/dist/cli/acp/command.js +127 -0
- package/dist/cli/acp/command.js.map +1 -0
- package/dist/cli/acp/eventMap.js +84 -0
- package/dist/cli/acp/eventMap.js.map +1 -0
- package/dist/cli/acp/framing.js +118 -0
- package/dist/cli/acp/framing.js.map +1 -0
- package/dist/cli/acp/protocol.js +192 -0
- package/dist/cli/acp/protocol.js.map +1 -0
- package/dist/cli/acp/server.js +229 -0
- package/dist/cli/acp/server.js.map +1 -0
- package/dist/cli/acp/turnAdapter.js +144 -0
- package/dist/cli/acp/turnAdapter.js.map +1 -0
- package/dist/cli/commands/report.js +273 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/components/statusChips.js +18 -1
- package/dist/cli/components/statusChips.js.map +1 -1
- package/dist/cli/main.bundled.js +2392 -763
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +32 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/safety/jails/baseEnv.js +132 -0
- package/dist/cli/safety/jails/baseEnv.js.map +1 -0
- package/dist/cli/safety/jails/execPath.js +116 -0
- package/dist/cli/safety/jails/execPath.js.map +1 -0
- package/dist/cli/safety/jails/linux.js +19 -21
- package/dist/cli/safety/jails/linux.js.map +1 -1
- package/dist/cli/safety/osJail.js +29 -5
- package/dist/cli/safety/osJail.js.map +1 -1
- package/dist/cli/slashCommands.js +21 -1
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/statusline.js +126 -0
- package/dist/cli/slashHandlers/statusline.js.map +1 -0
- package/dist/cli/statusline/statuslineConfig.js +169 -0
- package/dist/cli/statusline/statuslineConfig.js.map +1 -0
- package/dist/cli/statusline/statuslineCustom.js +154 -0
- package/dist/cli/statusline/statuslineCustom.js.map +1 -0
- package/dist/cli/statusline/statuslineItems.js +93 -0
- package/dist/cli/statusline/statuslineItems.js.map +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -464,6 +464,7 @@ See [`docs/plans/2026-07-01-council-workspace-cli-stubs.md`](./docs/plans/2026-0
|
|
|
464
464
|
| [PRINCIPLES.md](./PRINCIPLES.md) | First principles (P1–P6) + enforcement map |
|
|
465
465
|
| [docs/GUIDA.md](./docs/GUIDA.md) | **Full user guide** |
|
|
466
466
|
| [docs/TOOLS.md](./docs/TOOLS.md) | Tool map (builtin, workspace, MCP, SSH, plan phase) |
|
|
467
|
+
| [docs/CAPABILITIES.md](./docs/CAPABILITIES.md) | Capability matrix: shipped / experimental / planned, with in-tree evidence |
|
|
467
468
|
| [docs/media/](./docs/media/) | English marketing stills + trailer |
|
|
468
469
|
| [CONTRIBUTING.md](./CONTRIBUTING.md) | Dev setup, PR expectations |
|
|
469
470
|
| [SECURITY.md](./SECURITY.md) | Vulnerability reporting |
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acp/command — `zelari-code acp`: the Agent Client Protocol front door.
|
|
3
|
+
*
|
|
4
|
+
* Host discipline mirrors `serve` / `--serve-harness` (main.ts): no TUI, no
|
|
5
|
+
* Ink, no preflight — the transport owns stdin/stdout for the whole process
|
|
6
|
+
* lifetime. Flags are minimal and default to the existing configuration:
|
|
7
|
+
*
|
|
8
|
+
* --cwd <path> Fallback workspace for clients that omit cwd on
|
|
9
|
+
* session/new (default: the process cwd)
|
|
10
|
+
* --provider <id> Provider override (default: the active provider)
|
|
11
|
+
* --model <id> Model override (default: the provider's model)
|
|
12
|
+
* --help, -h Print this help and exit 0
|
|
13
|
+
*
|
|
14
|
+
* Exit codes: 0 on a clean shutdown (stdin EOF or SIGINT/SIGTERM), 1 on a
|
|
15
|
+
* fatal transport failure.
|
|
16
|
+
*
|
|
17
|
+
* @since 2.51.0
|
|
18
|
+
*/
|
|
19
|
+
import { createHeadlessTurnDispatcher } from './turnAdapter.js';
|
|
20
|
+
import { startAcpServer } from './server.js';
|
|
21
|
+
/** Read one `--flag <value>` pair; a missing/flag-shaped value is ignored. */
|
|
22
|
+
function readFlagValue(argv, flag) {
|
|
23
|
+
const i = argv.indexOf(flag);
|
|
24
|
+
if (i < 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
const value = argv[i + 1];
|
|
27
|
+
return typeof value === 'string' && value.length > 0 && !value.startsWith('--')
|
|
28
|
+
? value
|
|
29
|
+
: undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Parse the `acp` subcommand flags (never throws; unknown flags are ignored). */
|
|
32
|
+
export function parseAcpFlags(argv) {
|
|
33
|
+
const out = {};
|
|
34
|
+
if (argv.includes('--help') || argv.includes('-h'))
|
|
35
|
+
out.help = true;
|
|
36
|
+
const cwd = readFlagValue(argv, '--cwd');
|
|
37
|
+
const model = readFlagValue(argv, '--model');
|
|
38
|
+
const provider = readFlagValue(argv, '--provider');
|
|
39
|
+
if (cwd !== undefined)
|
|
40
|
+
out.cwd = cwd;
|
|
41
|
+
if (model !== undefined)
|
|
42
|
+
out.model = model;
|
|
43
|
+
if (provider !== undefined)
|
|
44
|
+
out.provider = provider;
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
export function acpHelpText() {
|
|
48
|
+
return ('zelari-code acp — Agent Client Protocol server on stdio (for editors)\n' +
|
|
49
|
+
'\n' +
|
|
50
|
+
'Speaks JSON-RPC 2.0 over stdin/stdout with LSP-style framing\n' +
|
|
51
|
+
'(`Content-Length: <bytes>\\r\\n\\r\\n<json>`). Point an ACP-capable editor\n' +
|
|
52
|
+
'(e.g. Zed) at this command as a custom agent server.\n' +
|
|
53
|
+
'\n' +
|
|
54
|
+
'Methods (implemented subset):\n' +
|
|
55
|
+
' initialize protocolVersion + agent capabilities\n' +
|
|
56
|
+
' session/new { cwd } -> { sessionId }\n' +
|
|
57
|
+
' session/prompt { sessionId, prompt: [{type:"text",text}] }\n' +
|
|
58
|
+
' -> { stopReason } when the turn ends\n' +
|
|
59
|
+
' session/cancel { sessionId }\n' +
|
|
60
|
+
'\n' +
|
|
61
|
+
'Notifications sent to the client: session/update with\n' +
|
|
62
|
+
'agent_message_chunk, tool_call and tool_call_update (see\n' +
|
|
63
|
+
'src/cli/acp/protocol.ts for the exact subset and its non-goals).\n' +
|
|
64
|
+
'\n' +
|
|
65
|
+
'Options:\n' +
|
|
66
|
+
' --cwd <path> Fallback workspace when a client omits cwd\n' +
|
|
67
|
+
' (default: current directory)\n' +
|
|
68
|
+
' --provider <id> Provider override (default: the active provider)\n' +
|
|
69
|
+
' --model <id> Model override (default: the provider default)\n' +
|
|
70
|
+
' --help, -h Print this help and exit\n' +
|
|
71
|
+
'\n' +
|
|
72
|
+
'Turns run through the same headless dispatch as `--headless`\n' +
|
|
73
|
+
'(kraken by default), one turn at a time; stdin EOF shuts down cleanly.\n');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Run the ACP transport until stdin closes (or a signal arrives). Never
|
|
77
|
+
* throws: a fatal boot failure is reported and mapped to exit code 1.
|
|
78
|
+
*/
|
|
79
|
+
export async function runAcpCommand(opts = {}) {
|
|
80
|
+
if (opts.help === true) {
|
|
81
|
+
process.stdout.write(acpHelpText());
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
const dispatcher = createHeadlessTurnDispatcher({
|
|
85
|
+
...(opts.model ? { model: opts.model } : {}),
|
|
86
|
+
...(opts.provider ? { provider: opts.provider } : {}),
|
|
87
|
+
});
|
|
88
|
+
return await new Promise((resolve) => {
|
|
89
|
+
let settled = false;
|
|
90
|
+
const finish = (code) => {
|
|
91
|
+
if (settled)
|
|
92
|
+
return;
|
|
93
|
+
settled = true;
|
|
94
|
+
resolve(code);
|
|
95
|
+
};
|
|
96
|
+
let handle;
|
|
97
|
+
try {
|
|
98
|
+
handle = startAcpServer({
|
|
99
|
+
dispatcher,
|
|
100
|
+
...(opts.cwd ? { fallbackCwd: opts.cwd } : {}),
|
|
101
|
+
...(opts.input ? { input: opts.input } : {}),
|
|
102
|
+
...(opts.output ? { output: opts.output } : {}),
|
|
103
|
+
log: (line) => {
|
|
104
|
+
try {
|
|
105
|
+
process.stderr.write(`${line}\n`);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* stderr closed: diagnostics are best-effort */
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
onShutdown: () => finish(0),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
process.stderr.write(`[zelari-code acp] fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
116
|
+
finish(1);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const onSignal = () => {
|
|
120
|
+
handle.close();
|
|
121
|
+
finish(0);
|
|
122
|
+
};
|
|
123
|
+
process.once('SIGINT', onSignal);
|
|
124
|
+
process.once('SIGTERM', onSignal);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/cli/acp/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acp/eventMap — pure projection from the headless NDJSON event stream onto
|
|
3
|
+
* the ACP `session/update` subset (protocol.ts).
|
|
4
|
+
*
|
|
5
|
+
* The source schema is the CLI's own documented BrainEvent stream
|
|
6
|
+
* (`@zelari/core/events`, `createBrainEvent`) — the same one the TUI projects
|
|
7
|
+
* with `src/cli/hooks/eventsToMessages.ts` and hosts parse on stdout when
|
|
8
|
+
* `--headless --output json` is used. Anchoring on it (instead of scraping
|
|
9
|
+
* stdout text) keeps the mapping stable and testable.
|
|
10
|
+
*
|
|
11
|
+
* Events outside the mapped set (log, kraken_metrics, verification_run,
|
|
12
|
+
* session_started, harness_state, …) produce NO update on purpose: the ACP
|
|
13
|
+
* client sees the message text and the tool calls, nothing else.
|
|
14
|
+
*
|
|
15
|
+
* Fail-soft: anything that is not an object with a recognised `type` maps to
|
|
16
|
+
* an empty update list. This function never throws.
|
|
17
|
+
*/
|
|
18
|
+
import { agentMessageChunk, toolCallStart, toolCallStatus } from './protocol.js';
|
|
19
|
+
/** Project one decoded headless event into zero or more ACP updates. */
|
|
20
|
+
export function mapHeadlessEvent(event) {
|
|
21
|
+
if (typeof event !== 'object' || event === null || Array.isArray(event))
|
|
22
|
+
return [];
|
|
23
|
+
const e = event;
|
|
24
|
+
switch (e['type']) {
|
|
25
|
+
case 'message_delta': {
|
|
26
|
+
const delta = e['delta'];
|
|
27
|
+
return typeof delta === 'string' && delta.length > 0 ? [agentMessageChunk(delta)] : [];
|
|
28
|
+
}
|
|
29
|
+
case 'tool_execution_start': {
|
|
30
|
+
const callId = e['toolCallId'];
|
|
31
|
+
const toolName = e['toolName'];
|
|
32
|
+
if (typeof callId !== 'string' || callId.length === 0)
|
|
33
|
+
return [];
|
|
34
|
+
const title = typeof toolName === 'string' && toolName.length > 0 ? toolName : 'tool';
|
|
35
|
+
return [toolCallStart({ callId, title })];
|
|
36
|
+
}
|
|
37
|
+
case 'tool_execution_end': {
|
|
38
|
+
const callId = e['toolCallId'];
|
|
39
|
+
if (typeof callId !== 'string' || callId.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
return [toolCallStatus({ callId, status: e['isError'] === true ? 'failed' : 'completed' })];
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Split complete lines out of `buffer`. `flush` additionally emits the
|
|
49
|
+
* trailing partial line (called once, after the turn's stdout was restored).
|
|
50
|
+
* Empty lines are skipped — the mapping is line-oriented NDJSON.
|
|
51
|
+
*/
|
|
52
|
+
export function drainLines(buffer, flush, onLine) {
|
|
53
|
+
for (;;) {
|
|
54
|
+
const nl = buffer.text.indexOf('\n');
|
|
55
|
+
if (nl === -1)
|
|
56
|
+
break;
|
|
57
|
+
const line = buffer.text.slice(0, nl).trim();
|
|
58
|
+
buffer.text = buffer.text.slice(nl + 1);
|
|
59
|
+
if (line.length > 0)
|
|
60
|
+
onLine(line);
|
|
61
|
+
}
|
|
62
|
+
if (!flush)
|
|
63
|
+
return;
|
|
64
|
+
const tail = buffer.text.trim();
|
|
65
|
+
buffer.text = '';
|
|
66
|
+
if (tail.length > 0)
|
|
67
|
+
onLine(tail);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* One captured stdout line -> updates. A line that is not JSON is treated as
|
|
71
|
+
* plain assistant text (a non-JSON writer in the turn path must not lose the
|
|
72
|
+
* message). Never throws.
|
|
73
|
+
*/
|
|
74
|
+
export function mapCapturedLine(line) {
|
|
75
|
+
let event;
|
|
76
|
+
try {
|
|
77
|
+
event = JSON.parse(line);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [agentMessageChunk(line)];
|
|
81
|
+
}
|
|
82
|
+
return mapHeadlessEvent(event);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=eventMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventMap.js","sourceRoot":"","sources":["../../../src/cli/acp/eventMap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAsB,MAAM,eAAe,CAAC;AAErG,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnF,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAClB,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YACzB,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzF,CAAC;QACD,KAAK,sBAAsB,CAAC,CAAC,CAAC;YAC5B,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;YAC/B,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YACjE,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;YACtF,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,oBAAoB,CAAC,CAAC,CAAC;YAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;YAC/B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YACjE,OAAO,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC9F,CAAC;QACD;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAOD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,MAAkB,EAClB,KAAc,EACd,MAA8B;IAE9B,SAAS,CAAC;QACR,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,MAAM;QACrB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;IACjB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createMessageParser, encodeMessage } from '../lsp/protocol.js';
|
|
2
|
+
export { createMessageParser, encodeMessage };
|
|
3
|
+
/**
|
|
4
|
+
* Frame writer. `sink.write` is bound HERE, at construction time, on purpose:
|
|
5
|
+
* an in-flight turn may temporarily patch `process.stdout.write` (see
|
|
6
|
+
* turnAdapter.ts — the headless dispatch writes its NDJSON stream there), and
|
|
7
|
+
* ACP frames must never be swallowed by that capture window.
|
|
8
|
+
*/
|
|
9
|
+
export function createFrameWriter(sink, onError) {
|
|
10
|
+
const write = sink.write.bind(sink);
|
|
11
|
+
return {
|
|
12
|
+
write(message) {
|
|
13
|
+
try {
|
|
14
|
+
write(encodeMessage(message));
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
try {
|
|
18
|
+
onError?.(err instanceof Error ? err.message : String(err));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
/* diagnostics must never throw */
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Advisory scan for frame headers the codec will have to drop. Returns one
|
|
29
|
+
* message per header block seen in `chunk` that carries no usable
|
|
30
|
+
* `Content-Length`. Pure, bounded (headers are truncated), never throws.
|
|
31
|
+
*
|
|
32
|
+
* Chunk boundaries mean a header split across two reads is simply not
|
|
33
|
+
* reported — this is a diagnostic, never a gate: the codec decides.
|
|
34
|
+
*/
|
|
35
|
+
export function malformedFrameWarnings(chunk) {
|
|
36
|
+
const out = [];
|
|
37
|
+
let rest = chunk;
|
|
38
|
+
for (;;) {
|
|
39
|
+
const headerEnd = rest.indexOf('\r\n\r\n');
|
|
40
|
+
if (headerEnd === -1)
|
|
41
|
+
return out;
|
|
42
|
+
const header = rest.slice(0, headerEnd);
|
|
43
|
+
// A JSON body never contains a raw CRLFCRLF (JSON escapes control chars),
|
|
44
|
+
// so any separator we find starts a header block.
|
|
45
|
+
if (!/Content-Length:\s*\d+/i.test(header)) {
|
|
46
|
+
out.push(`frame without a usable Content-Length: ${JSON.stringify(header.slice(0, 80))}`);
|
|
47
|
+
}
|
|
48
|
+
rest = rest.slice(headerEnd + 4);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Attach the incremental decoder to a readable stream. Chunk boundaries are
|
|
53
|
+
* irrelevant: partial frames are buffered, several frames per chunk are all
|
|
54
|
+
* delivered, and a throwing `onMessage` is contained (it cannot desync the
|
|
55
|
+
* stream).
|
|
56
|
+
*/
|
|
57
|
+
export function attachFrameReader(stream, options) {
|
|
58
|
+
const parser = createMessageParser();
|
|
59
|
+
let closed = false;
|
|
60
|
+
const report = (detail) => {
|
|
61
|
+
try {
|
|
62
|
+
options.onMalformedFrame?.(detail);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* diagnostics must never throw */
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const finish = () => {
|
|
69
|
+
if (closed)
|
|
70
|
+
return;
|
|
71
|
+
closed = true;
|
|
72
|
+
try {
|
|
73
|
+
options.onEof();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
/* shutdown is best-effort */
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const onData = (chunk) => {
|
|
80
|
+
for (const warning of malformedFrameWarnings(chunk))
|
|
81
|
+
report(warning);
|
|
82
|
+
let messages;
|
|
83
|
+
try {
|
|
84
|
+
messages = parser.push(chunk);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
report(`decoder error: ${err instanceof Error ? err.message : String(err)}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
for (const message of messages) {
|
|
91
|
+
try {
|
|
92
|
+
options.onMessage(message);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
report(`consumer error: ${err instanceof Error ? err.message : String(err)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const onError = (err) => {
|
|
100
|
+
report(`stdin error: ${err.message}`);
|
|
101
|
+
finish();
|
|
102
|
+
};
|
|
103
|
+
stream.setEncoding('utf8');
|
|
104
|
+
stream.on('data', onData);
|
|
105
|
+
stream.on('end', finish);
|
|
106
|
+
stream.on('close', finish);
|
|
107
|
+
stream.on('error', onError);
|
|
108
|
+
return {
|
|
109
|
+
detach() {
|
|
110
|
+
closed = true;
|
|
111
|
+
stream.off('data', onData);
|
|
112
|
+
stream.off('end', finish);
|
|
113
|
+
stream.off('close', finish);
|
|
114
|
+
stream.off('error', onError);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=framing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framing.js","sourceRoot":"","sources":["../../../src/cli/acp/framing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acp/protocol — the ACP subset served by `zelari-code acp`.
|
|
3
|
+
*
|
|
4
|
+
* Agent Client Protocol front door for editors (Zed and any client that
|
|
5
|
+
* speaks ACP): JSON-RPC 2.0 over stdio, framed LSP-style (see framing.ts).
|
|
6
|
+
* Implemented by hand — ZERO new dependencies (P5).
|
|
7
|
+
*
|
|
8
|
+
* ---------------------------------------------------------------------------
|
|
9
|
+
* INBOUND (client -> agent)
|
|
10
|
+
* initialize request { protocolVersion?: number }
|
|
11
|
+
* -> { protocolVersion, agentCapabilities, authMethods }
|
|
12
|
+
* session/new request { cwd: string, mcpServers?: unknown[] }
|
|
13
|
+
* -> { sessionId }
|
|
14
|
+
* session/prompt request { sessionId, prompt: ContentBlock[] }
|
|
15
|
+
* -> { stopReason } (resolved when the turn ends;
|
|
16
|
+
* the JSON-RPC loop is NOT blocked meanwhile)
|
|
17
|
+
* session/cancel request { sessionId } -> null
|
|
18
|
+
* notification { sessionId } (accepted both ways: the spec
|
|
19
|
+
* has it as a notification, a request is answered with null)
|
|
20
|
+
* initialized notification (ignored by design)
|
|
21
|
+
* $/… or anything else -> -32601. Unknown notification -> logged, no response.
|
|
22
|
+
*
|
|
23
|
+
* OUTBOUND (agent -> client), all `session/update` NOTIFICATIONS:
|
|
24
|
+
* { sessionId, update: { sessionUpdate: 'agent_message_chunk',
|
|
25
|
+
* content: { type: 'text', text } } }
|
|
26
|
+
* { sessionId, update: { sessionUpdate: 'tool_call',
|
|
27
|
+
* callId, title, kind: 'execute', status } }
|
|
28
|
+
* { sessionId, update: { sessionUpdate: 'tool_call_update', callId, status } }
|
|
29
|
+
* status is 'pending' | 'in_progress' | 'completed' | 'failed'.
|
|
30
|
+
*
|
|
31
|
+
* Errors: -32700 parse, -32600 invalid request, -32601 method not found,
|
|
32
|
+
* -32602 invalid params, -32603 internal error (the process stays alive).
|
|
33
|
+
*
|
|
34
|
+
* ---------------------------------------------------------------------------
|
|
35
|
+
* DELIBERATELY OUT OF SCOPE (this is a coherent, stable subset — not full
|
|
36
|
+
* spec fidelity; the missing pieces are named here so nobody guesses):
|
|
37
|
+
* - agent -> client REVERSE requests (session/request_permission,
|
|
38
|
+
* fs/read_text_file, fs/write_text_file, terminal/*). The agent runs its
|
|
39
|
+
* own tools through the CLI policy/permission stack instead; ACP clients
|
|
40
|
+
* therefore never get a permission prompt to answer.
|
|
41
|
+
* - session/load + session/resume (`loadSession: false` is advertised),
|
|
42
|
+
* session modes, session models, auth flows (`authMethods: []`).
|
|
43
|
+
* - client-supplied MCP servers (session/new.mcpServers) are accepted off
|
|
44
|
+
* the wire and IGNORED — project/user `.zelari/mcp.json` stays the only
|
|
45
|
+
* MCP source, same as every other CLI surface.
|
|
46
|
+
* - content blocks other than text (image, audio, resource) are skipped.
|
|
47
|
+
* - true mid-turn cancellation: `session/cancel` resolves the pending
|
|
48
|
+
* prompt with stopReason 'cancelled' and drops further updates for that
|
|
49
|
+
* turn, but the in-process headless turn itself is not force-killed
|
|
50
|
+
* (dispatchHeadlessTurn exposes no abort seam). Bounded by the CLI's own
|
|
51
|
+
* turn/tool budgets.
|
|
52
|
+
*/
|
|
53
|
+
/** ACP protocol version this agent implements (echoed back on initialize). */
|
|
54
|
+
export const ACP_PROTOCOL_VERSION = 1;
|
|
55
|
+
/** JSON-RPC 2.0 error codes used by this server. */
|
|
56
|
+
export const JSON_RPC_ERRORS = {
|
|
57
|
+
PARSE_ERROR: -32700,
|
|
58
|
+
INVALID_REQUEST: -32600,
|
|
59
|
+
METHOD_NOT_FOUND: -32601,
|
|
60
|
+
INVALID_PARAMS: -32602,
|
|
61
|
+
INTERNAL_ERROR: -32603,
|
|
62
|
+
};
|
|
63
|
+
/** Typed JSON-RPC error, mapped 1:1 onto the wire by the server loop. */
|
|
64
|
+
export class AcpError extends Error {
|
|
65
|
+
code;
|
|
66
|
+
constructor(code, message) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.code = code;
|
|
69
|
+
this.name = 'AcpError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** -32602 factory — malformed/absent params for a known method. */
|
|
73
|
+
export function invalidParams(message) {
|
|
74
|
+
return new AcpError(JSON_RPC_ERRORS.INVALID_PARAMS, message);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Classify one decoded frame as request / notification / invalid.
|
|
78
|
+
* `jsonrpc` is validated only when present (lenient boot: a client that
|
|
79
|
+
* omits the field is still understood); `method` must be a non-empty string.
|
|
80
|
+
*/
|
|
81
|
+
export function parseJsonRpcMessage(raw) {
|
|
82
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
83
|
+
return { kind: 'invalid', id: null, reason: 'message must be a JSON object' };
|
|
84
|
+
}
|
|
85
|
+
const msg = raw;
|
|
86
|
+
if (msg['jsonrpc'] !== undefined && msg['jsonrpc'] !== '2.0') {
|
|
87
|
+
return { kind: 'invalid', id: readId(msg['id']), reason: 'jsonrpc must be "2.0"' };
|
|
88
|
+
}
|
|
89
|
+
const method = msg['method'];
|
|
90
|
+
if (typeof method !== 'string' || method.length === 0) {
|
|
91
|
+
return { kind: 'invalid', id: readId(msg['id']), reason: 'method must be a non-empty string' };
|
|
92
|
+
}
|
|
93
|
+
const id = msg['id'];
|
|
94
|
+
if (id === undefined || typeof id === 'string' || typeof id === 'number' || id === null) {
|
|
95
|
+
return id === undefined
|
|
96
|
+
? {
|
|
97
|
+
kind: 'notification',
|
|
98
|
+
notification: { jsonrpc: '2.0', method, ...paramsOf(msg) },
|
|
99
|
+
}
|
|
100
|
+
: { kind: 'request', request: { jsonrpc: '2.0', id, method, ...paramsOf(msg) } };
|
|
101
|
+
}
|
|
102
|
+
return { kind: 'invalid', id: null, reason: 'id must be a string, a number or null' };
|
|
103
|
+
}
|
|
104
|
+
function paramsOf(msg) {
|
|
105
|
+
return msg['params'] === undefined ? {} : { params: msg['params'] };
|
|
106
|
+
}
|
|
107
|
+
function readId(raw) {
|
|
108
|
+
return typeof raw === 'string' || typeof raw === 'number' ? raw : null;
|
|
109
|
+
}
|
|
110
|
+
function asObject(params) {
|
|
111
|
+
return typeof params === 'object' && params !== null && !Array.isArray(params)
|
|
112
|
+
? params
|
|
113
|
+
: {};
|
|
114
|
+
}
|
|
115
|
+
/** `initialize` params — protocolVersion is optional (defaults to ours). */
|
|
116
|
+
export function readInitializeParams(params) {
|
|
117
|
+
const p = asObject(params);
|
|
118
|
+
const version = p['protocolVersion'];
|
|
119
|
+
if (version === undefined)
|
|
120
|
+
return { protocolVersion: ACP_PROTOCOL_VERSION };
|
|
121
|
+
if (typeof version !== 'number' || !Number.isFinite(version)) {
|
|
122
|
+
throw invalidParams('initialize.protocolVersion must be a number');
|
|
123
|
+
}
|
|
124
|
+
return { protocolVersion: version };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* `session/new` params — `cwd` is required by the spec; `fallbackCwd` (the
|
|
128
|
+
* CLI `--cwd` flag) covers clients that omit it. `mcpServers` is accepted
|
|
129
|
+
* and ignored (see the module header).
|
|
130
|
+
*/
|
|
131
|
+
export function readSessionNewParams(params, fallbackCwd) {
|
|
132
|
+
const p = asObject(params);
|
|
133
|
+
const cwd = p['cwd'];
|
|
134
|
+
if (typeof cwd === 'string' && cwd.trim().length > 0)
|
|
135
|
+
return { cwd: cwd.trim() };
|
|
136
|
+
if (typeof fallbackCwd === 'string' && fallbackCwd.trim().length > 0) {
|
|
137
|
+
return { cwd: fallbackCwd.trim() };
|
|
138
|
+
}
|
|
139
|
+
throw invalidParams('session/new requires a non-empty `cwd`');
|
|
140
|
+
}
|
|
141
|
+
/** `session/prompt` params — text blocks are concatenated, others skipped. */
|
|
142
|
+
export function readPromptParams(params) {
|
|
143
|
+
const p = asObject(params);
|
|
144
|
+
const sessionId = p['sessionId'];
|
|
145
|
+
if (typeof sessionId !== 'string' || sessionId.length === 0) {
|
|
146
|
+
throw invalidParams('session/prompt requires a `sessionId`');
|
|
147
|
+
}
|
|
148
|
+
const prompt = p['prompt'];
|
|
149
|
+
if (!Array.isArray(prompt)) {
|
|
150
|
+
throw invalidParams('session/prompt requires a `prompt` content-block array');
|
|
151
|
+
}
|
|
152
|
+
const parts = [];
|
|
153
|
+
for (const block of prompt) {
|
|
154
|
+
const b = asObject(block);
|
|
155
|
+
if (b['type'] === 'text' && typeof b['text'] === 'string')
|
|
156
|
+
parts.push(b['text']);
|
|
157
|
+
}
|
|
158
|
+
const text = parts.join('');
|
|
159
|
+
if (text.trim().length === 0) {
|
|
160
|
+
throw invalidParams('session/prompt requires at least one non-empty text block');
|
|
161
|
+
}
|
|
162
|
+
return { sessionId, text };
|
|
163
|
+
}
|
|
164
|
+
/** Shared reader for the session-scoped methods (cancel). */
|
|
165
|
+
export function readSessionIdParams(params, method) {
|
|
166
|
+
const p = asObject(params);
|
|
167
|
+
const sessionId = p['sessionId'];
|
|
168
|
+
if (typeof sessionId !== 'string' || sessionId.length === 0) {
|
|
169
|
+
throw invalidParams(`${method} requires a \`sessionId\``);
|
|
170
|
+
}
|
|
171
|
+
return { sessionId };
|
|
172
|
+
}
|
|
173
|
+
export function agentMessageChunk(text) {
|
|
174
|
+
return { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text } };
|
|
175
|
+
}
|
|
176
|
+
export function toolCallStart(input) {
|
|
177
|
+
return {
|
|
178
|
+
sessionUpdate: 'tool_call',
|
|
179
|
+
callId: input.callId,
|
|
180
|
+
title: input.title,
|
|
181
|
+
kind: 'execute',
|
|
182
|
+
status: input.status ?? 'pending',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
export function toolCallStatus(input) {
|
|
186
|
+
return { sessionUpdate: 'tool_call_update', callId: input.callId, status: input.status };
|
|
187
|
+
}
|
|
188
|
+
/** Wrap one update into the `session/update` notification the client expects. */
|
|
189
|
+
export function sessionUpdateNotification(sessionId, update) {
|
|
190
|
+
return { jsonrpc: '2.0', method: 'session/update', params: { sessionId, update } };
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/cli/acp/protocol.ts"],"names":[],"mappings":"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"}
|