agents-bridge 0.1.1 → 0.1.3
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 +12 -12
- package/config.example.json +116 -51
- package/dist/agents/agent-resolver.d.ts +12 -0
- package/dist/agents/agent-resolver.js +1 -0
- package/dist/agents/agent-resolver.js.map +1 -1
- package/dist/agents/agent-router.d.ts +8 -0
- package/dist/agents/cli-output.d.ts +35 -0
- package/dist/agents/cli-output.d.ts.map +1 -0
- package/dist/agents/cli-output.js +277 -0
- package/dist/agents/cli-output.js.map +1 -0
- package/dist/agents/cli-output.test.d.ts +2 -0
- package/dist/agents/cli-output.test.d.ts.map +1 -0
- package/dist/agents/cli-output.test.js +56 -0
- package/dist/agents/cli-output.test.js.map +1 -0
- package/dist/agents/cli-runner.d.ts +23 -0
- package/dist/agents/cli-runner.d.ts.map +1 -1
- package/dist/agents/cli-runner.js +196 -157
- package/dist/agents/cli-runner.js.map +1 -1
- package/dist/agents/cli-runner.test.d.ts +2 -0
- package/dist/agents/cli-runner.test.d.ts.map +1 -0
- package/dist/agents/cli-runner.test.js +119 -0
- package/dist/agents/cli-runner.test.js.map +1 -0
- package/dist/agents/cli-session-store.d.ts +47 -0
- package/dist/agents/cli-session-store.d.ts.map +1 -1
- package/dist/agents/cli-session-store.js +29 -0
- package/dist/agents/cli-session-store.js.map +1 -1
- package/dist/agents/cli-session-store.test.d.ts +2 -0
- package/dist/agents/cli-session-store.test.d.ts.map +1 -0
- package/dist/agents/cli-session-store.test.js +30 -0
- package/dist/agents/cli-session-store.test.js.map +1 -0
- package/dist/agents/codex-app-server-runtime.d.ts +72 -0
- package/dist/agents/codex-app-server-runtime.d.ts.map +1 -0
- package/dist/agents/codex-app-server-runtime.js +772 -0
- package/dist/agents/codex-app-server-runtime.js.map +1 -0
- package/dist/agents/codex-app-server-runtime.test.d.ts +2 -0
- package/dist/agents/codex-app-server-runtime.test.d.ts.map +1 -0
- package/dist/agents/codex-app-server-runtime.test.js +164 -0
- package/dist/agents/codex-app-server-runtime.test.js.map +1 -0
- package/dist/agents/model-selection.d.ts +3 -2
- package/dist/agents/model-selection.js +1 -1
- package/dist/channels/botlink.d.ts +2 -0
- package/dist/cli/cmd-onboard.d.ts.map +1 -1
- package/dist/cli/cmd-onboard.js +9 -78
- package/dist/cli/cmd-onboard.js.map +1 -1
- package/dist/cli/cmd-start.d.ts.map +1 -1
- package/dist/cli/cmd-start.js +154 -16
- package/dist/cli/cmd-start.js.map +1 -1
- package/dist/cli/cmd-version.d.ts +4 -0
- package/dist/cli/cmd-version.d.ts.map +1 -0
- package/dist/cli/cmd-version.js +12 -0
- package/dist/cli/cmd-version.js.map +1 -0
- package/dist/cli/run-main.d.ts +2 -1
- package/dist/cli/run-main.d.ts.map +1 -1
- package/dist/cli/run-main.js +42 -21
- package/dist/cli/run-main.js.map +1 -1
- package/dist/cli/run-main.test.d.ts +2 -0
- package/dist/cli/run-main.test.d.ts.map +1 -0
- package/dist/cli/run-main.test.js +17 -0
- package/dist/cli/run-main.test.js.map +1 -0
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/default-config.d.ts +9 -0
- package/dist/config/default-config.d.ts.map +1 -0
- package/dist/config/default-config.js +83 -0
- package/dist/config/default-config.js.map +1 -0
- package/dist/config/default-config.test.d.ts +2 -0
- package/dist/config/default-config.test.d.ts.map +1 -0
- package/dist/config/default-config.test.js +19 -0
- package/dist/config/default-config.test.js.map +1 -0
- package/dist/config/paths.d.ts +4 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +7 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/zod-schema.d.ts +656 -51
- package/dist/config/zod-schema.d.ts.map +1 -1
- package/dist/config/zod-schema.js +16 -0
- package/dist/config/zod-schema.js.map +1 -1
- package/dist/entry.d.ts +1 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +12 -1
- package/dist/entry.js.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +28 -5
- package/dist/logger.js.map +1 -1
- package/dist/types/index.d.ts +39 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/console-encoding.d.ts +0 -6
- package/dist/console-encoding.d.ts.map +0 -1
- package/dist/console-encoding.js +0 -35
- package/dist/console-encoding.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { createCliOutputProbe, parseCliJsonl, } from './cli-output.js';
|
|
4
|
+
const CODEX_BACKEND = {
|
|
5
|
+
command: 'codex',
|
|
6
|
+
output: 'jsonl',
|
|
7
|
+
sessionIdFields: ['thread_id'],
|
|
8
|
+
};
|
|
9
|
+
test('parseCliJsonl extracts codex thread id and final agent text', () => {
|
|
10
|
+
const raw = [
|
|
11
|
+
'{"type":"thread.started","thread_id":"thread-123"}',
|
|
12
|
+
'{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"OK"}}',
|
|
13
|
+
'{"type":"turn.completed","usage":{"input_tokens":10,"output_tokens":2}}',
|
|
14
|
+
].join('\n');
|
|
15
|
+
const parsed = parseCliJsonl(raw, CODEX_BACKEND);
|
|
16
|
+
assert.ok(parsed);
|
|
17
|
+
assert.equal(parsed.cliSessionId, 'thread-123');
|
|
18
|
+
assert.equal(parsed.text, 'OK');
|
|
19
|
+
assert.deepEqual(parsed.usage, {
|
|
20
|
+
input: 10,
|
|
21
|
+
output: 2,
|
|
22
|
+
cacheRead: undefined,
|
|
23
|
+
cacheWrite: undefined,
|
|
24
|
+
total: undefined,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
test('parseCliJsonl keeps codex thread id even when no final text is present', () => {
|
|
28
|
+
const raw = [
|
|
29
|
+
'{"type":"thread.started","thread_id":"thread-456"}',
|
|
30
|
+
'{"type":"turn.completed","usage":{"input_tokens":10,"output_tokens":0}}',
|
|
31
|
+
].join('\n');
|
|
32
|
+
const parsed = parseCliJsonl(raw, CODEX_BACKEND);
|
|
33
|
+
assert.ok(parsed);
|
|
34
|
+
assert.equal(parsed.cliSessionId, 'thread-456');
|
|
35
|
+
assert.equal(parsed.text, '');
|
|
36
|
+
});
|
|
37
|
+
test('createCliOutputProbe tracks reconnects, fallback, and first message timing', () => {
|
|
38
|
+
const probe = createCliOutputProbe({
|
|
39
|
+
backend: CODEX_BACKEND,
|
|
40
|
+
mode: 'jsonl',
|
|
41
|
+
});
|
|
42
|
+
probe.onStdout('{"type":"thread.started","thread_id":"thread-789"}\n', 20);
|
|
43
|
+
probe.onStdout('{"type":"error","message":"Reconnecting... 2/5 (timeout waiting for child process to exit)"}\n', 40);
|
|
44
|
+
probe.onStdout('{"type":"item.completed","item":{"id":"item_0","type":"error","message":"Falling back from WebSockets to HTTPS transport. timeout waiting for child process to exit"}}\n', 60);
|
|
45
|
+
probe.onStdout('{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"READY"}}\n', 80);
|
|
46
|
+
probe.onStdout('{"type":"turn.completed","usage":{"input_tokens":1,"output_tokens":1}}\n', 100);
|
|
47
|
+
const snapshot = probe.snapshot();
|
|
48
|
+
assert.equal(snapshot.cliSessionId, 'thread-789');
|
|
49
|
+
assert.equal(snapshot.threadStartedMs, 20);
|
|
50
|
+
assert.equal(snapshot.firstAgentMessageMs, 80);
|
|
51
|
+
assert.equal(snapshot.turnCompletedMs, 100);
|
|
52
|
+
assert.equal(snapshot.reconnectCount, 1);
|
|
53
|
+
assert.equal(snapshot.fellBackToHttps, true);
|
|
54
|
+
assert.equal(snapshot.lastText, 'READY');
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=cli-output.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-output.test.js","sourceRoot":"","sources":["../../src/agents/cli-output.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AppConfig } from '../config/zod-schema.js';
|
|
2
|
+
import type { CliBackendConfig, CliRunParams, CliRunResult, CliTurnExecutor, Logger } from '../types/index.js';
|
|
3
|
+
import { type CodexTurnRuntime } from './codex-app-server-runtime.js';
|
|
4
|
+
import { CliSessionStore } from './cli-session-store.js';
|
|
5
|
+
export declare class LocalCliTurnExecutor implements CliTurnExecutor {
|
|
6
|
+
private readonly options;
|
|
7
|
+
private codexRuntime;
|
|
8
|
+
constructor(options: {
|
|
9
|
+
config: Pick<AppConfig, 'agents'>;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
sessionStore?: CliSessionStore;
|
|
12
|
+
defaultTimeoutMs?: number;
|
|
13
|
+
createCodexRuntime?: (backend: CliBackendConfig) => CodexTurnRuntime;
|
|
14
|
+
});
|
|
15
|
+
run(params: CliRunParams): Promise<CliRunResult>;
|
|
16
|
+
private emitProcessing;
|
|
17
|
+
private executeTurn;
|
|
18
|
+
teardown(): Promise<void>;
|
|
19
|
+
private getCodexRuntime;
|
|
20
|
+
private executeCodexAppServerTurn;
|
|
21
|
+
private executeCliExecTurn;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=cli-runner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-runner.d.ts","sourceRoot":"","sources":["../../src/agents/cli-runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"cli-runner.d.ts","sourceRoot":"","sources":["../../src/agents/cli-runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EACV,gBAAgB,EAGhB,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,MAAM,EAEP,MAAM,mBAAmB,CAAA;AAc1B,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,eAAe,EAAwB,MAAM,wBAAwB,CAAA;AAkB9E,qBAAa,oBAAqB,YAAW,eAAe;IAIxD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,OAAO,CAAC,YAAY,CAAgC;gBAGjC,OAAO,EAAE;QACxB,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;QACjC,MAAM,EAAE,MAAM,CAAA;QACd,YAAY,CAAC,EAAE,eAAe,CAAA;QAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAA;QACzB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAA;KACrE;IAGG,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;YAyHxC,cAAc;YAId,WAAW;IAqBnB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,OAAO,CAAC,eAAe;YAUT,yBAAyB;YA+DzB,kBAAkB;CAwIjC"}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { CLI_FRESH_WATCHDOG_DEFAULTS, CLI_RESUME_WATCHDOG_DEFAULTS, CLI_WATCHDOG_MIN_TIMEOUT_MS, normalizeProviderId, resolveCliBackendConfig, } from './cli-backends.js';
|
|
4
|
+
import { createCliOutputProbe, isSessionExpiredError, parseCliOutput, } from './cli-output.js';
|
|
5
|
+
import { CodexAppServerRuntime, } from './codex-app-server-runtime.js';
|
|
4
6
|
import { CliSessionStore, parseAgentSessionKey } from './cli-session-store.js';
|
|
5
7
|
const CLI_RUN_QUEUES = new Map();
|
|
6
8
|
const DEFAULT_TIMEOUT_MS = 15 * 60 * 1_000;
|
|
7
|
-
const SESSION_EXPIRED_PATTERNS = [
|
|
8
|
-
/session expired/i,
|
|
9
|
-
/invalid session/i,
|
|
10
|
-
/conversation not found/i,
|
|
11
|
-
/thread not found/i,
|
|
12
|
-
/resume session not found/i,
|
|
13
|
-
];
|
|
14
9
|
class CliExecutionError extends Error {
|
|
15
10
|
reason;
|
|
16
11
|
stdout;
|
|
@@ -27,6 +22,7 @@ class CliExecutionError extends Error {
|
|
|
27
22
|
}
|
|
28
23
|
export class LocalCliTurnExecutor {
|
|
29
24
|
options;
|
|
25
|
+
codexRuntime = null;
|
|
30
26
|
constructor(options) {
|
|
31
27
|
this.options = options;
|
|
32
28
|
}
|
|
@@ -39,21 +35,28 @@ export class LocalCliTurnExecutor {
|
|
|
39
35
|
}
|
|
40
36
|
const sessionStore = this.options.sessionStore ?? new CliSessionStore();
|
|
41
37
|
const envelope = params.envelope;
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
await sessionStore.getWithLegacyFallback(envelope.sessionId,
|
|
38
|
+
let existingSessionEntry = await sessionStore.get(envelope.sessionId);
|
|
39
|
+
const parsedAgentSession = parseAgentSessionKey(envelope.sessionId);
|
|
40
|
+
if (!existingSessionEntry && parsedAgentSession) {
|
|
41
|
+
existingSessionEntry = await sessionStore.getWithLegacyFallback(envelope.sessionId, parsedAgentSession.originalKey);
|
|
46
42
|
}
|
|
47
43
|
const sessionEntry = await sessionStore.ensureSession(envelope.sessionId, {
|
|
48
44
|
provider: providerId,
|
|
49
45
|
model: params.model,
|
|
50
46
|
});
|
|
51
47
|
const existingCliSessionId = await sessionStore.getCliSessionId(envelope.sessionId, providerId);
|
|
48
|
+
if (providerId === 'codex-cli' && !existingCliSessionId && existingSessionEntry?.provider === providerId) {
|
|
49
|
+
this.options.logger.warn({
|
|
50
|
+
provider: providerId,
|
|
51
|
+
sessionId: envelope.sessionId,
|
|
52
|
+
runtimeSessionId: sessionEntry.runtimeSessionId,
|
|
53
|
+
}, 'No persisted Codex thread id found for existing botlink session; next turn will start fresh');
|
|
54
|
+
}
|
|
52
55
|
await this.emitProcessing(callbacks);
|
|
53
56
|
const queueKey = backendResolved.config.serialize === false
|
|
54
57
|
? `${backendResolved.id}:${randomUUID()}`
|
|
55
58
|
: `${backendResolved.id}:${envelope.sessionId}`;
|
|
56
|
-
return enqueueCliRun(queueKey, async () => {
|
|
59
|
+
return enqueueCliRun(queueKey, async ({ queueWaitMs }) => {
|
|
57
60
|
try {
|
|
58
61
|
const result = await this.executeTurn({
|
|
59
62
|
envelope,
|
|
@@ -64,6 +67,7 @@ export class LocalCliTurnExecutor {
|
|
|
64
67
|
systemPrompt: params.systemPrompt,
|
|
65
68
|
backend: backendResolved.config,
|
|
66
69
|
cliSessionId: existingCliSessionId,
|
|
70
|
+
queueWaitMs,
|
|
67
71
|
runtimeSessionId: sessionEntry.runtimeSessionId,
|
|
68
72
|
agentEnv: params.agentEnv,
|
|
69
73
|
agentClearEnv: params.agentClearEnv,
|
|
@@ -73,6 +77,15 @@ export class LocalCliTurnExecutor {
|
|
|
73
77
|
model: params.model,
|
|
74
78
|
});
|
|
75
79
|
}
|
|
80
|
+
else if (providerId === 'codex-cli') {
|
|
81
|
+
this.options.logger.warn({
|
|
82
|
+
provider: providerId,
|
|
83
|
+
sessionId: envelope.sessionId,
|
|
84
|
+
runtimeSessionId: sessionEntry.runtimeSessionId,
|
|
85
|
+
isResume: result.isResume,
|
|
86
|
+
diagnostics: result.diagnostics,
|
|
87
|
+
}, 'Codex turn completed without a persisted thread id; future turns will remain fresh');
|
|
88
|
+
}
|
|
76
89
|
else {
|
|
77
90
|
await sessionStore.ensureSession(envelope.sessionId, {
|
|
78
91
|
provider: providerId,
|
|
@@ -84,8 +97,8 @@ export class LocalCliTurnExecutor {
|
|
|
84
97
|
catch (error) {
|
|
85
98
|
if (existingCliSessionId &&
|
|
86
99
|
isSessionExpiredError(error) &&
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
(providerId === 'codex-cli' ||
|
|
101
|
+
Boolean(backendResolved.config.resumeArgs && backendResolved.config.resumeArgs.length > 0))) {
|
|
89
102
|
this.options.logger.warn({ provider: providerId, sessionId: envelope.sessionId }, 'CLI session expired, clearing provider mapping and retrying fresh turn');
|
|
90
103
|
await sessionStore.clearCliSessionId(envelope.sessionId, providerId, { model: params.model });
|
|
91
104
|
const retried = await this.executeTurn({
|
|
@@ -97,6 +110,7 @@ export class LocalCliTurnExecutor {
|
|
|
97
110
|
systemPrompt: params.systemPrompt,
|
|
98
111
|
backend: backendResolved.config,
|
|
99
112
|
cliSessionId: undefined,
|
|
113
|
+
queueWaitMs,
|
|
100
114
|
runtimeSessionId: sessionEntry.runtimeSessionId,
|
|
101
115
|
agentEnv: params.agentEnv,
|
|
102
116
|
agentClearEnv: params.agentClearEnv,
|
|
@@ -116,6 +130,65 @@ export class LocalCliTurnExecutor {
|
|
|
116
130
|
await callbacks.onProcessing?.();
|
|
117
131
|
}
|
|
118
132
|
async executeTurn(params) {
|
|
133
|
+
if (params.providerId === 'codex-cli' && params.backend.runtimeMode === 'app-server') {
|
|
134
|
+
return this.executeCodexAppServerTurn(params);
|
|
135
|
+
}
|
|
136
|
+
return this.executeCliExecTurn(params);
|
|
137
|
+
}
|
|
138
|
+
async teardown() {
|
|
139
|
+
await this.codexRuntime?.teardown();
|
|
140
|
+
}
|
|
141
|
+
getCodexRuntime(backend) {
|
|
142
|
+
if (!this.codexRuntime) {
|
|
143
|
+
this.codexRuntime = this.options.createCodexRuntime?.(backend) ?? new CodexAppServerRuntime({
|
|
144
|
+
backend,
|
|
145
|
+
logger: this.options.logger,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return this.codexRuntime;
|
|
149
|
+
}
|
|
150
|
+
async executeCodexAppServerTurn(params) {
|
|
151
|
+
const normalizedModel = normalizeCliModel(params.model, params.backend);
|
|
152
|
+
const existingCliSessionId = params.cliSessionId?.trim() || undefined;
|
|
153
|
+
const isResume = Boolean(existingCliSessionId);
|
|
154
|
+
const promptPayload = buildPromptPayload(params.envelope, params.backend, true);
|
|
155
|
+
this.options.logger.info({
|
|
156
|
+
provider: params.providerId,
|
|
157
|
+
model: normalizedModel,
|
|
158
|
+
sessionId: params.envelope.sessionId,
|
|
159
|
+
cliSessionId: existingCliSessionId,
|
|
160
|
+
runtimeSessionId: params.runtimeSessionId,
|
|
161
|
+
isResume,
|
|
162
|
+
runtimeMode: 'app-server',
|
|
163
|
+
}, 'Starting Codex app-server turn');
|
|
164
|
+
const runtime = this.getCodexRuntime(params.backend);
|
|
165
|
+
const result = await runtime.run({
|
|
166
|
+
sessionId: params.envelope.sessionId,
|
|
167
|
+
runtimeSessionId: params.runtimeSessionId,
|
|
168
|
+
existingThreadId: existingCliSessionId,
|
|
169
|
+
model: normalizedModel,
|
|
170
|
+
workspaceDir: params.workspaceDir,
|
|
171
|
+
timeoutMs: params.timeoutMs,
|
|
172
|
+
noProgressTimeoutMs: resolveCliNoOutputTimeoutMs(params.backend, params.timeoutMs, isResume),
|
|
173
|
+
systemPrompt: resolveAppServerSystemPrompt(params.backend, params.systemPrompt, !existingCliSessionId),
|
|
174
|
+
prompt: promptPayload.prompt,
|
|
175
|
+
imagePaths: promptPayload.imagePaths,
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
text: result.text,
|
|
179
|
+
provider: params.providerId,
|
|
180
|
+
model: params.model,
|
|
181
|
+
cliSessionId: result.cliSessionId,
|
|
182
|
+
isResume: result.isResume,
|
|
183
|
+
usage: result.usage,
|
|
184
|
+
diagnostics: {
|
|
185
|
+
...result.diagnostics,
|
|
186
|
+
queueWaitMs: params.queueWaitMs,
|
|
187
|
+
totalElapsedMs: result.diagnostics.totalElapsedMs + params.queueWaitMs,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async executeCliExecTurn(params) {
|
|
119
192
|
const normalizedModel = normalizeCliModel(params.model, params.backend);
|
|
120
193
|
const existingCliSessionId = params.cliSessionId?.trim() || undefined;
|
|
121
194
|
const useResume = Boolean(existingCliSessionId &&
|
|
@@ -125,7 +198,7 @@ export class LocalCliTurnExecutor {
|
|
|
125
198
|
backend: params.backend,
|
|
126
199
|
cliSessionId: existingCliSessionId,
|
|
127
200
|
});
|
|
128
|
-
const promptPayload = buildPromptPayload(params.envelope, params.backend);
|
|
201
|
+
const promptPayload = buildPromptPayload(params.envelope, params.backend, Boolean(params.backend.imageArg));
|
|
129
202
|
const promptInput = resolvePromptInput({
|
|
130
203
|
backend: params.backend,
|
|
131
204
|
prompt: promptPayload.prompt,
|
|
@@ -165,6 +238,10 @@ export class LocalCliTurnExecutor {
|
|
|
165
238
|
for (const key of params.agentClearEnv ?? []) {
|
|
166
239
|
delete env[key];
|
|
167
240
|
}
|
|
241
|
+
const probe = createCliOutputProbe({
|
|
242
|
+
backend: params.backend,
|
|
243
|
+
mode: outputMode,
|
|
244
|
+
});
|
|
168
245
|
const output = await executeCliCommand({
|
|
169
246
|
command: params.backend.command,
|
|
170
247
|
args,
|
|
@@ -173,22 +250,56 @@ export class LocalCliTurnExecutor {
|
|
|
173
250
|
stdin: promptInput.stdin,
|
|
174
251
|
timeoutMs: params.timeoutMs,
|
|
175
252
|
noOutputTimeoutMs: resolveCliNoOutputTimeoutMs(params.backend, params.timeoutMs, useResume),
|
|
253
|
+
onStdoutChunk(chunk, atMs) {
|
|
254
|
+
probe.onStdout(chunk, atMs);
|
|
255
|
+
},
|
|
256
|
+
onStderrChunk(chunk, atMs) {
|
|
257
|
+
probe.onStderr(chunk, atMs);
|
|
258
|
+
},
|
|
176
259
|
});
|
|
260
|
+
const probeSnapshot = probe.snapshot();
|
|
177
261
|
const parsed = parseCliOutput(output.stdout.trim(), outputMode, params.backend) ?? {
|
|
178
|
-
text: output.stdout.trim(),
|
|
262
|
+
text: probeSnapshot.lastText ?? output.stdout.trim(),
|
|
263
|
+
cliSessionId: probeSnapshot.cliSessionId,
|
|
264
|
+
usage: probeSnapshot.usage,
|
|
179
265
|
};
|
|
180
|
-
const cliSessionId = parsed.cliSessionId ?? existingCliSessionId ?? sessionIdToSend.sessionId;
|
|
266
|
+
const cliSessionId = parsed.cliSessionId ?? probeSnapshot.cliSessionId ?? existingCliSessionId ?? sessionIdToSend.sessionId;
|
|
267
|
+
const diagnostics = buildRunDiagnostics({
|
|
268
|
+
queueWaitMs: params.queueWaitMs,
|
|
269
|
+
commandElapsedMs: output.elapsedMs,
|
|
270
|
+
outputMode,
|
|
271
|
+
stdout: output.stdout,
|
|
272
|
+
stderr: output.stderr,
|
|
273
|
+
cliSessionId,
|
|
274
|
+
existingCliSessionId,
|
|
275
|
+
runtimeSessionId: sessionIdToSend.sessionId,
|
|
276
|
+
probe: probeSnapshot,
|
|
277
|
+
});
|
|
278
|
+
if (params.providerId === 'codex-cli' && !cliSessionId) {
|
|
279
|
+
this.options.logger.warn({
|
|
280
|
+
provider: params.providerId,
|
|
281
|
+
sessionId: params.envelope.sessionId,
|
|
282
|
+
runtimeSessionId: params.runtimeSessionId,
|
|
283
|
+
isResume: useResume,
|
|
284
|
+
diagnostics,
|
|
285
|
+
stdoutPreview: trimOutputPreview(output.stdout),
|
|
286
|
+
stderrPreview: trimOutputPreview(output.stderr),
|
|
287
|
+
}, 'Unable to extract Codex thread id from CLI output');
|
|
288
|
+
}
|
|
289
|
+
const text = parsed.text.trim() || probeSnapshot.lastText?.trim() || output.stdout.trim();
|
|
181
290
|
return {
|
|
182
|
-
text
|
|
291
|
+
text,
|
|
183
292
|
provider: params.providerId,
|
|
184
293
|
model: params.model,
|
|
185
294
|
cliSessionId,
|
|
186
295
|
isResume: useResume,
|
|
187
296
|
usage: parsed.usage,
|
|
297
|
+
diagnostics,
|
|
188
298
|
};
|
|
189
299
|
}
|
|
190
300
|
}
|
|
191
301
|
async function enqueueCliRun(key, task) {
|
|
302
|
+
const queuedAt = Date.now();
|
|
192
303
|
const previous = CLI_RUN_QUEUES.get(key) ?? Promise.resolve();
|
|
193
304
|
let release;
|
|
194
305
|
const current = new Promise((resolve) => {
|
|
@@ -198,7 +309,9 @@ async function enqueueCliRun(key, task) {
|
|
|
198
309
|
CLI_RUN_QUEUES.set(key, tail);
|
|
199
310
|
await previous.catch(() => undefined);
|
|
200
311
|
try {
|
|
201
|
-
return await task(
|
|
312
|
+
return await task({
|
|
313
|
+
queueWaitMs: Date.now() - queuedAt,
|
|
314
|
+
});
|
|
202
315
|
}
|
|
203
316
|
finally {
|
|
204
317
|
release();
|
|
@@ -229,6 +342,20 @@ function resolveSystemPrompt(backend, systemPrompt, isNewSession, useResume) {
|
|
|
229
342
|
}
|
|
230
343
|
return trimmed;
|
|
231
344
|
}
|
|
345
|
+
function resolveAppServerSystemPrompt(backend, systemPrompt, isNewThread) {
|
|
346
|
+
const trimmed = systemPrompt?.trim();
|
|
347
|
+
if (!trimmed) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
const when = backend.systemPromptWhen ?? 'first';
|
|
351
|
+
if (when === 'never') {
|
|
352
|
+
return undefined;
|
|
353
|
+
}
|
|
354
|
+
if (when === 'first' && !isNewThread) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
return trimmed;
|
|
358
|
+
}
|
|
232
359
|
function resolveSessionIdToSend(params) {
|
|
233
360
|
const mode = params.backend.sessionMode ?? 'always';
|
|
234
361
|
const existing = params.cliSessionId?.trim();
|
|
@@ -253,7 +380,7 @@ function resolvePromptInput(params) {
|
|
|
253
380
|
}
|
|
254
381
|
return { promptArg: params.prompt };
|
|
255
382
|
}
|
|
256
|
-
function buildPromptPayload(envelope, backend) {
|
|
383
|
+
function buildPromptPayload(envelope, backend, useSeparateImageInputs = Boolean(backend.imageArg)) {
|
|
257
384
|
const imagePaths = envelope.content.attachments
|
|
258
385
|
.filter((attachment) => attachment.type === 'photo' && attachment.localPath)
|
|
259
386
|
.map((attachment) => attachment.localPath);
|
|
@@ -262,14 +389,14 @@ function buildPromptPayload(envelope, backend) {
|
|
|
262
389
|
return `Attachment: ${attachment.localPath}${name}`;
|
|
263
390
|
});
|
|
264
391
|
const promptParts = [envelope.content.text.trim()];
|
|
265
|
-
if (!
|
|
392
|
+
if (!useSeparateImageInputs && imagePaths.length > 0) {
|
|
266
393
|
promptParts.push(...imagePaths.map((imagePath) => `Image: ${imagePath}`));
|
|
267
394
|
}
|
|
268
395
|
const nonImageFiles = fileLines.filter((line) => !imagePaths.some((imagePath) => line.includes(imagePath)));
|
|
269
396
|
promptParts.push(...nonImageFiles);
|
|
270
397
|
return {
|
|
271
398
|
prompt: promptParts.filter(Boolean).join('\n\n').trim(),
|
|
272
|
-
imagePaths:
|
|
399
|
+
imagePaths: useSeparateImageInputs && imagePaths.length > 0 ? imagePaths : undefined,
|
|
273
400
|
};
|
|
274
401
|
}
|
|
275
402
|
function buildCliArgs(params) {
|
|
@@ -326,6 +453,7 @@ async function executeCliCommand(params) {
|
|
|
326
453
|
const stderrChunks = [];
|
|
327
454
|
let settled = false;
|
|
328
455
|
let timedOutReason = null;
|
|
456
|
+
const startedAt = Date.now();
|
|
329
457
|
const child = spawn(params.command, params.args, {
|
|
330
458
|
cwd: params.cwd,
|
|
331
459
|
env: params.env,
|
|
@@ -346,6 +474,7 @@ async function executeCliCommand(params) {
|
|
|
346
474
|
resolve(result ?? {
|
|
347
475
|
stdout: decodeCliOutput(Buffer.concat(stdoutChunks)),
|
|
348
476
|
stderr: decodeCliOutput(Buffer.concat(stderrChunks)),
|
|
477
|
+
elapsedMs: Date.now() - startedAt,
|
|
349
478
|
});
|
|
350
479
|
};
|
|
351
480
|
const armNoOutputTimer = () => {
|
|
@@ -365,11 +494,15 @@ async function executeCliCommand(params) {
|
|
|
365
494
|
finish(new CliExecutionError(error.message, 'spawn-error', decodeCliOutput(Buffer.concat(stdoutChunks)), decodeCliOutput(Buffer.concat(stderrChunks))));
|
|
366
495
|
});
|
|
367
496
|
child.stdout?.on('data', (chunk) => {
|
|
368
|
-
|
|
497
|
+
const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
498
|
+
stdoutChunks.push(buffer);
|
|
499
|
+
params.onStdoutChunk?.(decodeCliOutput(buffer), Date.now() - startedAt);
|
|
369
500
|
armNoOutputTimer();
|
|
370
501
|
});
|
|
371
502
|
child.stderr?.on('data', (chunk) => {
|
|
372
|
-
|
|
503
|
+
const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
504
|
+
stderrChunks.push(buffer);
|
|
505
|
+
params.onStderrChunk?.(decodeCliOutput(buffer), Date.now() - startedAt);
|
|
373
506
|
armNoOutputTimer();
|
|
374
507
|
});
|
|
375
508
|
child.on('close', (code) => {
|
|
@@ -377,6 +510,7 @@ async function executeCliCommand(params) {
|
|
|
377
510
|
const stderr = decodeCliOutput(Buffer.concat(stderrChunks));
|
|
378
511
|
const trimmedStdout = stdout.trim();
|
|
379
512
|
const trimmedStderr = stderr.trim();
|
|
513
|
+
const elapsedMs = Date.now() - startedAt;
|
|
380
514
|
if (timedOutReason) {
|
|
381
515
|
const timeoutMessage = timedOutReason === 'overall-timeout'
|
|
382
516
|
? `CLI exceeded timeout (${Math.round(params.timeoutMs / 1000)}s) and was terminated.`
|
|
@@ -388,7 +522,7 @@ async function executeCliCommand(params) {
|
|
|
388
522
|
finish(new CliExecutionError(trimmedStderr || trimmedStdout || `CLI failed with exit code ${code ?? 'unknown'}.`, 'exit', trimmedStdout, trimmedStderr, code));
|
|
389
523
|
return;
|
|
390
524
|
}
|
|
391
|
-
finish(undefined, { stdout: trimmedStdout, stderr: trimmedStderr });
|
|
525
|
+
finish(undefined, { stdout: trimmedStdout, stderr: trimmedStderr, elapsedMs });
|
|
392
526
|
});
|
|
393
527
|
if (params.stdin !== undefined && child.stdin) {
|
|
394
528
|
child.stdin.write(params.stdin);
|
|
@@ -432,145 +566,50 @@ function tryDecodeWithEncoding(buffer, encoding) {
|
|
|
432
566
|
return null;
|
|
433
567
|
}
|
|
434
568
|
}
|
|
435
|
-
function
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return { text: raw.trim() };
|
|
441
|
-
}
|
|
442
|
-
if (mode === 'jsonl') {
|
|
443
|
-
return parseCliJsonl(raw, backend);
|
|
444
|
-
}
|
|
445
|
-
return parseCliJson(raw, backend);
|
|
446
|
-
}
|
|
447
|
-
function parseCliJson(raw, backend) {
|
|
448
|
-
try {
|
|
449
|
-
const parsed = JSON.parse(raw);
|
|
450
|
-
if (!isRecord(parsed)) {
|
|
451
|
-
return null;
|
|
569
|
+
function buildRunDiagnostics(params) {
|
|
570
|
+
let cliSessionIdSource;
|
|
571
|
+
if (params.cliSessionId) {
|
|
572
|
+
if (params.probe.cliSessionId && params.cliSessionId === params.probe.cliSessionId) {
|
|
573
|
+
cliSessionIdSource = 'stream';
|
|
452
574
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
collectText(parsed.content) ||
|
|
456
|
-
collectText(parsed.result) ||
|
|
457
|
-
collectText(parsed)).trim(),
|
|
458
|
-
cliSessionId: pickCliSessionId(parsed, backend),
|
|
459
|
-
usage: isRecord(parsed.usage) ? toUsage(parsed.usage) : undefined,
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
catch {
|
|
463
|
-
return null;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
function parseCliJsonl(raw, backend) {
|
|
467
|
-
const lines = raw
|
|
468
|
-
.split(/\r?\n/g)
|
|
469
|
-
.map((line) => line.trim())
|
|
470
|
-
.filter(Boolean);
|
|
471
|
-
if (lines.length === 0) {
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
let cliSessionId;
|
|
475
|
-
let usage;
|
|
476
|
-
let lastText = '';
|
|
477
|
-
for (const line of lines) {
|
|
478
|
-
try {
|
|
479
|
-
const parsed = JSON.parse(line);
|
|
480
|
-
if (!isRecord(parsed)) {
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
cliSessionId = cliSessionId ?? pickCliSessionId(parsed, backend) ?? getString(parsed.thread_id);
|
|
484
|
-
if (isRecord(parsed.usage)) {
|
|
485
|
-
usage = toUsage(parsed.usage) ?? usage;
|
|
486
|
-
}
|
|
487
|
-
const candidate = (collectText(parsed.item) ||
|
|
488
|
-
collectText(parsed.message) ||
|
|
489
|
-
collectText(parsed.delta) ||
|
|
490
|
-
collectText(parsed.result)).trim();
|
|
491
|
-
if (candidate) {
|
|
492
|
-
lastText = candidate;
|
|
493
|
-
}
|
|
575
|
+
else if (params.existingCliSessionId && params.cliSessionId === params.existingCliSessionId) {
|
|
576
|
+
cliSessionIdSource = 'existing';
|
|
494
577
|
}
|
|
495
|
-
|
|
496
|
-
|
|
578
|
+
else if (params.runtimeSessionId && params.cliSessionId === params.runtimeSessionId) {
|
|
579
|
+
cliSessionIdSource = 'runtime';
|
|
497
580
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
return { text: lastText, cliSessionId, usage };
|
|
503
|
-
}
|
|
504
|
-
function pickCliSessionId(parsed, backend) {
|
|
505
|
-
const fields = backend.sessionIdFields ?? ['session_id', 'sessionId', 'conversation_id', 'conversationId'];
|
|
506
|
-
for (const field of fields) {
|
|
507
|
-
const value = getString(parsed[field]);
|
|
508
|
-
if (value) {
|
|
509
|
-
return value;
|
|
581
|
+
else {
|
|
582
|
+
cliSessionIdSource = 'parsed';
|
|
510
583
|
}
|
|
511
584
|
}
|
|
512
|
-
return
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (typeof value.content === 'string') {
|
|
531
|
-
return value.content;
|
|
532
|
-
}
|
|
533
|
-
if (Array.isArray(value.content)) {
|
|
534
|
-
return value.content.map((entry) => collectText(entry)).filter(Boolean).join('\n');
|
|
535
|
-
}
|
|
536
|
-
if (isRecord(value.message)) {
|
|
537
|
-
return collectText(value.message);
|
|
538
|
-
}
|
|
539
|
-
if (isRecord(value.item)) {
|
|
540
|
-
return collectText(value.item);
|
|
541
|
-
}
|
|
542
|
-
return '';
|
|
543
|
-
}
|
|
544
|
-
function toUsage(raw) {
|
|
545
|
-
const pick = (...keys) => {
|
|
546
|
-
for (const key of keys) {
|
|
547
|
-
const value = raw[key];
|
|
548
|
-
if (typeof value === 'number' && value >= 0) {
|
|
549
|
-
return value;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
return undefined;
|
|
553
|
-
};
|
|
554
|
-
const usage = {
|
|
555
|
-
input: pick('input_tokens', 'inputTokens'),
|
|
556
|
-
output: pick('output_tokens', 'outputTokens'),
|
|
557
|
-
cacheRead: pick('cache_read_input_tokens', 'cached_input_tokens', 'cacheRead'),
|
|
558
|
-
cacheWrite: pick('cache_write_input_tokens', 'cacheWrite'),
|
|
559
|
-
total: pick('total_tokens', 'total'),
|
|
585
|
+
return {
|
|
586
|
+
queueWaitMs: params.queueWaitMs,
|
|
587
|
+
commandElapsedMs: params.commandElapsedMs,
|
|
588
|
+
totalElapsedMs: params.queueWaitMs + params.commandElapsedMs,
|
|
589
|
+
outputMode: params.outputMode,
|
|
590
|
+
runtimeMode: 'exec',
|
|
591
|
+
hadStdout: Boolean(params.stdout),
|
|
592
|
+
hadStderr: Boolean(params.stderr),
|
|
593
|
+
firstStdoutMs: params.probe.firstStdoutMs,
|
|
594
|
+
firstStderrMs: params.probe.firstStderrMs,
|
|
595
|
+
threadStartedMs: params.probe.threadStartedMs,
|
|
596
|
+
firstAgentMessageMs: params.probe.firstAgentMessageMs,
|
|
597
|
+
turnCompletedMs: params.probe.turnCompletedMs,
|
|
598
|
+
reconnectCount: params.probe.reconnectCount,
|
|
599
|
+
fellBackToHttps: params.probe.fellBackToHttps,
|
|
600
|
+
threadOperation: params.existingCliSessionId ? 'resume' : 'start',
|
|
601
|
+
threadOperationElapsedMs: params.probe.threadStartedMs,
|
|
602
|
+
cliSessionIdSource,
|
|
560
603
|
};
|
|
561
|
-
|
|
604
|
+
}
|
|
605
|
+
function trimOutputPreview(raw, limit = 1200) {
|
|
606
|
+
const normalized = raw.trim();
|
|
607
|
+
if (!normalized) {
|
|
562
608
|
return undefined;
|
|
563
609
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
return SESSION_EXPIRED_PATTERNS.some((pattern) => pattern.test(message));
|
|
569
|
-
}
|
|
570
|
-
function isRecord(value) {
|
|
571
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
572
|
-
}
|
|
573
|
-
function getString(value) {
|
|
574
|
-
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
610
|
+
if (normalized.length <= limit) {
|
|
611
|
+
return normalized;
|
|
612
|
+
}
|
|
613
|
+
return `${normalized.slice(0, limit)}...`;
|
|
575
614
|
}
|
|
576
615
|
//# sourceMappingURL=cli-runner.js.map
|