lark-remote 0.1.5 → 0.1.7
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/CHANGELOG.md +39 -0
- package/README.en.md +5 -1
- package/README.md +10 -2
- package/dist/bridge/approval-coordinator.d.ts +131 -0
- package/dist/bridge/approval-coordinator.js +409 -0
- package/dist/bridge/inbound-media.d.ts +62 -0
- package/dist/bridge/inbound-media.js +282 -0
- package/dist/bridge/index.d.ts +138 -5
- package/dist/bridge/index.js +569 -65
- package/dist/bridge/queue-manager.d.ts +11 -1
- package/dist/bridge/queue-manager.js +20 -11
- package/dist/card/approval-render.d.ts +16 -0
- package/dist/card/approval-render.js +363 -0
- package/dist/card/bash-card-session.d.ts +2 -24
- package/dist/card/bash-card-session.js +6 -73
- package/dist/card/bash-renderer.js +2 -3
- package/dist/card/card-budget.js +29 -75
- package/dist/card/card-session.d.ts +48 -0
- package/dist/card/card-session.js +86 -0
- package/dist/card/card-shared.d.ts +13 -10
- package/dist/card/card-shared.js +27 -24
- package/dist/card/run-card-session.d.ts +2 -55
- package/dist/card/run-card-session.js +6 -104
- package/dist/card/run-renderer.js +140 -202
- package/dist/card/run-state.d.ts +38 -3
- package/dist/card/run-state.js +221 -11
- package/dist/card/text-truncate.d.ts +13 -12
- package/dist/card/text-truncate.js +13 -63
- package/dist/common/fs.d.ts +8 -0
- package/dist/common/fs.js +17 -0
- package/dist/common/guards.d.ts +0 -20
- package/dist/common/guards.js +0 -43
- package/dist/common/sleep.d.ts +2 -0
- package/dist/common/sleep.js +5 -0
- package/dist/common/truncate.d.ts +39 -0
- package/dist/common/truncate.js +92 -0
- package/dist/config/codex-config.d.ts +1 -1
- package/dist/config/codex-config.js +1 -1
- package/dist/config/dir.js +1 -1
- package/dist/config/index.d.ts +116 -9
- package/dist/config/index.js +122 -29
- package/dist/connector/index.d.ts +74 -2
- package/dist/connector/index.js +119 -1
- package/dist/index.js +139 -99
- package/dist/instance-lock.js +4 -12
- package/dist/order/alias-resolve.d.ts +13 -0
- package/dist/order/alias-resolve.js +22 -0
- package/dist/order/alias-store.d.ts +38 -0
- package/dist/order/alias-store.js +106 -0
- package/dist/persistence/atomic-write.d.ts +8 -2
- package/dist/persistence/atomic-write.js +23 -7
- package/dist/restart.js +2 -1
- package/dist/router/card-helpers.d.ts +63 -0
- package/dist/router/card-helpers.js +146 -0
- package/dist/router/config/claude.js +25 -1
- package/dist/router/config/codex.d.ts +1 -1
- package/dist/router/config/codex.js +33 -5
- package/dist/router/config/common/model-patch.d.ts +22 -0
- package/dist/router/config/common/model-patch.js +26 -0
- package/dist/router/config/common/render.js +9 -4
- package/dist/router/config/index.d.ts +12 -0
- package/dist/router/config/index.js +25 -1
- package/dist/router/config/kimi.d.ts +4 -1
- package/dist/router/config/kimi.js +27 -0
- package/dist/router/config/opencode.js +18 -5
- package/dist/router/config/pi.js +4 -5
- package/dist/router/config/types.d.ts +12 -3
- package/dist/router/index.d.ts +77 -17
- package/dist/router/index.js +455 -549
- package/dist/router/utils.d.ts +4 -0
- package/dist/router/utils.js +9 -2
- package/dist/runner/agent-choices-common.d.ts +16 -0
- package/dist/runner/agent-choices-common.js +31 -0
- package/dist/runner/claude/runner.d.ts +47 -10
- package/dist/runner/claude/runner.js +117 -46
- package/dist/runner/claude/session.d.ts +166 -0
- package/dist/runner/claude/session.js +749 -0
- package/dist/runner/codex/app-server/protocol-types.d.ts +519 -0
- package/dist/runner/codex/app-server/protocol-types.js +56 -0
- package/dist/runner/codex/app-server/runner.d.ts +104 -0
- package/dist/runner/codex/app-server/runner.js +377 -0
- package/dist/runner/codex/app-server/translator.d.ts +89 -0
- package/dist/runner/codex/app-server/translator.js +716 -0
- package/dist/runner/codex/index.d.ts +1 -1
- package/dist/runner/codex/index.js +1 -1
- package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
- package/dist/runner/common/acp/protocol-helpers.js +21 -0
- package/dist/runner/common/acp/protocol-types.d.ts +238 -0
- package/dist/runner/common/acp/protocol-types.js +75 -0
- package/dist/runner/common/connection-based-runner.d.ts +111 -0
- package/dist/runner/common/connection-based-runner.js +261 -0
- package/dist/runner/common/jsonrpc/client.d.ts +89 -0
- package/dist/runner/common/jsonrpc/client.js +243 -0
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
- package/dist/runner/common/jsonrpc/connection-manager.js +205 -0
- package/dist/runner/common/jsonrpc/transport.d.ts +50 -0
- package/dist/runner/common/jsonrpc/transport.js +230 -0
- package/dist/runner/common/jsonrpc/types.d.ts +32 -0
- package/dist/runner/common/jsonrpc/types.js +9 -0
- package/dist/runner/common/process-stopper.d.ts +2 -1
- package/dist/runner/common/process-stopper.js +1 -0
- package/dist/runner/common/runner-utils.d.ts +6 -13
- package/dist/runner/common/runner-utils.js +6 -47
- package/dist/runner/common/spawning-runner.d.ts +24 -70
- package/dist/runner/common/spawning-runner.js +98 -152
- package/dist/runner/common/usage.d.ts +2 -0
- package/dist/runner/common/usage.js +2 -0
- package/dist/runner/index.d.ts +5 -5
- package/dist/runner/index.js +4 -4
- package/dist/runner/kimi/acp/runner.d.ts +106 -0
- package/dist/runner/kimi/acp/runner.js +448 -0
- package/dist/runner/kimi/acp/translator.d.ts +124 -0
- package/dist/runner/kimi/acp/translator.js +336 -0
- package/dist/runner/kimi/index.d.ts +1 -1
- package/dist/runner/kimi/index.js +1 -1
- package/dist/runner/opencode/acp/index.d.ts +1 -0
- package/dist/runner/opencode/acp/index.js +2 -0
- package/dist/runner/opencode/acp/runner.d.ts +121 -0
- package/dist/runner/opencode/acp/runner.js +429 -0
- package/dist/runner/opencode/acp/translator.d.ts +119 -0
- package/dist/runner/opencode/acp/translator.js +345 -0
- package/dist/runner/opencode/index.d.ts +1 -1
- package/dist/runner/opencode/index.js +1 -1
- package/dist/runner/pi/index.d.ts +1 -1
- package/dist/runner/pi/index.js +1 -1
- package/dist/runner/pi/rpc/client.d.ts +52 -0
- package/dist/runner/pi/rpc/client.js +114 -0
- package/dist/runner/pi/rpc/index.d.ts +4 -0
- package/dist/runner/pi/rpc/index.js +4 -0
- package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
- package/dist/runner/pi/rpc/protocol-types.js +21 -0
- package/dist/runner/pi/rpc/runner.d.ts +80 -0
- package/dist/runner/pi/rpc/runner.js +199 -0
- package/dist/runner/pi/rpc/translator.d.ts +55 -0
- package/dist/runner/pi/rpc/translator.js +258 -0
- package/dist/runner/registry.d.ts +7 -8
- package/dist/runner/registry.js +3 -4
- package/dist/runner/resolve-agent-choices.js +18 -59
- package/dist/runner/sync-agent-choices.js +16 -67
- package/dist/runner/types.d.ts +153 -2
- package/dist/session/claude/session-index.js +2 -3
- package/dist/session/claude/session-reader.js +3 -6
- package/dist/session/claude/sessions.js +6 -13
- package/dist/session/codex/rollout-reader.d.ts +2 -2
- package/dist/session/codex/rollout-reader.js +57 -17
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -2
- package/dist/session/common/content-blocks.d.ts +7 -0
- package/dist/session/common/content-blocks.js +13 -9
- package/dist/session/common/jsonl.d.ts +8 -25
- package/dist/session/common/jsonl.js +126 -261
- package/dist/session/common/pagination.d.ts +28 -0
- package/dist/session/common/pagination.js +33 -0
- package/dist/session/kimi/sessions.d.ts +34 -1
- package/dist/session/kimi/sessions.js +208 -98
- package/dist/session/opencode/sessions.d.ts +17 -3
- package/dist/session/opencode/sessions.js +29 -29
- package/dist/session/pi/sessions.d.ts +5 -1
- package/dist/session/pi/sessions.js +12 -16
- package/dist/session/session-store.d.ts +11 -1
- package/dist/session/session-store.js +75 -178
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/install.d.ts +20 -2
- package/dist/update/install.js +51 -7
- package/package.json +1 -1
- package/dist/runner/codex/argv.d.ts +0 -33
- package/dist/runner/codex/argv.js +0 -40
- package/dist/runner/codex/jsonl.d.ts +0 -37
- package/dist/runner/codex/jsonl.js +0 -221
- package/dist/runner/codex/runner.d.ts +0 -63
- package/dist/runner/codex/runner.js +0 -124
- package/dist/runner/common/exec-translator.d.ts +0 -73
- package/dist/runner/common/exec-translator.js +0 -105
- package/dist/runner/kimi/jsonl.d.ts +0 -28
- package/dist/runner/kimi/jsonl.js +0 -143
- package/dist/runner/kimi/runner.d.ts +0 -49
- package/dist/runner/kimi/runner.js +0 -105
- package/dist/runner/opencode/argv.d.ts +0 -32
- package/dist/runner/opencode/argv.js +0 -35
- package/dist/runner/opencode/jsonl.d.ts +0 -68
- package/dist/runner/opencode/jsonl.js +0 -295
- package/dist/runner/opencode/runner.d.ts +0 -61
- package/dist/runner/opencode/runner.js +0 -93
- package/dist/runner/pi/jsonl.d.ts +0 -128
- package/dist/runner/pi/jsonl.js +0 -249
- package/dist/runner/pi/runner.d.ts +0 -36
- package/dist/runner/pi/runner.js +0 -76
- package/dist/session/common/catch-up.d.ts +0 -12
- package/dist/session/common/catch-up.js +0 -31
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi RPC protocol types.
|
|
3
|
+
*
|
|
4
|
+
* pi's `--mode rpc` is a headless JSON-lines protocol over stdio (NOT JSON-RPC):
|
|
5
|
+
* - client → stdin: commands `{ type, id, ... }`
|
|
6
|
+
* - server → stdout: responses `{ id, type:"response", command, success, data|error }`
|
|
7
|
+
* and events (session lifecycle / message / compaction) as bare
|
|
8
|
+
* JSON objects (no envelope).
|
|
9
|
+
*
|
|
10
|
+
* Turn semantics (verified live against pi 0.84.1, 2026-08-18):
|
|
11
|
+
* - `prompt` returns an ack (`success:true`) immediately; the turn then streams
|
|
12
|
+
* events and completes with an `agent_settled` event.
|
|
13
|
+
* - `compact` runs synchronously and returns its response at completion; it also
|
|
14
|
+
* streams `compaction_start` / `compaction_end` events.
|
|
15
|
+
* - `abort` cancels the current operation.
|
|
16
|
+
* - `get_state` returns the bound session id (`data.sessionId`) — pi binds the
|
|
17
|
+
* session at spawn via `--session-id`, so this is how a new session's id is
|
|
18
|
+
* discovered.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=protocol-types.js.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PiRpcRunner: workspace-lifetime runner driving pi in `--mode rpc`
|
|
3
|
+
* (JSON-lines protocol over stdio).
|
|
4
|
+
*
|
|
5
|
+
* Flow per run: acquire persistent connection → resolve session id (via
|
|
6
|
+
* `get_state`, or the spawn-bound `--session-id`) → synthetic init → send
|
|
7
|
+
* `prompt` (fire-and-ack) → consume pi events until `agent_settled` → result.
|
|
8
|
+
*
|
|
9
|
+
* Compact flow: acquire → `compact` command → consume `compaction_*` events →
|
|
10
|
+
* result from the compact response (completion signal).
|
|
11
|
+
*
|
|
12
|
+
* Structurally aligned with KimiAcpRunner / OpencodeAcpRunner via the shared
|
|
13
|
+
* ConnectionBasedRunner base (notification queue, forceFinish/stopRequested,
|
|
14
|
+
* turn idle watchdog, connection-lost retry). Differences:
|
|
15
|
+
* - pi binds the session at spawn via `--session-id` (no RPC resume-by-id);
|
|
16
|
+
* the PiRpcConnectionManager respawns when the requested session changes.
|
|
17
|
+
* - no approval server-requests (pi RPC exposes none in this scope) →
|
|
18
|
+
* respondApproval is a no-op.
|
|
19
|
+
* - `prompt` ack ≠ turn completion; the turn ends on `agent_settled`.
|
|
20
|
+
*/
|
|
21
|
+
import type { AgentSessionReader, AgentEvent, AgentStatusInfo, SpawnOptions } from '../../types.js';
|
|
22
|
+
import { ConnectionBasedRunner } from '../../common/connection-based-runner.js';
|
|
23
|
+
import { PiRpcClient } from './client.js';
|
|
24
|
+
import { type PiRpcTranslatorEvent } from './translator.js';
|
|
25
|
+
export interface PiRpcRunnerOptions {
|
|
26
|
+
/** LLM provider, e.g. 'Volcano'. */
|
|
27
|
+
provider?: string;
|
|
28
|
+
/** Model id or alias, e.g. 'glm-5.2'. */
|
|
29
|
+
model?: string;
|
|
30
|
+
/** Thinking level: off/minimal/low/medium/high/xhigh/max. */
|
|
31
|
+
thinking?: string;
|
|
32
|
+
/** Tool allowlist. */
|
|
33
|
+
tools?: string[];
|
|
34
|
+
workspace: string;
|
|
35
|
+
sessionReader: AgentSessionReader;
|
|
36
|
+
/** Path to the pi binary. Defaults to `pi`. */
|
|
37
|
+
binary?: string;
|
|
38
|
+
/** Environment variables to pass to the pi process. */
|
|
39
|
+
env?: Record<string, string | undefined>;
|
|
40
|
+
requestTimeoutMs?: number;
|
|
41
|
+
idleTtlMs?: number;
|
|
42
|
+
turnIdleTimeoutMs?: number;
|
|
43
|
+
}
|
|
44
|
+
export declare class PiRpcRunner extends ConnectionBasedRunner<PiRpcClient, PiRpcTranslatorEvent> {
|
|
45
|
+
private readonly manager;
|
|
46
|
+
private currentTranslator;
|
|
47
|
+
private activeSessionId;
|
|
48
|
+
private promptSettled;
|
|
49
|
+
private readonly defaultModel;
|
|
50
|
+
private readonly provider;
|
|
51
|
+
private readonly thinking;
|
|
52
|
+
constructor(opts: PiRpcRunnerOptions);
|
|
53
|
+
protected get logTag(): string;
|
|
54
|
+
protected get turnTimeoutErrorMessage(): string;
|
|
55
|
+
protected get turnInterruptedErrorMessage(): string;
|
|
56
|
+
protected currentSessionId(): string | null;
|
|
57
|
+
protected shouldDeferStop(): boolean;
|
|
58
|
+
protected cancelCurrentTurn(): Promise<void>;
|
|
59
|
+
protected clearTurnState(): void;
|
|
60
|
+
protected releaseConnection(cwd: string): Promise<void>;
|
|
61
|
+
protected notifyIdle(cwd: string): void;
|
|
62
|
+
protected disposeConnections(): Promise<void>;
|
|
63
|
+
getStatusInfo(): AgentStatusInfo;
|
|
64
|
+
/**
|
|
65
|
+
* Normal turn: acquire → resolve session id → prompt (fire-and-ack) → the
|
|
66
|
+
* `agent_settled` event produces the result.
|
|
67
|
+
*/
|
|
68
|
+
protected setupTurn(message: string, opts: SpawnOptions): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Compact the current session. The `compact` command returns a response at
|
|
71
|
+
* completion (the completion signal); `compaction_*` events stream meanwhile.
|
|
72
|
+
*/
|
|
73
|
+
runCompact(_message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
|
|
74
|
+
/**
|
|
75
|
+
* Approvals are out of scope for pi RPC (no server requests surface in this
|
|
76
|
+
* integration); the bridge's duck-typed respondApproval call is a no-op.
|
|
77
|
+
*/
|
|
78
|
+
respondApproval(_requestId: number | string, _response: unknown): Promise<void>;
|
|
79
|
+
private handleEvent;
|
|
80
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PiRpcRunner: workspace-lifetime runner driving pi in `--mode rpc`
|
|
3
|
+
* (JSON-lines protocol over stdio).
|
|
4
|
+
*
|
|
5
|
+
* Flow per run: acquire persistent connection → resolve session id (via
|
|
6
|
+
* `get_state`, or the spawn-bound `--session-id`) → synthetic init → send
|
|
7
|
+
* `prompt` (fire-and-ack) → consume pi events until `agent_settled` → result.
|
|
8
|
+
*
|
|
9
|
+
* Compact flow: acquire → `compact` command → consume `compaction_*` events →
|
|
10
|
+
* result from the compact response (completion signal).
|
|
11
|
+
*
|
|
12
|
+
* Structurally aligned with KimiAcpRunner / OpencodeAcpRunner via the shared
|
|
13
|
+
* ConnectionBasedRunner base (notification queue, forceFinish/stopRequested,
|
|
14
|
+
* turn idle watchdog, connection-lost retry). Differences:
|
|
15
|
+
* - pi binds the session at spawn via `--session-id` (no RPC resume-by-id);
|
|
16
|
+
* the PiRpcConnectionManager respawns when the requested session changes.
|
|
17
|
+
* - no approval server-requests (pi RPC exposes none in this scope) →
|
|
18
|
+
* respondApproval is a no-op.
|
|
19
|
+
* - `prompt` ack ≠ turn completion; the turn ends on `agent_settled`.
|
|
20
|
+
*/
|
|
21
|
+
import { ConnectionBasedRunner } from '../../common/connection-based-runner.js';
|
|
22
|
+
import { ConnectionManager } from '../../common/jsonrpc/connection-manager.js';
|
|
23
|
+
import { getLogger } from '../../../logger/index.js';
|
|
24
|
+
import { PiRpcClient } from './client.js';
|
|
25
|
+
import { PiRpcTranslator } from './translator.js';
|
|
26
|
+
export class PiRpcRunner extends ConnectionBasedRunner {
|
|
27
|
+
manager;
|
|
28
|
+
currentTranslator = null;
|
|
29
|
+
activeSessionId = null;
|
|
30
|
+
promptSettled = false;
|
|
31
|
+
defaultModel;
|
|
32
|
+
provider;
|
|
33
|
+
thinking;
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
super({
|
|
36
|
+
kind: 'pi',
|
|
37
|
+
sessionReader: opts.sessionReader,
|
|
38
|
+
turnIdleTimeoutMs: opts.turnIdleTimeoutMs,
|
|
39
|
+
});
|
|
40
|
+
this.defaultModel = opts.model ?? 'glm-5.2';
|
|
41
|
+
this.provider = opts.provider ?? 'Volcano';
|
|
42
|
+
this.thinking = opts.thinking ?? 'medium';
|
|
43
|
+
const tools = (opts.tools ?? ['read', 'bash', 'edit', 'write', 'grep', 'find', 'ls']).join(',');
|
|
44
|
+
this.manager = new ConnectionManager({
|
|
45
|
+
binary: opts.binary ?? 'pi',
|
|
46
|
+
buildArgs: (req) => buildPiArgs(this.provider, this.defaultModel, this.thinking, tools, req.sessionId),
|
|
47
|
+
clientFactory: ({ transport, baseOnClose, requestTimeoutMs }) => new PiRpcClient(transport, baseOnClose, requestTimeoutMs),
|
|
48
|
+
shouldReuse: ({ client, req, boundSessionId }) => req.sessionId !== undefined &&
|
|
49
|
+
boundSessionId === req.sessionId &&
|
|
50
|
+
client.ready &&
|
|
51
|
+
client.healthy,
|
|
52
|
+
env: opts.env,
|
|
53
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
54
|
+
idleTtlMs: opts.idleTtlMs,
|
|
55
|
+
logTag: 'pi-rpc-connection-manager',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
get logTag() {
|
|
59
|
+
return 'pi-rpc-runner';
|
|
60
|
+
}
|
|
61
|
+
get turnTimeoutErrorMessage() {
|
|
62
|
+
return 'Pi RPC turn timed out';
|
|
63
|
+
}
|
|
64
|
+
get turnInterruptedErrorMessage() {
|
|
65
|
+
return 'Pi RPC turn interrupted';
|
|
66
|
+
}
|
|
67
|
+
currentSessionId() {
|
|
68
|
+
return this.activeSessionId;
|
|
69
|
+
}
|
|
70
|
+
shouldDeferStop() {
|
|
71
|
+
return !this.promptSettled;
|
|
72
|
+
}
|
|
73
|
+
async cancelCurrentTurn() {
|
|
74
|
+
if (!this.currentClient || !this.activeSessionId)
|
|
75
|
+
return;
|
|
76
|
+
try {
|
|
77
|
+
this.currentClient.notify({ type: 'abort' });
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
getLogger().warn(`[${this.logTag}] abort failed: ${err.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
clearTurnState() {
|
|
84
|
+
this.currentTranslator = null;
|
|
85
|
+
this.promptSettled = false;
|
|
86
|
+
this.activeSessionId = null;
|
|
87
|
+
}
|
|
88
|
+
async releaseConnection(cwd) {
|
|
89
|
+
await this.manager.release(cwd);
|
|
90
|
+
}
|
|
91
|
+
notifyIdle(cwd) {
|
|
92
|
+
this.manager.notifyIdle(cwd);
|
|
93
|
+
}
|
|
94
|
+
async disposeConnections() {
|
|
95
|
+
await this.manager.disposeAll();
|
|
96
|
+
}
|
|
97
|
+
getStatusInfo() {
|
|
98
|
+
return {
|
|
99
|
+
kind: 'pi',
|
|
100
|
+
model: this.defaultModel,
|
|
101
|
+
provider: this.provider,
|
|
102
|
+
reasoning: this.thinking,
|
|
103
|
+
extras: { mode: 'rpc' },
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Normal turn: acquire → resolve session id → prompt (fire-and-ack) → the
|
|
108
|
+
* `agent_settled` event produces the result.
|
|
109
|
+
*/
|
|
110
|
+
async setupTurn(message, opts) {
|
|
111
|
+
const client = await this.manager.acquire(opts.cwd, { sessionId: opts.sessionId });
|
|
112
|
+
this.manager.notifyActivity(opts.cwd);
|
|
113
|
+
this.currentClient = client;
|
|
114
|
+
client.setHooks({
|
|
115
|
+
onEvent: (evt) => this.handleEvent(evt),
|
|
116
|
+
onClose: () => this.failTurn('Pi RPC connection closed'),
|
|
117
|
+
});
|
|
118
|
+
let sessionId = opts.sessionId;
|
|
119
|
+
if (!sessionId) {
|
|
120
|
+
const state = await client.request({ type: 'get_state' });
|
|
121
|
+
if (state.success && state.data) {
|
|
122
|
+
sessionId = state.data.sessionId;
|
|
123
|
+
}
|
|
124
|
+
if (!sessionId)
|
|
125
|
+
throw new Error('pi RPC did not report a session id');
|
|
126
|
+
// Bind the discovered id so a following run resumes the SAME session on
|
|
127
|
+
// this live connection instead of respawning it.
|
|
128
|
+
this.manager.bindSession(opts.cwd, sessionId);
|
|
129
|
+
}
|
|
130
|
+
this.activeSessionId = sessionId;
|
|
131
|
+
const translator = new PiRpcTranslator();
|
|
132
|
+
translator.setSessionId(sessionId);
|
|
133
|
+
this.currentTranslator = translator;
|
|
134
|
+
const turnId = `turn-${Date.now()}`;
|
|
135
|
+
this.currentTurnId = turnId;
|
|
136
|
+
this.pushEvents([translator.produceTurnStarted(sessionId, turnId)]);
|
|
137
|
+
this.promptSettled = false;
|
|
138
|
+
await client.request({ type: 'prompt', message });
|
|
139
|
+
this.promptSettled = true;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Compact the current session. The `compact` command returns a response at
|
|
143
|
+
* completion (the completion signal); `compaction_*` events stream meanwhile.
|
|
144
|
+
*/
|
|
145
|
+
async *runCompact(_message, opts) {
|
|
146
|
+
yield* this.executeTurn(opts, async () => {
|
|
147
|
+
if (!opts.sessionId) {
|
|
148
|
+
throw new Error('compact requires a sessionId');
|
|
149
|
+
}
|
|
150
|
+
const client = await this.manager.acquire(opts.cwd, { sessionId: opts.sessionId });
|
|
151
|
+
this.manager.notifyActivity(opts.cwd);
|
|
152
|
+
this.currentClient = client;
|
|
153
|
+
client.setHooks({
|
|
154
|
+
onEvent: (evt) => this.handleEvent(evt),
|
|
155
|
+
onClose: () => this.failTurn('Pi RPC connection closed'),
|
|
156
|
+
});
|
|
157
|
+
this.activeSessionId = opts.sessionId;
|
|
158
|
+
const translator = new PiRpcTranslator();
|
|
159
|
+
translator.setSessionId(opts.sessionId);
|
|
160
|
+
translator.setOperationKind('compact');
|
|
161
|
+
this.currentTranslator = translator;
|
|
162
|
+
const turnId = `compact-${Date.now()}`;
|
|
163
|
+
this.currentTurnId = turnId;
|
|
164
|
+
this.pushEvents([translator.produceTurnStarted(opts.sessionId, turnId)]);
|
|
165
|
+
this.promptSettled = false;
|
|
166
|
+
const response = await client.request({ type: 'compact' });
|
|
167
|
+
this.promptSettled = true;
|
|
168
|
+
// Guard: a stop may have already ended this turn (deferred-stop during
|
|
169
|
+
// compaction) — don't push a stale result into a (possibly reused) queue.
|
|
170
|
+
if (this.stopRequested || !this._isRunning)
|
|
171
|
+
return;
|
|
172
|
+
this.pushEvents([translator.produceCompactResult(opts.sessionId, response)]);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Approvals are out of scope for pi RPC (no server requests surface in this
|
|
177
|
+
* integration); the bridge's duck-typed respondApproval call is a no-op.
|
|
178
|
+
*/
|
|
179
|
+
async respondApproval(_requestId, _response) {
|
|
180
|
+
// no-op
|
|
181
|
+
}
|
|
182
|
+
handleEvent(evt) {
|
|
183
|
+
const events = this.currentTranslator?.handleEvent(evt) ?? [];
|
|
184
|
+
this.pushEvents(events);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** Build pi `--mode rpc` spawn args. `sessionId` is bound at spawn (pi has no
|
|
188
|
+
* RPC resume-by-id command); omitting it spawns a brand-new session. */
|
|
189
|
+
function buildPiArgs(provider, model, thinking, tools, sessionId) {
|
|
190
|
+
const args = ['--mode', 'rpc', '--provider', provider, '--model', model];
|
|
191
|
+
if (thinking && thinking !== 'off')
|
|
192
|
+
args.push('--thinking', thinking);
|
|
193
|
+
if (tools)
|
|
194
|
+
args.push('--tools', tools);
|
|
195
|
+
if (sessionId)
|
|
196
|
+
args.push('--session-id', sessionId);
|
|
197
|
+
return args;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PiRpcTranslator: maps pi RPC events into AgentEvents for the PiRpcRunner.
|
|
3
|
+
*
|
|
4
|
+
* Content mapping accumulates pi's incremental message_start/update/end events
|
|
5
|
+
* into assistant/user events with text/thinking/tool content, and drives the
|
|
6
|
+
* turn lifecycle differently:
|
|
7
|
+
* - `prompt` is fire-and-ack, so the runner produces a `result` on the
|
|
8
|
+
* `agent_settled` event (turn completion), not on a prompt response.
|
|
9
|
+
* - `compact` returns a response at completion; the runner produces the
|
|
10
|
+
* `result` from that response.
|
|
11
|
+
* - `agent_settled` is ignored for compaction turns (no double result).
|
|
12
|
+
*/
|
|
13
|
+
import type { AgentEvent, ResultEvent } from '../../types.js';
|
|
14
|
+
import { type PiRpcEvent, type PiRpcResponse } from './protocol-types.js';
|
|
15
|
+
export interface PiRpcTurnStartedEvent {
|
|
16
|
+
type: 'turn_started';
|
|
17
|
+
threadId: string;
|
|
18
|
+
turnId: string;
|
|
19
|
+
operationKind: 'turn' | 'compaction';
|
|
20
|
+
timestamp?: string;
|
|
21
|
+
}
|
|
22
|
+
export type PiRpcTranslatorEvent = AgentEvent | PiRpcTurnStartedEvent;
|
|
23
|
+
export declare class PiRpcTranslator {
|
|
24
|
+
private content;
|
|
25
|
+
private currentContentIndex;
|
|
26
|
+
private sessionId;
|
|
27
|
+
private operationKind;
|
|
28
|
+
private currentTurnId;
|
|
29
|
+
private accInput;
|
|
30
|
+
private accOutput;
|
|
31
|
+
private accCacheRead;
|
|
32
|
+
private accCacheCreation;
|
|
33
|
+
private lastTotalTokens;
|
|
34
|
+
private hasUsage;
|
|
35
|
+
/** Provider failure (stopReason="error") on the final assistant message. */
|
|
36
|
+
private lastAssistantStopReason;
|
|
37
|
+
private lastAssistantErrorMessage;
|
|
38
|
+
getSessionId(): string;
|
|
39
|
+
setSessionId(id: string): void;
|
|
40
|
+
setOperationKind(kind: 'turn' | 'compact'): void;
|
|
41
|
+
getUsage(): ResultEvent['usage'] | undefined;
|
|
42
|
+
/** Translate an incoming pi RPC event into AgentEvents to push. */
|
|
43
|
+
handleEvent(evt: PiRpcEvent): PiRpcTranslatorEvent[];
|
|
44
|
+
/** Self-produced turn_started (no wire equivalent). */
|
|
45
|
+
produceTurnStarted(sessionId: string, turnId: string): PiRpcTurnStartedEvent;
|
|
46
|
+
/** Result for a completed normal turn (agent_settled). */
|
|
47
|
+
produceResultFromSettled(): AgentEvent;
|
|
48
|
+
/** Result from a compact command response (completion signal for compaction). */
|
|
49
|
+
produceCompactResult(sessionId: string, response: PiRpcResponse): AgentEvent;
|
|
50
|
+
/** Error result (for setup/request failures). */
|
|
51
|
+
produceErrorResult(sessionId: string, errorMessage: string): AgentEvent;
|
|
52
|
+
private handleMessageEnd;
|
|
53
|
+
private addUsage;
|
|
54
|
+
private applyUpdate;
|
|
55
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PiRpcTranslator: maps pi RPC events into AgentEvents for the PiRpcRunner.
|
|
3
|
+
*
|
|
4
|
+
* Content mapping accumulates pi's incremental message_start/update/end events
|
|
5
|
+
* into assistant/user events with text/thinking/tool content, and drives the
|
|
6
|
+
* turn lifecycle differently:
|
|
7
|
+
* - `prompt` is fire-and-ack, so the runner produces a `result` on the
|
|
8
|
+
* `agent_settled` event (turn completion), not on a prompt response.
|
|
9
|
+
* - `compact` returns a response at completion; the runner produces the
|
|
10
|
+
* `result` from that response.
|
|
11
|
+
* - `agent_settled` is ignored for compaction turns (no double result).
|
|
12
|
+
*/
|
|
13
|
+
export class PiRpcTranslator {
|
|
14
|
+
content = [];
|
|
15
|
+
currentContentIndex = -1;
|
|
16
|
+
sessionId = '';
|
|
17
|
+
operationKind = 'turn';
|
|
18
|
+
currentTurnId = '';
|
|
19
|
+
// ccusage-aligned usage accumulation (per-message, summed across the turn).
|
|
20
|
+
accInput = 0;
|
|
21
|
+
accOutput = 0;
|
|
22
|
+
accCacheRead = 0;
|
|
23
|
+
accCacheCreation = 0;
|
|
24
|
+
lastTotalTokens;
|
|
25
|
+
hasUsage = false;
|
|
26
|
+
/** Provider failure (stopReason="error") on the final assistant message. */
|
|
27
|
+
lastAssistantStopReason;
|
|
28
|
+
lastAssistantErrorMessage;
|
|
29
|
+
getSessionId() {
|
|
30
|
+
return this.sessionId;
|
|
31
|
+
}
|
|
32
|
+
setSessionId(id) {
|
|
33
|
+
this.sessionId = id;
|
|
34
|
+
}
|
|
35
|
+
setOperationKind(kind) {
|
|
36
|
+
this.operationKind = kind;
|
|
37
|
+
}
|
|
38
|
+
getUsage() {
|
|
39
|
+
if (!this.hasUsage &&
|
|
40
|
+
this.accInput === 0 &&
|
|
41
|
+
this.accOutput === 0 &&
|
|
42
|
+
this.accCacheRead === 0 &&
|
|
43
|
+
this.accCacheCreation === 0 &&
|
|
44
|
+
this.lastTotalTokens === undefined) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
input_tokens: this.accInput,
|
|
49
|
+
output_tokens: this.accOutput,
|
|
50
|
+
cache_read_tokens: this.accCacheRead,
|
|
51
|
+
cache_creation_tokens: this.accCacheCreation,
|
|
52
|
+
...(this.lastTotalTokens != null ? { total_tokens: this.lastTotalTokens } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Translate an incoming pi RPC event into AgentEvents to push. */
|
|
56
|
+
handleEvent(evt) {
|
|
57
|
+
switch (evt.type) {
|
|
58
|
+
case 'session': {
|
|
59
|
+
const s = evt;
|
|
60
|
+
this.sessionId = s.id;
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
type: 'system',
|
|
64
|
+
subtype: 'init',
|
|
65
|
+
session_id: s.id,
|
|
66
|
+
cwd: s.cwd,
|
|
67
|
+
model: s.model ?? '',
|
|
68
|
+
timestamp: new Date().toISOString(),
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
case 'message_start': {
|
|
73
|
+
const m = evt.message;
|
|
74
|
+
if (m.role === 'assistant') {
|
|
75
|
+
this.content = [];
|
|
76
|
+
this.currentContentIndex = -1;
|
|
77
|
+
}
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
case 'message_update': {
|
|
81
|
+
this.applyUpdate(evt.assistantMessageEvent);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
case 'message_end': {
|
|
85
|
+
return this.handleMessageEnd(evt);
|
|
86
|
+
}
|
|
87
|
+
case 'agent_settled': {
|
|
88
|
+
// Normal-turn completion signal. Compaction turns finish via the
|
|
89
|
+
// compact response, not agent_settled.
|
|
90
|
+
if (this.operationKind === 'turn') {
|
|
91
|
+
return [this.produceResultFromSettled()];
|
|
92
|
+
}
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
default:
|
|
96
|
+
// turn_start/turn_end, agent_start/agent_end, compaction_start/end,
|
|
97
|
+
// tool_execution_*, and unknown types are ignored.
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Self-produced turn_started (no wire equivalent). */
|
|
102
|
+
produceTurnStarted(sessionId, turnId) {
|
|
103
|
+
this.sessionId = sessionId;
|
|
104
|
+
this.currentTurnId = turnId;
|
|
105
|
+
return {
|
|
106
|
+
type: 'turn_started',
|
|
107
|
+
threadId: sessionId,
|
|
108
|
+
turnId,
|
|
109
|
+
operationKind: this.operationKind === 'compact' ? 'compaction' : 'turn',
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Result for a completed normal turn (agent_settled). */
|
|
114
|
+
produceResultFromSettled() {
|
|
115
|
+
const usage = this.getUsage();
|
|
116
|
+
return {
|
|
117
|
+
type: 'result',
|
|
118
|
+
subtype: this.lastAssistantStopReason === 'error' ? 'error' : 'success',
|
|
119
|
+
session_id: this.sessionId,
|
|
120
|
+
...(this.lastAssistantStopReason === 'error'
|
|
121
|
+
? { errorMessage: this.lastAssistantErrorMessage?.trim() || 'pi reported an error' }
|
|
122
|
+
: {}),
|
|
123
|
+
...(usage ? { usage } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Result from a compact command response (completion signal for compaction). */
|
|
127
|
+
produceCompactResult(sessionId, response) {
|
|
128
|
+
if (response.success) {
|
|
129
|
+
return {
|
|
130
|
+
type: 'result',
|
|
131
|
+
subtype: 'success',
|
|
132
|
+
session_id: sessionId,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
type: 'result',
|
|
137
|
+
subtype: 'error',
|
|
138
|
+
session_id: sessionId,
|
|
139
|
+
errorMessage: response.error,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** Error result (for setup/request failures). */
|
|
143
|
+
produceErrorResult(sessionId, errorMessage) {
|
|
144
|
+
return {
|
|
145
|
+
type: 'result',
|
|
146
|
+
subtype: 'error',
|
|
147
|
+
session_id: sessionId,
|
|
148
|
+
errorMessage,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// =========================================================================
|
|
152
|
+
// Internal
|
|
153
|
+
// =========================================================================
|
|
154
|
+
handleMessageEnd(evt) {
|
|
155
|
+
const m = evt.message;
|
|
156
|
+
if (m.role === 'assistant') {
|
|
157
|
+
this.lastAssistantStopReason = m.stopReason;
|
|
158
|
+
this.lastAssistantErrorMessage = m.stopReason === 'error' ? m.errorMessage : undefined;
|
|
159
|
+
if (m.usage)
|
|
160
|
+
this.addUsage(m.usage);
|
|
161
|
+
const event = {
|
|
162
|
+
type: 'assistant',
|
|
163
|
+
message: { content: this.content },
|
|
164
|
+
timestamp: new Date().toISOString(),
|
|
165
|
+
};
|
|
166
|
+
this.content = [];
|
|
167
|
+
this.currentContentIndex = -1;
|
|
168
|
+
return [event];
|
|
169
|
+
}
|
|
170
|
+
if (m.role === 'toolResult') {
|
|
171
|
+
return [
|
|
172
|
+
{
|
|
173
|
+
type: 'user',
|
|
174
|
+
message: {
|
|
175
|
+
content: [
|
|
176
|
+
{
|
|
177
|
+
type: 'tool_result',
|
|
178
|
+
tool_use_id: m.toolCallId ?? '',
|
|
179
|
+
content: m.content ?? '',
|
|
180
|
+
is_error: m.isError ?? false,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
timestamp: new Date().toISOString(),
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
addUsage(u) {
|
|
191
|
+
this.hasUsage = true;
|
|
192
|
+
this.accInput += u.input ?? 0;
|
|
193
|
+
this.accOutput += u.output ?? 0;
|
|
194
|
+
this.accCacheRead += u.cacheRead ?? 0;
|
|
195
|
+
this.accCacheCreation += u.cacheWrite ?? 0;
|
|
196
|
+
if (u.totalTokens != null)
|
|
197
|
+
this.lastTotalTokens = u.totalTokens;
|
|
198
|
+
}
|
|
199
|
+
applyUpdate(evt) {
|
|
200
|
+
switch (evt.type) {
|
|
201
|
+
case 'text_start':
|
|
202
|
+
this.content.push({ type: 'text', text: '' });
|
|
203
|
+
this.currentContentIndex++;
|
|
204
|
+
break;
|
|
205
|
+
case 'text_delta':
|
|
206
|
+
if (this.currentContentIndex >= 0 &&
|
|
207
|
+
this.content[this.currentContentIndex]?.type === 'text') {
|
|
208
|
+
this.content[this.currentContentIndex].text += evt.delta ?? '';
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
case 'text_end':
|
|
212
|
+
if (this.currentContentIndex >= 0 &&
|
|
213
|
+
this.content[this.currentContentIndex]?.type === 'text' &&
|
|
214
|
+
evt.content) {
|
|
215
|
+
this.content[this.currentContentIndex].text = evt.content;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'thinking_start':
|
|
219
|
+
this.content.push({ type: 'thinking', thinking: '' });
|
|
220
|
+
this.currentContentIndex++;
|
|
221
|
+
break;
|
|
222
|
+
case 'thinking_delta':
|
|
223
|
+
if (this.currentContentIndex >= 0 &&
|
|
224
|
+
this.content[this.currentContentIndex]?.type === 'thinking') {
|
|
225
|
+
this.content[this.currentContentIndex].thinking +=
|
|
226
|
+
evt.delta ?? '';
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
case 'thinking_end':
|
|
230
|
+
if (this.currentContentIndex >= 0 &&
|
|
231
|
+
this.content[this.currentContentIndex]?.type === 'thinking' &&
|
|
232
|
+
evt.content) {
|
|
233
|
+
this.content[this.currentContentIndex].thinking = evt.content;
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
case 'toolcall_start':
|
|
237
|
+
this.content.push({ type: 'tool_use', id: '', name: '', input: {} });
|
|
238
|
+
this.currentContentIndex++;
|
|
239
|
+
break;
|
|
240
|
+
case 'toolcall_end':
|
|
241
|
+
if (this.currentContentIndex >= 0 &&
|
|
242
|
+
this.content[this.currentContentIndex]?.type === 'tool_use' &&
|
|
243
|
+
evt.toolCall) {
|
|
244
|
+
this.content[this.currentContentIndex] = {
|
|
245
|
+
type: 'tool_use',
|
|
246
|
+
id: evt.toolCall.id,
|
|
247
|
+
name: evt.toolCall.name,
|
|
248
|
+
input: evt.toolCall.arguments,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
default:
|
|
253
|
+
// toolcall_delta: pi provides complete arguments at toolcall_end, ignore.
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=translator.js.map
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import type { AgentKind,
|
|
1
|
+
import type { AgentKind, Runner } from './index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Registry of agent factories, keyed by `AgentKind`. `index.ts` registers one
|
|
4
4
|
* factory per available agent at startup; `Bridge.getRunner(workspace)` looks
|
|
5
5
|
* up `config.defaultAgent` here to pick the concrete runner.
|
|
6
6
|
*
|
|
7
|
-
* Singleton caching: the factory MAY cache its instance internally.
|
|
8
|
-
* current agents (claude,
|
|
9
|
-
*
|
|
10
|
-
* runner per call.
|
|
7
|
+
* Singleton caching: the factory MAY cache its instance internally. Most
|
|
8
|
+
* current agents (claude, opencode, pi, kimi) are spawn-per-message; codex is
|
|
9
|
+
* workspace-lifetime (app-server). Each factory returns a runner per call.
|
|
11
10
|
*
|
|
12
11
|
* Dynamic config reload: factory can read latest config via registry's
|
|
13
12
|
* configContainer, enabling runtime config changes to take effect.
|
|
@@ -17,7 +16,7 @@ export declare class AgentRegistry {
|
|
|
17
16
|
private readonly displayNames;
|
|
18
17
|
/** Container for runtime config updates (pi provider dynamic reload). */
|
|
19
18
|
private configContainer?;
|
|
20
|
-
register(kind: AgentKind, factory: (workspace: string) =>
|
|
19
|
+
register(kind: AgentKind, factory: (workspace: string) => Runner): void;
|
|
21
20
|
/**
|
|
22
21
|
* 注册 agent 的显示名。应在 register(kind, factory) 后调用。
|
|
23
22
|
* 用于 agentDisplayName 等全局查询,实现单点真相。
|
|
@@ -31,7 +30,7 @@ export declare class AgentRegistry {
|
|
|
31
30
|
* 设置全局 registry 实例,供 agentDisplayName 等全局函数使用。
|
|
32
31
|
* 在 index.ts 初始化所有 agents 后调用。
|
|
33
32
|
*/
|
|
34
|
-
static setGlobalInstance(reg: AgentRegistry): void;
|
|
33
|
+
static setGlobalInstance(reg: AgentRegistry | undefined): void;
|
|
35
34
|
/**
|
|
36
35
|
* 获取全局 registry 实例。
|
|
37
36
|
*/
|
|
@@ -50,5 +49,5 @@ export declare class AgentRegistry {
|
|
|
50
49
|
getConfigContainer(): {
|
|
51
50
|
current: unknown;
|
|
52
51
|
} | undefined;
|
|
53
|
-
get(kind: AgentKind, workspace: string):
|
|
52
|
+
get(kind: AgentKind, workspace: string): Runner;
|
|
54
53
|
}
|
package/dist/runner/registry.js
CHANGED
|
@@ -8,10 +8,9 @@ let globalRegistry;
|
|
|
8
8
|
* factory per available agent at startup; `Bridge.getRunner(workspace)` looks
|
|
9
9
|
* up `config.defaultAgent` here to pick the concrete runner.
|
|
10
10
|
*
|
|
11
|
-
* Singleton caching: the factory MAY cache its instance internally.
|
|
12
|
-
* current agents (claude,
|
|
13
|
-
*
|
|
14
|
-
* runner per call.
|
|
11
|
+
* Singleton caching: the factory MAY cache its instance internally. Most
|
|
12
|
+
* current agents (claude, opencode, pi, kimi) are spawn-per-message; codex is
|
|
13
|
+
* workspace-lifetime (app-server). Each factory returns a runner per call.
|
|
15
14
|
*
|
|
16
15
|
* Dynamic config reload: factory can read latest config via registry's
|
|
17
16
|
* configContainer, enabling runtime config changes to take effect.
|