acpx 0.1.9 → 0.1.11
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/dist/cli.js +2248 -1651
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
4
|
+
import { Command, CommanderError, InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
5
5
|
import { realpathSync } from "fs";
|
|
6
6
|
import fs6 from "fs/promises";
|
|
7
7
|
import path7 from "path";
|
|
@@ -11,9 +11,9 @@ import { findSkillsRoot, maybeHandleSkillflag } from "skillflag";
|
|
|
11
11
|
// src/agent-registry.ts
|
|
12
12
|
var AGENT_REGISTRY = {
|
|
13
13
|
codex: "npx @zed-industries/codex-acp",
|
|
14
|
-
claude: "npx @zed-industries/claude-agent-acp",
|
|
14
|
+
claude: "npx -y @zed-industries/claude-agent-acp",
|
|
15
15
|
gemini: "gemini",
|
|
16
|
-
opencode: "npx opencode-ai",
|
|
16
|
+
opencode: "npx -y opencode-ai acp",
|
|
17
17
|
pi: "npx pi-acp"
|
|
18
18
|
};
|
|
19
19
|
var DEFAULT_AGENT_NAME = "codex";
|
|
@@ -43,6 +43,223 @@ function listBuiltInAgents(overrides) {
|
|
|
43
43
|
return Object.keys(mergeAgentRegistry(overrides));
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// src/cli-internal-owner.ts
|
|
47
|
+
import { InvalidArgumentError } from "commander";
|
|
48
|
+
|
|
49
|
+
// src/types.ts
|
|
50
|
+
var EXIT_CODES = {
|
|
51
|
+
SUCCESS: 0,
|
|
52
|
+
ERROR: 1,
|
|
53
|
+
USAGE: 2,
|
|
54
|
+
TIMEOUT: 3,
|
|
55
|
+
NO_SESSION: 4,
|
|
56
|
+
PERMISSION_DENIED: 5,
|
|
57
|
+
INTERRUPTED: 130
|
|
58
|
+
};
|
|
59
|
+
var OUTPUT_FORMATS = ["text", "json", "quiet"];
|
|
60
|
+
var PERMISSION_MODES = ["approve-all", "approve-reads", "deny-all"];
|
|
61
|
+
var AUTH_POLICIES = ["skip", "fail"];
|
|
62
|
+
var NON_INTERACTIVE_PERMISSION_POLICIES = ["deny", "fail"];
|
|
63
|
+
var OUTPUT_ERROR_CODES = [
|
|
64
|
+
"NO_SESSION",
|
|
65
|
+
"TIMEOUT",
|
|
66
|
+
"PERMISSION_DENIED",
|
|
67
|
+
"PERMISSION_PROMPT_UNAVAILABLE",
|
|
68
|
+
"RUNTIME",
|
|
69
|
+
"USAGE"
|
|
70
|
+
];
|
|
71
|
+
var OUTPUT_ERROR_ORIGINS = ["cli", "runtime", "queue", "acp"];
|
|
72
|
+
|
|
73
|
+
// src/cli-internal-owner.ts
|
|
74
|
+
function parseNonEmptyValue(label, value) {
|
|
75
|
+
const trimmed = value.trim();
|
|
76
|
+
if (trimmed.length === 0) {
|
|
77
|
+
throw new InvalidArgumentError(`${label} must not be empty`);
|
|
78
|
+
}
|
|
79
|
+
return trimmed;
|
|
80
|
+
}
|
|
81
|
+
function parseNonNegativeMilliseconds(value) {
|
|
82
|
+
const parsed = Number(value);
|
|
83
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
84
|
+
throw new InvalidArgumentError("TTL must be a non-negative number of milliseconds");
|
|
85
|
+
}
|
|
86
|
+
return Math.round(parsed);
|
|
87
|
+
}
|
|
88
|
+
function parseTimeoutMilliseconds(value) {
|
|
89
|
+
const parsed = Number(value);
|
|
90
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
91
|
+
throw new InvalidArgumentError("Timeout must be a positive number of milliseconds");
|
|
92
|
+
}
|
|
93
|
+
return Math.round(parsed);
|
|
94
|
+
}
|
|
95
|
+
function parsePermissionMode(value) {
|
|
96
|
+
if (!PERMISSION_MODES.includes(value)) {
|
|
97
|
+
throw new InvalidArgumentError(
|
|
98
|
+
`Invalid permission mode "${value}". Expected one of: ${PERMISSION_MODES.join(", ")}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
function parseAuthPolicy(value) {
|
|
104
|
+
if (!AUTH_POLICIES.includes(value)) {
|
|
105
|
+
throw new InvalidArgumentError(
|
|
106
|
+
`Invalid auth policy "${value}". Expected one of: ${AUTH_POLICIES.join(", ")}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
function parseNonInteractivePermissionPolicy(value) {
|
|
112
|
+
if (!NON_INTERACTIVE_PERMISSION_POLICIES.includes(
|
|
113
|
+
value
|
|
114
|
+
)) {
|
|
115
|
+
throw new InvalidArgumentError(
|
|
116
|
+
`Invalid non-interactive permission policy "${value}". Expected one of: ${NON_INTERACTIVE_PERMISSION_POLICIES.join(", ")}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
function parseQueueOwnerFlags(argv, defaultTtlMs) {
|
|
122
|
+
if (argv[0] !== "__queue-owner") {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
const flags = {
|
|
126
|
+
ttlMs: defaultTtlMs
|
|
127
|
+
};
|
|
128
|
+
const consumeValue = (index, token) => {
|
|
129
|
+
if (token.includes("=")) {
|
|
130
|
+
return {
|
|
131
|
+
value: token.slice(token.indexOf("=") + 1),
|
|
132
|
+
next: index
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const value = argv[index + 1];
|
|
136
|
+
if (!value || value.startsWith("-")) {
|
|
137
|
+
throw new InvalidArgumentError(`${token} requires a value`);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
value,
|
|
141
|
+
next: index + 1
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
145
|
+
const token = argv[index];
|
|
146
|
+
if (token === "--session-id" || token.startsWith("--session-id=")) {
|
|
147
|
+
const consumed = consumeValue(index, token);
|
|
148
|
+
flags.sessionId = parseNonEmptyValue("Session id", consumed.value);
|
|
149
|
+
index = consumed.next;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (token === "--ttl-ms" || token.startsWith("--ttl-ms=")) {
|
|
153
|
+
const consumed = consumeValue(index, token);
|
|
154
|
+
flags.ttlMs = parseNonNegativeMilliseconds(consumed.value);
|
|
155
|
+
index = consumed.next;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (token === "--permission-mode" || token.startsWith("--permission-mode=")) {
|
|
159
|
+
const consumed = consumeValue(index, token);
|
|
160
|
+
flags.permissionMode = parsePermissionMode(consumed.value);
|
|
161
|
+
index = consumed.next;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (token === "--non-interactive-permissions" || token.startsWith("--non-interactive-permissions=")) {
|
|
165
|
+
const consumed = consumeValue(index, token);
|
|
166
|
+
flags.nonInteractivePermissions = parseNonInteractivePermissionPolicy(
|
|
167
|
+
consumed.value
|
|
168
|
+
);
|
|
169
|
+
index = consumed.next;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (token === "--auth-policy" || token.startsWith("--auth-policy=")) {
|
|
173
|
+
const consumed = consumeValue(index, token);
|
|
174
|
+
flags.authPolicy = parseAuthPolicy(consumed.value);
|
|
175
|
+
index = consumed.next;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (token === "--timeout-ms" || token.startsWith("--timeout-ms=")) {
|
|
179
|
+
const consumed = consumeValue(index, token);
|
|
180
|
+
flags.timeoutMs = parseTimeoutMilliseconds(consumed.value);
|
|
181
|
+
index = consumed.next;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (token === "--verbose") {
|
|
185
|
+
flags.verbose = true;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (token === "--suppress-sdk-console-errors") {
|
|
189
|
+
flags.suppressSdkConsoleErrors = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
throw new InvalidArgumentError(`Unknown __queue-owner option: ${token}`);
|
|
193
|
+
}
|
|
194
|
+
if (!flags.sessionId) {
|
|
195
|
+
throw new InvalidArgumentError("__queue-owner requires --session-id");
|
|
196
|
+
}
|
|
197
|
+
if (!flags.permissionMode) {
|
|
198
|
+
throw new InvalidArgumentError("__queue-owner requires --permission-mode");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
sessionId: flags.sessionId,
|
|
202
|
+
ttlMs: flags.ttlMs ?? defaultTtlMs,
|
|
203
|
+
permissionMode: flags.permissionMode,
|
|
204
|
+
nonInteractivePermissions: flags.nonInteractivePermissions,
|
|
205
|
+
authPolicy: flags.authPolicy,
|
|
206
|
+
timeoutMs: flags.timeoutMs,
|
|
207
|
+
verbose: flags.verbose,
|
|
208
|
+
suppressSdkConsoleErrors: flags.suppressSdkConsoleErrors
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/cli-public.ts
|
|
213
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
214
|
+
function configurePublicCli(options) {
|
|
215
|
+
const builtInAgents = options.listBuiltInAgents(options.config.agents);
|
|
216
|
+
for (const agentName of builtInAgents) {
|
|
217
|
+
options.registerAgentCommand(options.program, agentName, options.config);
|
|
218
|
+
}
|
|
219
|
+
options.registerDefaultCommands(options.program, options.config);
|
|
220
|
+
const scan = options.detectAgentToken(options.argv);
|
|
221
|
+
if (!scan.hasAgentOverride && scan.token && !options.topLevelVerbs.has(scan.token) && !builtInAgents.includes(scan.token)) {
|
|
222
|
+
options.registerAgentCommand(options.program, scan.token, options.config);
|
|
223
|
+
}
|
|
224
|
+
options.program.argument("[prompt...]", "Prompt text").action(async function(promptParts) {
|
|
225
|
+
if (promptParts.length === 0 && process.stdin.isTTY) {
|
|
226
|
+
if (options.requestedJsonStrict) {
|
|
227
|
+
throw new InvalidArgumentError2(
|
|
228
|
+
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
this.outputHelp();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
await options.handlePromptAction(this, promptParts);
|
|
235
|
+
});
|
|
236
|
+
options.program.addHelpText(
|
|
237
|
+
"after",
|
|
238
|
+
`
|
|
239
|
+
Examples:
|
|
240
|
+
acpx codex sessions new
|
|
241
|
+
acpx codex "fix the tests"
|
|
242
|
+
acpx codex prompt "fix the tests"
|
|
243
|
+
acpx codex --no-wait "queue follow-up task"
|
|
244
|
+
acpx codex exec "what does this repo do"
|
|
245
|
+
acpx codex cancel
|
|
246
|
+
acpx codex set-mode plan
|
|
247
|
+
acpx codex set approval_policy conservative
|
|
248
|
+
acpx codex -s backend "fix the API"
|
|
249
|
+
acpx codex sessions
|
|
250
|
+
acpx codex sessions new --name backend
|
|
251
|
+
acpx codex sessions ensure --name backend
|
|
252
|
+
acpx codex sessions close backend
|
|
253
|
+
acpx codex status
|
|
254
|
+
acpx config show
|
|
255
|
+
acpx config init
|
|
256
|
+
acpx --ttl 30 codex "investigate flaky tests"
|
|
257
|
+
acpx claude "refactor auth"
|
|
258
|
+
acpx gemini "add logging"
|
|
259
|
+
acpx --agent ./my-custom-server "do something"`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
46
263
|
// src/config.ts
|
|
47
264
|
import fs from "fs/promises";
|
|
48
265
|
import os from "os";
|
|
@@ -92,7 +309,7 @@ function parseTimeoutMs(value, sourcePath) {
|
|
|
92
309
|
}
|
|
93
310
|
return Math.round(value * 1e3);
|
|
94
311
|
}
|
|
95
|
-
function
|
|
312
|
+
function parsePermissionMode2(value, sourcePath) {
|
|
96
313
|
if (value == null) {
|
|
97
314
|
return void 0;
|
|
98
315
|
}
|
|
@@ -103,7 +320,7 @@ function parsePermissionMode(value, sourcePath) {
|
|
|
103
320
|
}
|
|
104
321
|
return value;
|
|
105
322
|
}
|
|
106
|
-
function
|
|
323
|
+
function parseNonInteractivePermissionPolicy2(value, sourcePath) {
|
|
107
324
|
if (value == null) {
|
|
108
325
|
return void 0;
|
|
109
326
|
}
|
|
@@ -116,7 +333,7 @@ function parseNonInteractivePermissionPolicy(value, sourcePath) {
|
|
|
116
333
|
}
|
|
117
334
|
return value;
|
|
118
335
|
}
|
|
119
|
-
function
|
|
336
|
+
function parseAuthPolicy2(value, sourcePath) {
|
|
120
337
|
if (value == null) {
|
|
121
338
|
return void 0;
|
|
122
339
|
}
|
|
@@ -239,15 +456,15 @@ async function loadResolvedConfig(cwd) {
|
|
|
239
456
|
const globalConfig = globalResult.config;
|
|
240
457
|
const projectConfig = projectResult.config;
|
|
241
458
|
const defaultAgent = parseDefaultAgent(projectConfig?.defaultAgent, projectPath) ?? parseDefaultAgent(globalConfig?.defaultAgent, globalPath) ?? DEFAULT_AGENT_NAME;
|
|
242
|
-
const defaultPermissions =
|
|
243
|
-
const nonInteractivePermissions =
|
|
459
|
+
const defaultPermissions = parsePermissionMode2(projectConfig?.defaultPermissions, projectPath) ?? parsePermissionMode2(globalConfig?.defaultPermissions, globalPath) ?? DEFAULT_PERMISSION_MODE;
|
|
460
|
+
const nonInteractivePermissions = parseNonInteractivePermissionPolicy2(
|
|
244
461
|
projectConfig?.nonInteractivePermissions,
|
|
245
462
|
projectPath
|
|
246
|
-
) ??
|
|
463
|
+
) ?? parseNonInteractivePermissionPolicy2(
|
|
247
464
|
globalConfig?.nonInteractivePermissions,
|
|
248
465
|
globalPath
|
|
249
466
|
) ?? DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY;
|
|
250
|
-
const authPolicy =
|
|
467
|
+
const authPolicy = parseAuthPolicy2(projectConfig?.authPolicy, projectPath) ?? parseAuthPolicy2(globalConfig?.authPolicy, globalPath) ?? DEFAULT_AUTH_POLICY;
|
|
251
468
|
const ttlMs = parseTtlMs(projectConfig?.ttl, projectPath) ?? parseTtlMs(globalConfig?.ttl, globalPath) ?? DEFAULT_TTL_MS;
|
|
252
469
|
const timeoutConfiguredInProject = projectConfig != null && Object.prototype.hasOwnProperty.call(projectConfig, "timeout");
|
|
253
470
|
const timeoutConfiguredInGlobal = globalConfig != null && Object.prototype.hasOwnProperty.call(globalConfig, "timeout");
|
|
@@ -388,96 +605,14 @@ var PermissionPromptUnavailableError = class extends AcpxOperationalError {
|
|
|
388
605
|
}
|
|
389
606
|
};
|
|
390
607
|
|
|
391
|
-
// src/
|
|
392
|
-
var EXIT_CODES = {
|
|
393
|
-
SUCCESS: 0,
|
|
394
|
-
ERROR: 1,
|
|
395
|
-
USAGE: 2,
|
|
396
|
-
TIMEOUT: 3,
|
|
397
|
-
NO_SESSION: 4,
|
|
398
|
-
PERMISSION_DENIED: 5,
|
|
399
|
-
INTERRUPTED: 130
|
|
400
|
-
};
|
|
401
|
-
var OUTPUT_FORMATS = ["text", "json", "quiet"];
|
|
402
|
-
var AUTH_POLICIES = ["skip", "fail"];
|
|
403
|
-
var NON_INTERACTIVE_PERMISSION_POLICIES = ["deny", "fail"];
|
|
404
|
-
var OUTPUT_ERROR_CODES = [
|
|
405
|
-
"NO_SESSION",
|
|
406
|
-
"TIMEOUT",
|
|
407
|
-
"PERMISSION_DENIED",
|
|
408
|
-
"PERMISSION_PROMPT_UNAVAILABLE",
|
|
409
|
-
"RUNTIME",
|
|
410
|
-
"USAGE"
|
|
411
|
-
];
|
|
412
|
-
var OUTPUT_ERROR_ORIGINS = ["cli", "runtime", "queue", "acp"];
|
|
413
|
-
|
|
414
|
-
// src/error-normalization.ts
|
|
608
|
+
// src/acp-error-shapes.ts
|
|
415
609
|
var RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
|
|
416
|
-
var AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
417
610
|
function asRecord(value) {
|
|
418
611
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
419
612
|
return void 0;
|
|
420
613
|
}
|
|
421
614
|
return value;
|
|
422
615
|
}
|
|
423
|
-
function isAuthRequiredMessage(value) {
|
|
424
|
-
if (!value) {
|
|
425
|
-
return false;
|
|
426
|
-
}
|
|
427
|
-
const normalized = value.toLowerCase();
|
|
428
|
-
return normalized.includes("auth required") || normalized.includes("authentication required") || normalized.includes("authorization required") || normalized.includes("credential required") || normalized.includes("credentials required") || normalized.includes("token required") || normalized.includes("login required");
|
|
429
|
-
}
|
|
430
|
-
function isAcpAuthRequiredPayload(acp) {
|
|
431
|
-
if (!acp) {
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
|
-
if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) {
|
|
435
|
-
return false;
|
|
436
|
-
}
|
|
437
|
-
if (isAuthRequiredMessage(acp.message)) {
|
|
438
|
-
return true;
|
|
439
|
-
}
|
|
440
|
-
const data = asRecord(acp.data);
|
|
441
|
-
if (!data) {
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
444
|
-
if (data.authRequired === true) {
|
|
445
|
-
return true;
|
|
446
|
-
}
|
|
447
|
-
const methodId = data.methodId;
|
|
448
|
-
if (typeof methodId === "string" && methodId.trim().length > 0) {
|
|
449
|
-
return true;
|
|
450
|
-
}
|
|
451
|
-
const methods = data.methods;
|
|
452
|
-
if (Array.isArray(methods) && methods.length > 0) {
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
function isOutputErrorCode(value) {
|
|
458
|
-
return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
|
|
459
|
-
}
|
|
460
|
-
function isOutputErrorOrigin(value) {
|
|
461
|
-
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
462
|
-
}
|
|
463
|
-
function readOutputErrorMeta(error) {
|
|
464
|
-
const record = asRecord(error);
|
|
465
|
-
if (!record) {
|
|
466
|
-
return {};
|
|
467
|
-
}
|
|
468
|
-
const outputCode = isOutputErrorCode(record.outputCode) ? record.outputCode : void 0;
|
|
469
|
-
const detailCode = typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0;
|
|
470
|
-
const origin = isOutputErrorOrigin(record.origin) ? record.origin : void 0;
|
|
471
|
-
const retryable = typeof record.retryable === "boolean" ? record.retryable : void 0;
|
|
472
|
-
const acp = toAcpErrorPayload(record.acp);
|
|
473
|
-
return {
|
|
474
|
-
outputCode,
|
|
475
|
-
detailCode,
|
|
476
|
-
origin,
|
|
477
|
-
retryable,
|
|
478
|
-
acp
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
616
|
function toAcpErrorPayload(value) {
|
|
482
617
|
const record = asRecord(value);
|
|
483
618
|
if (!record) {
|
|
@@ -521,19 +656,7 @@ function extractAcpErrorInternal(value, depth) {
|
|
|
521
656
|
}
|
|
522
657
|
return void 0;
|
|
523
658
|
}
|
|
524
|
-
function
|
|
525
|
-
return error instanceof Error && error.name === "TimeoutError";
|
|
526
|
-
}
|
|
527
|
-
function isNoSessionLike(error) {
|
|
528
|
-
return error instanceof Error && error.name === "NoSessionError";
|
|
529
|
-
}
|
|
530
|
-
function isUsageLike(error) {
|
|
531
|
-
if (!(error instanceof Error)) {
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord(error)?.code === "commander.invalidArgument";
|
|
535
|
-
}
|
|
536
|
-
function formatErrorMessage(error) {
|
|
659
|
+
function formatUnknownErrorMessage(error) {
|
|
537
660
|
if (error instanceof Error) {
|
|
538
661
|
return error.message;
|
|
539
662
|
}
|
|
@@ -549,6 +672,31 @@ function formatErrorMessage(error) {
|
|
|
549
672
|
}
|
|
550
673
|
return String(error);
|
|
551
674
|
}
|
|
675
|
+
function isSessionNotFoundText(value) {
|
|
676
|
+
if (typeof value !== "string") {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
const normalized = value.toLowerCase();
|
|
680
|
+
return normalized.includes("resource_not_found") || normalized.includes("resource not found") || normalized.includes("session not found") || normalized.includes("unknown session");
|
|
681
|
+
}
|
|
682
|
+
function hasSessionNotFoundHint(value, depth = 0) {
|
|
683
|
+
if (depth > 4) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
if (isSessionNotFoundText(value)) {
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
if (Array.isArray(value)) {
|
|
690
|
+
return value.some((entry) => hasSessionNotFoundHint(entry, depth + 1));
|
|
691
|
+
}
|
|
692
|
+
const record = asRecord(value);
|
|
693
|
+
if (!record) {
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
return Object.values(record).some(
|
|
697
|
+
(entry) => hasSessionNotFoundHint(entry, depth + 1)
|
|
698
|
+
);
|
|
699
|
+
}
|
|
552
700
|
function extractAcpError(error) {
|
|
553
701
|
return extractAcpErrorInternal(error, 0);
|
|
554
702
|
}
|
|
@@ -557,12 +705,131 @@ function isAcpResourceNotFoundError(error) {
|
|
|
557
705
|
if (acp && RESOURCE_NOT_FOUND_ACP_CODES.has(acp.code)) {
|
|
558
706
|
return true;
|
|
559
707
|
}
|
|
560
|
-
|
|
561
|
-
|
|
708
|
+
if (acp) {
|
|
709
|
+
if (isSessionNotFoundText(acp.message)) {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
if (hasSessionNotFoundHint(acp.data)) {
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return isSessionNotFoundText(formatUnknownErrorMessage(error));
|
|
562
717
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
718
|
+
|
|
719
|
+
// src/error-normalization.ts
|
|
720
|
+
var AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
721
|
+
function asRecord2(value) {
|
|
722
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
return value;
|
|
726
|
+
}
|
|
727
|
+
function isAuthRequiredMessage(value) {
|
|
728
|
+
if (!value) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
const normalized = value.toLowerCase();
|
|
732
|
+
return normalized.includes("auth required") || normalized.includes("authentication required") || normalized.includes("authorization required") || normalized.includes("credential required") || normalized.includes("credentials required") || normalized.includes("token required") || normalized.includes("login required");
|
|
733
|
+
}
|
|
734
|
+
function isAcpAuthRequiredPayload(acp) {
|
|
735
|
+
if (!acp) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
if (isAuthRequiredMessage(acp.message)) {
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
const data = asRecord2(acp.data);
|
|
745
|
+
if (!data) {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
if (data.authRequired === true) {
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
const methodId = data.methodId;
|
|
752
|
+
if (typeof methodId === "string" && methodId.trim().length > 0) {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
const methods = data.methods;
|
|
756
|
+
if (Array.isArray(methods) && methods.length > 0) {
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
function isOutputErrorCode(value) {
|
|
762
|
+
return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
|
|
763
|
+
}
|
|
764
|
+
function isOutputErrorOrigin(value) {
|
|
765
|
+
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
766
|
+
}
|
|
767
|
+
function readOutputErrorMeta(error) {
|
|
768
|
+
const record = asRecord2(error);
|
|
769
|
+
if (!record) {
|
|
770
|
+
return {};
|
|
771
|
+
}
|
|
772
|
+
const outputCode = isOutputErrorCode(record.outputCode) ? record.outputCode : void 0;
|
|
773
|
+
const detailCode = typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0;
|
|
774
|
+
const origin = isOutputErrorOrigin(record.origin) ? record.origin : void 0;
|
|
775
|
+
const retryable = typeof record.retryable === "boolean" ? record.retryable : void 0;
|
|
776
|
+
const acp = toAcpErrorPayload2(record.acp);
|
|
777
|
+
return {
|
|
778
|
+
outputCode,
|
|
779
|
+
detailCode,
|
|
780
|
+
origin,
|
|
781
|
+
retryable,
|
|
782
|
+
acp
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
function toAcpErrorPayload2(value) {
|
|
786
|
+
const record = asRecord2(value);
|
|
787
|
+
if (!record) {
|
|
788
|
+
return void 0;
|
|
789
|
+
}
|
|
790
|
+
if (typeof record.code !== "number" || !Number.isFinite(record.code)) {
|
|
791
|
+
return void 0;
|
|
792
|
+
}
|
|
793
|
+
if (typeof record.message !== "string" || record.message.length === 0) {
|
|
794
|
+
return void 0;
|
|
795
|
+
}
|
|
796
|
+
return {
|
|
797
|
+
code: record.code,
|
|
798
|
+
message: record.message,
|
|
799
|
+
data: record.data
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function isTimeoutLike(error) {
|
|
803
|
+
return error instanceof Error && error.name === "TimeoutError";
|
|
804
|
+
}
|
|
805
|
+
function isNoSessionLike(error) {
|
|
806
|
+
return error instanceof Error && error.name === "NoSessionError";
|
|
807
|
+
}
|
|
808
|
+
function isUsageLike(error) {
|
|
809
|
+
if (!(error instanceof Error)) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord2(error)?.code === "commander.invalidArgument";
|
|
813
|
+
}
|
|
814
|
+
function formatErrorMessage(error) {
|
|
815
|
+
if (error instanceof Error) {
|
|
816
|
+
return error.message;
|
|
817
|
+
}
|
|
818
|
+
if (error && typeof error === "object") {
|
|
819
|
+
const maybeMessage = error.message;
|
|
820
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0) {
|
|
821
|
+
return maybeMessage;
|
|
822
|
+
}
|
|
823
|
+
try {
|
|
824
|
+
return JSON.stringify(error);
|
|
825
|
+
} catch {
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return String(error);
|
|
829
|
+
}
|
|
830
|
+
function mapErrorCode(error) {
|
|
831
|
+
if (error instanceof PermissionPromptUnavailableError) {
|
|
832
|
+
return "PERMISSION_PROMPT_UNAVAILABLE";
|
|
566
833
|
}
|
|
567
834
|
if (error instanceof PermissionDeniedError) {
|
|
568
835
|
return "PERMISSION_DENIED";
|
|
@@ -658,7 +925,7 @@ function toStatusLabel(status) {
|
|
|
658
925
|
return "running";
|
|
659
926
|
}
|
|
660
927
|
}
|
|
661
|
-
function
|
|
928
|
+
function asRecord3(value) {
|
|
662
929
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
663
930
|
return void 0;
|
|
664
931
|
}
|
|
@@ -752,7 +1019,7 @@ function summarizeToolInput(rawInput) {
|
|
|
752
1019
|
if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
|
|
753
1020
|
return toInline(String(rawInput));
|
|
754
1021
|
}
|
|
755
|
-
const record =
|
|
1022
|
+
const record = asRecord3(rawInput);
|
|
756
1023
|
if (record) {
|
|
757
1024
|
const command = readFirstString(record, ["command", "cmd", "program"]);
|
|
758
1025
|
const args = readFirstStringArray(record, ["args", "arguments"]);
|
|
@@ -886,7 +1153,7 @@ function extractOutputText(value, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
|
886
1153
|
}
|
|
887
1154
|
return dedupeStrings(parts).join("\n");
|
|
888
1155
|
}
|
|
889
|
-
const record =
|
|
1156
|
+
const record = asRecord3(value);
|
|
890
1157
|
if (!record) {
|
|
891
1158
|
return void 0;
|
|
892
1159
|
}
|
|
@@ -2086,6 +2353,9 @@ var TerminalManager = class {
|
|
|
2086
2353
|
var REPLAY_IDLE_MS = 80;
|
|
2087
2354
|
var REPLAY_DRAIN_TIMEOUT_MS = 5e3;
|
|
2088
2355
|
var DRAIN_POLL_INTERVAL_MS = 20;
|
|
2356
|
+
var AGENT_CLOSE_AFTER_STDIN_END_MS = 100;
|
|
2357
|
+
var AGENT_CLOSE_TERM_GRACE_MS = 1500;
|
|
2358
|
+
var AGENT_CLOSE_KILL_GRACE_MS = 1e3;
|
|
2089
2359
|
function shouldSuppressSdkConsoleError(args) {
|
|
2090
2360
|
if (args.length === 0) {
|
|
2091
2361
|
return false;
|
|
@@ -2121,6 +2391,38 @@ function waitForSpawn2(child) {
|
|
|
2121
2391
|
child.once("error", onError);
|
|
2122
2392
|
});
|
|
2123
2393
|
}
|
|
2394
|
+
function isChildProcessRunning(child) {
|
|
2395
|
+
return child.exitCode == null && child.signalCode == null;
|
|
2396
|
+
}
|
|
2397
|
+
function waitForChildExit(child, timeoutMs) {
|
|
2398
|
+
if (!isChildProcessRunning(child)) {
|
|
2399
|
+
return Promise.resolve(true);
|
|
2400
|
+
}
|
|
2401
|
+
return new Promise((resolve) => {
|
|
2402
|
+
let settled = false;
|
|
2403
|
+
const timer = setTimeout(
|
|
2404
|
+
() => {
|
|
2405
|
+
finish(false);
|
|
2406
|
+
},
|
|
2407
|
+
Math.max(0, timeoutMs)
|
|
2408
|
+
);
|
|
2409
|
+
const finish = (value) => {
|
|
2410
|
+
if (settled) {
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
settled = true;
|
|
2414
|
+
child.off("close", onExitLike);
|
|
2415
|
+
child.off("exit", onExitLike);
|
|
2416
|
+
clearTimeout(timer);
|
|
2417
|
+
resolve(value);
|
|
2418
|
+
};
|
|
2419
|
+
const onExitLike = () => {
|
|
2420
|
+
finish(true);
|
|
2421
|
+
};
|
|
2422
|
+
child.once("close", onExitLike);
|
|
2423
|
+
child.once("exit", onExitLike);
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2124
2426
|
function splitCommandLine(value) {
|
|
2125
2427
|
const parts = [];
|
|
2126
2428
|
let current = "";
|
|
@@ -2542,8 +2844,8 @@ var AcpClient = class {
|
|
|
2542
2844
|
async close() {
|
|
2543
2845
|
this.closing = true;
|
|
2544
2846
|
await this.terminalManager.shutdown();
|
|
2545
|
-
if (this.agent
|
|
2546
|
-
this.agent
|
|
2847
|
+
if (this.agent) {
|
|
2848
|
+
await this.terminateAgentProcess(this.agent);
|
|
2547
2849
|
}
|
|
2548
2850
|
this.sessionUpdateChain = Promise.resolve();
|
|
2549
2851
|
this.observedSessionUpdates = 0;
|
|
@@ -2555,6 +2857,44 @@ var AcpClient = class {
|
|
|
2555
2857
|
this.connection = void 0;
|
|
2556
2858
|
this.agent = void 0;
|
|
2557
2859
|
}
|
|
2860
|
+
async terminateAgentProcess(child) {
|
|
2861
|
+
if (!child.stdin.destroyed) {
|
|
2862
|
+
try {
|
|
2863
|
+
child.stdin.end();
|
|
2864
|
+
} catch {
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
let exited = await waitForChildExit(child, AGENT_CLOSE_AFTER_STDIN_END_MS);
|
|
2868
|
+
if (!exited && isChildProcessRunning(child)) {
|
|
2869
|
+
try {
|
|
2870
|
+
child.kill("SIGTERM");
|
|
2871
|
+
} catch {
|
|
2872
|
+
}
|
|
2873
|
+
exited = await waitForChildExit(child, AGENT_CLOSE_TERM_GRACE_MS);
|
|
2874
|
+
}
|
|
2875
|
+
if (!exited && isChildProcessRunning(child)) {
|
|
2876
|
+
this.log(
|
|
2877
|
+
`agent did not exit after ${AGENT_CLOSE_TERM_GRACE_MS}ms; forcing SIGKILL`
|
|
2878
|
+
);
|
|
2879
|
+
try {
|
|
2880
|
+
child.kill("SIGKILL");
|
|
2881
|
+
} catch {
|
|
2882
|
+
}
|
|
2883
|
+
exited = await waitForChildExit(child, AGENT_CLOSE_KILL_GRACE_MS);
|
|
2884
|
+
}
|
|
2885
|
+
if (!child.stdin.destroyed) {
|
|
2886
|
+
child.stdin.destroy();
|
|
2887
|
+
}
|
|
2888
|
+
if (!child.stdout.destroyed) {
|
|
2889
|
+
child.stdout.destroy();
|
|
2890
|
+
}
|
|
2891
|
+
if (!child.stderr.destroyed) {
|
|
2892
|
+
child.stderr.destroy();
|
|
2893
|
+
}
|
|
2894
|
+
if (!exited) {
|
|
2895
|
+
child.unref();
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2558
2898
|
getConnection() {
|
|
2559
2899
|
if (!this.connection) {
|
|
2560
2900
|
throw new Error("ACP client not started");
|
|
@@ -2768,118 +3108,273 @@ var AcpClient = class {
|
|
|
2768
3108
|
}
|
|
2769
3109
|
};
|
|
2770
3110
|
|
|
2771
|
-
// src/queue-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
this.state = "active";
|
|
2793
|
-
}
|
|
3111
|
+
// src/queue-lease-store.ts
|
|
3112
|
+
import { createHash } from "crypto";
|
|
3113
|
+
import fs3 from "fs/promises";
|
|
3114
|
+
import os2 from "os";
|
|
3115
|
+
import path4 from "path";
|
|
3116
|
+
var PROCESS_EXIT_GRACE_MS = 1500;
|
|
3117
|
+
var PROCESS_POLL_MS = 50;
|
|
3118
|
+
var QUEUE_OWNER_STALE_HEARTBEAT_MS = 15e3;
|
|
3119
|
+
function queueBaseDir() {
|
|
3120
|
+
return path4.join(os2.homedir(), ".acpx", "queues");
|
|
3121
|
+
}
|
|
3122
|
+
function queueKeyForSession(sessionId) {
|
|
3123
|
+
return createHash("sha256").update(sessionId).digest("hex").slice(0, 24);
|
|
3124
|
+
}
|
|
3125
|
+
function queueLockFilePath(sessionId) {
|
|
3126
|
+
return path4.join(queueBaseDir(), `${queueKeyForSession(sessionId)}.lock`);
|
|
3127
|
+
}
|
|
3128
|
+
function queueSocketPath(sessionId) {
|
|
3129
|
+
const key = queueKeyForSession(sessionId);
|
|
3130
|
+
if (process.platform === "win32") {
|
|
3131
|
+
return `\\\\.\\pipe\\acpx-${key}`;
|
|
2794
3132
|
}
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
3133
|
+
return path4.join(queueBaseDir(), `${key}.sock`);
|
|
3134
|
+
}
|
|
3135
|
+
function parseQueueOwnerRecord(raw) {
|
|
3136
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3137
|
+
return null;
|
|
2798
3138
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
this.activeController = void 0;
|
|
3139
|
+
const record = raw;
|
|
3140
|
+
if (!Number.isInteger(record.pid) || record.pid <= 0 || typeof record.sessionId !== "string" || typeof record.socketPath !== "string" || typeof record.createdAt !== "string" || typeof record.heartbeatAt !== "string" || !Number.isInteger(record.ownerGeneration) || record.ownerGeneration <= 0 || !Number.isInteger(record.queueDepth) || record.queueDepth < 0) {
|
|
3141
|
+
return null;
|
|
2803
3142
|
}
|
|
2804
|
-
|
|
2805
|
-
|
|
3143
|
+
return {
|
|
3144
|
+
pid: record.pid,
|
|
3145
|
+
sessionId: record.sessionId,
|
|
3146
|
+
socketPath: record.socketPath,
|
|
3147
|
+
createdAt: record.createdAt,
|
|
3148
|
+
heartbeatAt: record.heartbeatAt,
|
|
3149
|
+
ownerGeneration: record.ownerGeneration,
|
|
3150
|
+
queueDepth: record.queueDepth
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
function createOwnerGeneration() {
|
|
3154
|
+
return Date.now() * 1e3 + Math.floor(Math.random() * 1e3);
|
|
3155
|
+
}
|
|
3156
|
+
function nowIso4() {
|
|
3157
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
3158
|
+
}
|
|
3159
|
+
function isQueueOwnerHeartbeatStale(owner) {
|
|
3160
|
+
const heartbeatMs = Date.parse(owner.heartbeatAt);
|
|
3161
|
+
if (!Number.isFinite(heartbeatMs)) {
|
|
3162
|
+
return true;
|
|
2806
3163
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
3164
|
+
return Date.now() - heartbeatMs > QUEUE_OWNER_STALE_HEARTBEAT_MS;
|
|
3165
|
+
}
|
|
3166
|
+
async function ensureQueueDir() {
|
|
3167
|
+
await fs3.mkdir(queueBaseDir(), { recursive: true });
|
|
3168
|
+
}
|
|
3169
|
+
async function removeSocketFile(socketPath) {
|
|
3170
|
+
if (process.platform === "win32") {
|
|
3171
|
+
return;
|
|
2809
3172
|
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
retryable: true
|
|
2816
|
-
});
|
|
3173
|
+
try {
|
|
3174
|
+
await fs3.unlink(socketPath);
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
if (error.code !== "ENOENT") {
|
|
3177
|
+
throw error;
|
|
2817
3178
|
}
|
|
2818
3179
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
this.pendingCancel = false;
|
|
2825
|
-
}
|
|
2826
|
-
return cancelled2;
|
|
2827
|
-
}
|
|
2828
|
-
if (this.state === "starting" || this.state === "active") {
|
|
2829
|
-
this.pendingCancel = true;
|
|
3180
|
+
}
|
|
3181
|
+
async function waitForProcessExit(pid, timeoutMs) {
|
|
3182
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
3183
|
+
while (Date.now() <= deadline) {
|
|
3184
|
+
if (!isProcessAlive(pid)) {
|
|
2830
3185
|
return true;
|
|
2831
3186
|
}
|
|
2832
|
-
|
|
3187
|
+
await waitMs2(PROCESS_POLL_MS);
|
|
2833
3188
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
3189
|
+
return !isProcessAlive(pid);
|
|
3190
|
+
}
|
|
3191
|
+
async function cleanupStaleQueueOwner(sessionId, owner) {
|
|
3192
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3193
|
+
const socketPath = owner?.socketPath ?? queueSocketPath(sessionId);
|
|
3194
|
+
await removeSocketFile(socketPath).catch(() => {
|
|
3195
|
+
});
|
|
3196
|
+
await fs3.unlink(lockPath).catch((error) => {
|
|
3197
|
+
if (error.code !== "ENOENT") {
|
|
3198
|
+
throw error;
|
|
2842
3199
|
}
|
|
2843
|
-
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
async function readQueueOwnerRecord(sessionId) {
|
|
3203
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3204
|
+
try {
|
|
3205
|
+
const payload = await fs3.readFile(lockPath, "utf8");
|
|
3206
|
+
const parsed = parseQueueOwnerRecord(JSON.parse(payload));
|
|
3207
|
+
return parsed ?? void 0;
|
|
3208
|
+
} catch {
|
|
3209
|
+
return void 0;
|
|
2844
3210
|
}
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
3211
|
+
}
|
|
3212
|
+
function isProcessAlive(pid) {
|
|
3213
|
+
if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) {
|
|
3214
|
+
return false;
|
|
3215
|
+
}
|
|
3216
|
+
try {
|
|
3217
|
+
process.kill(pid, 0);
|
|
3218
|
+
return true;
|
|
3219
|
+
} catch {
|
|
3220
|
+
return false;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
async function terminateProcess(pid) {
|
|
3224
|
+
if (!isProcessAlive(pid)) {
|
|
3225
|
+
return false;
|
|
3226
|
+
}
|
|
3227
|
+
try {
|
|
3228
|
+
process.kill(pid, "SIGTERM");
|
|
3229
|
+
} catch {
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
if (await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS)) {
|
|
3233
|
+
return true;
|
|
3234
|
+
}
|
|
3235
|
+
try {
|
|
3236
|
+
process.kill(pid, "SIGKILL");
|
|
3237
|
+
} catch {
|
|
3238
|
+
return false;
|
|
3239
|
+
}
|
|
3240
|
+
await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS);
|
|
3241
|
+
return true;
|
|
3242
|
+
}
|
|
3243
|
+
async function ensureOwnerIsUsable(sessionId, owner) {
|
|
3244
|
+
const alive = isProcessAlive(owner.pid);
|
|
3245
|
+
const stale = isQueueOwnerHeartbeatStale(owner);
|
|
3246
|
+
if (alive && !stale) {
|
|
3247
|
+
return true;
|
|
3248
|
+
}
|
|
3249
|
+
if (alive) {
|
|
3250
|
+
await terminateProcess(owner.pid).catch(() => {
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3254
|
+
return false;
|
|
3255
|
+
}
|
|
3256
|
+
async function readQueueOwnerStatus(sessionId) {
|
|
3257
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3258
|
+
if (!owner) {
|
|
3259
|
+
return void 0;
|
|
3260
|
+
}
|
|
3261
|
+
const alive = await ensureOwnerIsUsable(sessionId, owner);
|
|
3262
|
+
if (!alive) {
|
|
3263
|
+
return void 0;
|
|
3264
|
+
}
|
|
3265
|
+
return {
|
|
3266
|
+
pid: owner.pid,
|
|
3267
|
+
socketPath: owner.socketPath,
|
|
3268
|
+
heartbeatAt: owner.heartbeatAt,
|
|
3269
|
+
ownerGeneration: owner.ownerGeneration,
|
|
3270
|
+
queueDepth: owner.queueDepth,
|
|
3271
|
+
alive,
|
|
3272
|
+
stale: isQueueOwnerHeartbeatStale(owner)
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
async function tryAcquireQueueOwnerLease(sessionId, nowIsoFactory = nowIso4) {
|
|
3276
|
+
await ensureQueueDir();
|
|
3277
|
+
const lockPath = queueLockFilePath(sessionId);
|
|
3278
|
+
const socketPath = queueSocketPath(sessionId);
|
|
3279
|
+
const createdAt = nowIsoFactory();
|
|
3280
|
+
const ownerGeneration = createOwnerGeneration();
|
|
3281
|
+
const payload = JSON.stringify(
|
|
3282
|
+
{
|
|
3283
|
+
pid: process.pid,
|
|
3284
|
+
sessionId,
|
|
3285
|
+
socketPath,
|
|
3286
|
+
createdAt,
|
|
3287
|
+
heartbeatAt: createdAt,
|
|
3288
|
+
ownerGeneration,
|
|
3289
|
+
queueDepth: 0
|
|
3290
|
+
},
|
|
3291
|
+
null,
|
|
3292
|
+
2
|
|
3293
|
+
);
|
|
3294
|
+
try {
|
|
3295
|
+
await fs3.writeFile(lockPath, `${payload}
|
|
3296
|
+
`, {
|
|
3297
|
+
encoding: "utf8",
|
|
3298
|
+
flag: "wx"
|
|
3299
|
+
});
|
|
3300
|
+
await removeSocketFile(socketPath).catch(() => {
|
|
3301
|
+
});
|
|
3302
|
+
return {
|
|
3303
|
+
sessionId,
|
|
3304
|
+
lockPath,
|
|
3305
|
+
socketPath,
|
|
3306
|
+
createdAt,
|
|
3307
|
+
ownerGeneration
|
|
3308
|
+
};
|
|
3309
|
+
} catch (error) {
|
|
3310
|
+
if (error.code !== "EEXIST") {
|
|
3311
|
+
throw error;
|
|
2854
3312
|
}
|
|
2855
|
-
await
|
|
3313
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3314
|
+
if (!owner) {
|
|
3315
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3316
|
+
return void 0;
|
|
3317
|
+
}
|
|
3318
|
+
if (!isProcessAlive(owner.pid) || isQueueOwnerHeartbeatStale(owner)) {
|
|
3319
|
+
if (isProcessAlive(owner.pid)) {
|
|
3320
|
+
await terminateProcess(owner.pid).catch(() => {
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3324
|
+
}
|
|
3325
|
+
return void 0;
|
|
2856
3326
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3327
|
+
}
|
|
3328
|
+
async function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso4) {
|
|
3329
|
+
const payload = JSON.stringify(
|
|
3330
|
+
{
|
|
3331
|
+
pid: process.pid,
|
|
3332
|
+
sessionId: lease.sessionId,
|
|
3333
|
+
socketPath: lease.socketPath,
|
|
3334
|
+
createdAt: lease.createdAt,
|
|
3335
|
+
heartbeatAt: nowIsoFactory(),
|
|
3336
|
+
ownerGeneration: lease.ownerGeneration,
|
|
3337
|
+
queueDepth: Math.max(0, Math.round(options.queueDepth))
|
|
3338
|
+
},
|
|
3339
|
+
null,
|
|
3340
|
+
2
|
|
3341
|
+
);
|
|
3342
|
+
await fs3.writeFile(lease.lockPath, `${payload}
|
|
3343
|
+
`, {
|
|
3344
|
+
encoding: "utf8"
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
async function releaseQueueOwnerLease(lease) {
|
|
3348
|
+
await removeSocketFile(lease.socketPath).catch(() => {
|
|
3349
|
+
});
|
|
3350
|
+
await fs3.unlink(lease.lockPath).catch((error) => {
|
|
3351
|
+
if (error.code !== "ENOENT") {
|
|
3352
|
+
throw error;
|
|
2865
3353
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
async function terminateQueueOwnerForSession(sessionId) {
|
|
3357
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
3358
|
+
if (!owner) {
|
|
3359
|
+
return;
|
|
2871
3360
|
}
|
|
2872
|
-
|
|
3361
|
+
if (isProcessAlive(owner.pid)) {
|
|
3362
|
+
await terminateProcess(owner.pid);
|
|
3363
|
+
}
|
|
3364
|
+
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3365
|
+
}
|
|
3366
|
+
async function waitMs2(ms) {
|
|
3367
|
+
await new Promise((resolve) => {
|
|
3368
|
+
setTimeout(resolve, ms);
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
2873
3371
|
|
|
2874
|
-
// src/queue-ipc.ts
|
|
2875
|
-
import {
|
|
2876
|
-
import fs3 from "fs/promises";
|
|
3372
|
+
// src/queue-ipc-client.ts
|
|
3373
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2877
3374
|
import net from "net";
|
|
2878
|
-
import os2 from "os";
|
|
2879
|
-
import path4 from "path";
|
|
2880
3375
|
|
|
2881
3376
|
// src/queue-messages.ts
|
|
2882
|
-
function
|
|
3377
|
+
function asRecord4(value) {
|
|
2883
3378
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2884
3379
|
return void 0;
|
|
2885
3380
|
}
|
|
@@ -2898,7 +3393,7 @@ function isOutputErrorOrigin2(value) {
|
|
|
2898
3393
|
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
2899
3394
|
}
|
|
2900
3395
|
function parseAcpError(value) {
|
|
2901
|
-
const record =
|
|
3396
|
+
const record = asRecord4(value);
|
|
2902
3397
|
if (!record) {
|
|
2903
3398
|
return void 0;
|
|
2904
3399
|
}
|
|
@@ -2915,7 +3410,7 @@ function parseAcpError(value) {
|
|
|
2915
3410
|
};
|
|
2916
3411
|
}
|
|
2917
3412
|
function parseQueueRequest(raw) {
|
|
2918
|
-
const request =
|
|
3413
|
+
const request = asRecord4(raw);
|
|
2919
3414
|
if (!request) {
|
|
2920
3415
|
return null;
|
|
2921
3416
|
}
|
|
@@ -2973,15 +3468,15 @@ function parseQueueRequest(raw) {
|
|
|
2973
3468
|
return null;
|
|
2974
3469
|
}
|
|
2975
3470
|
function parseSessionSendResult(raw) {
|
|
2976
|
-
const result =
|
|
3471
|
+
const result = asRecord4(raw);
|
|
2977
3472
|
if (!result) {
|
|
2978
3473
|
return null;
|
|
2979
3474
|
}
|
|
2980
3475
|
if (typeof result.stopReason !== "string" || typeof result.sessionId !== "string" || typeof result.resumed !== "boolean") {
|
|
2981
3476
|
return null;
|
|
2982
3477
|
}
|
|
2983
|
-
const permissionStats =
|
|
2984
|
-
const record =
|
|
3478
|
+
const permissionStats = asRecord4(result.permissionStats);
|
|
3479
|
+
const record = asRecord4(result.record);
|
|
2985
3480
|
if (!permissionStats || !record) {
|
|
2986
3481
|
return null;
|
|
2987
3482
|
}
|
|
@@ -2996,7 +3491,7 @@ function parseSessionSendResult(raw) {
|
|
|
2996
3491
|
return result;
|
|
2997
3492
|
}
|
|
2998
3493
|
function parseQueueOwnerMessage(raw) {
|
|
2999
|
-
const message =
|
|
3494
|
+
const message = asRecord4(raw);
|
|
3000
3495
|
if (!message || typeof message.type !== "string") {
|
|
3001
3496
|
return null;
|
|
3002
3497
|
}
|
|
@@ -3021,7 +3516,7 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3021
3516
|
};
|
|
3022
3517
|
}
|
|
3023
3518
|
if (message.type === "client_operation") {
|
|
3024
|
-
const operation =
|
|
3519
|
+
const operation = asRecord4(message.operation);
|
|
3025
3520
|
if (!operation || typeof operation.method !== "string" || typeof operation.status !== "string" || typeof operation.summary !== "string" || typeof operation.timestamp !== "string") {
|
|
3026
3521
|
return null;
|
|
3027
3522
|
}
|
|
@@ -3076,7 +3571,7 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3076
3571
|
};
|
|
3077
3572
|
}
|
|
3078
3573
|
if (message.type === "set_config_option_result") {
|
|
3079
|
-
const response =
|
|
3574
|
+
const response = asRecord4(message.response);
|
|
3080
3575
|
if (!response || !Array.isArray(response.configOptions)) {
|
|
3081
3576
|
return null;
|
|
3082
3577
|
}
|
|
@@ -3109,504 +3604,354 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3109
3604
|
return null;
|
|
3110
3605
|
}
|
|
3111
3606
|
|
|
3112
|
-
// src/queue-ipc.ts
|
|
3113
|
-
var PROCESS_EXIT_GRACE_MS = 1500;
|
|
3114
|
-
var PROCESS_POLL_MS = 50;
|
|
3607
|
+
// src/queue-ipc-client.ts
|
|
3115
3608
|
var QUEUE_CONNECT_ATTEMPTS = 40;
|
|
3116
3609
|
var QUEUE_CONNECT_RETRY_MS = 50;
|
|
3117
|
-
function
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
3121
|
-
return {
|
|
3122
|
-
type: "error",
|
|
3123
|
-
requestId,
|
|
3124
|
-
code: "RUNTIME",
|
|
3125
|
-
detailCode,
|
|
3126
|
-
origin: "queue",
|
|
3127
|
-
retryable: options.retryable,
|
|
3128
|
-
message
|
|
3129
|
-
};
|
|
3610
|
+
function shouldRetryQueueConnect(error) {
|
|
3611
|
+
const code = error.code;
|
|
3612
|
+
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
3130
3613
|
}
|
|
3131
|
-
function
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3614
|
+
async function connectToSocket(socketPath) {
|
|
3615
|
+
return await new Promise((resolve, reject) => {
|
|
3616
|
+
const socket = net.createConnection(socketPath);
|
|
3617
|
+
const onConnect = () => {
|
|
3618
|
+
socket.off("error", onError);
|
|
3619
|
+
resolve(socket);
|
|
3620
|
+
};
|
|
3621
|
+
const onError = (error) => {
|
|
3622
|
+
socket.off("connect", onConnect);
|
|
3623
|
+
reject(error);
|
|
3624
|
+
};
|
|
3625
|
+
socket.once("connect", onConnect);
|
|
3626
|
+
socket.once("error", onError);
|
|
3137
3627
|
});
|
|
3138
|
-
return {
|
|
3139
|
-
type: "error",
|
|
3140
|
-
requestId,
|
|
3141
|
-
code: normalized.code,
|
|
3142
|
-
detailCode: normalized.detailCode,
|
|
3143
|
-
origin: normalized.origin,
|
|
3144
|
-
message: normalized.message,
|
|
3145
|
-
retryable: normalized.retryable,
|
|
3146
|
-
acp: normalized.acp
|
|
3147
|
-
};
|
|
3148
3628
|
}
|
|
3149
|
-
function
|
|
3150
|
-
|
|
3151
|
-
|
|
3629
|
+
async function connectToQueueOwner(owner) {
|
|
3630
|
+
let lastError;
|
|
3631
|
+
for (let attempt = 0; attempt < QUEUE_CONNECT_ATTEMPTS; attempt += 1) {
|
|
3632
|
+
try {
|
|
3633
|
+
return await connectToSocket(owner.socketPath);
|
|
3634
|
+
} catch (error) {
|
|
3635
|
+
lastError = error;
|
|
3636
|
+
if (!shouldRetryQueueConnect(error)) {
|
|
3637
|
+
throw error;
|
|
3638
|
+
}
|
|
3639
|
+
if (!isProcessAlive(owner.pid)) {
|
|
3640
|
+
return void 0;
|
|
3641
|
+
}
|
|
3642
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
3643
|
+
}
|
|
3152
3644
|
}
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
return true;
|
|
3156
|
-
} catch {
|
|
3157
|
-
return false;
|
|
3645
|
+
if (lastError && !shouldRetryQueueConnect(lastError)) {
|
|
3646
|
+
throw lastError;
|
|
3158
3647
|
}
|
|
3648
|
+
return void 0;
|
|
3159
3649
|
}
|
|
3160
|
-
async function
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
return true;
|
|
3165
|
-
}
|
|
3166
|
-
await new Promise((resolve) => {
|
|
3167
|
-
setTimeout(resolve, PROCESS_POLL_MS);
|
|
3168
|
-
});
|
|
3650
|
+
async function submitToQueueOwner(owner, options) {
|
|
3651
|
+
const socket = await connectToQueueOwner(owner);
|
|
3652
|
+
if (!socket) {
|
|
3653
|
+
return void 0;
|
|
3169
3654
|
}
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
if (await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS)) {
|
|
3182
|
-
return true;
|
|
3183
|
-
}
|
|
3184
|
-
try {
|
|
3185
|
-
process.kill(pid, "SIGKILL");
|
|
3186
|
-
} catch {
|
|
3187
|
-
return false;
|
|
3188
|
-
}
|
|
3189
|
-
await waitForProcessExit(pid, PROCESS_EXIT_GRACE_MS);
|
|
3190
|
-
return true;
|
|
3191
|
-
}
|
|
3192
|
-
function parseQueueOwnerRecord(raw) {
|
|
3193
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
3194
|
-
return null;
|
|
3195
|
-
}
|
|
3196
|
-
const record = raw;
|
|
3197
|
-
if (!Number.isInteger(record.pid) || record.pid <= 0 || typeof record.sessionId !== "string" || typeof record.socketPath !== "string") {
|
|
3198
|
-
return null;
|
|
3199
|
-
}
|
|
3200
|
-
return {
|
|
3201
|
-
pid: record.pid,
|
|
3202
|
-
sessionId: record.sessionId,
|
|
3203
|
-
socketPath: record.socketPath
|
|
3655
|
+
socket.setEncoding("utf8");
|
|
3656
|
+
const requestId = randomUUID2();
|
|
3657
|
+
const request = {
|
|
3658
|
+
type: "submit_prompt",
|
|
3659
|
+
requestId,
|
|
3660
|
+
message: options.message,
|
|
3661
|
+
permissionMode: options.permissionMode,
|
|
3662
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
3663
|
+
timeoutMs: options.timeoutMs,
|
|
3664
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
3665
|
+
waitForCompletion: options.waitForCompletion
|
|
3204
3666
|
};
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
function queueLockFilePath(sessionId) {
|
|
3210
|
-
return path4.join(queueBaseDir(), `${queueKeyForSession(sessionId)}.lock`);
|
|
3211
|
-
}
|
|
3212
|
-
function queueSocketPath(sessionId) {
|
|
3213
|
-
const key = queueKeyForSession(sessionId);
|
|
3214
|
-
if (process.platform === "win32") {
|
|
3215
|
-
return `\\\\.\\pipe\\acpx-${key}`;
|
|
3216
|
-
}
|
|
3217
|
-
return path4.join(queueBaseDir(), `${key}.sock`);
|
|
3218
|
-
}
|
|
3219
|
-
async function ensureQueueDir() {
|
|
3220
|
-
await fs3.mkdir(queueBaseDir(), { recursive: true });
|
|
3221
|
-
}
|
|
3222
|
-
async function removeSocketFile(socketPath) {
|
|
3223
|
-
if (process.platform === "win32") {
|
|
3224
|
-
return;
|
|
3225
|
-
}
|
|
3226
|
-
try {
|
|
3227
|
-
await fs3.unlink(socketPath);
|
|
3228
|
-
} catch (error) {
|
|
3229
|
-
if (error.code !== "ENOENT") {
|
|
3230
|
-
throw error;
|
|
3231
|
-
}
|
|
3232
|
-
}
|
|
3233
|
-
}
|
|
3234
|
-
async function readQueueOwnerRecord(sessionId) {
|
|
3235
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
3236
|
-
try {
|
|
3237
|
-
const payload = await fs3.readFile(lockPath, "utf8");
|
|
3238
|
-
const parsed = parseQueueOwnerRecord(JSON.parse(payload));
|
|
3239
|
-
return parsed ?? void 0;
|
|
3240
|
-
} catch {
|
|
3241
|
-
return void 0;
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
async function cleanupStaleQueueOwner(sessionId, owner) {
|
|
3245
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
3246
|
-
const socketPath = owner?.socketPath ?? queueSocketPath(sessionId);
|
|
3247
|
-
await removeSocketFile(socketPath).catch(() => {
|
|
3248
|
-
});
|
|
3249
|
-
await fs3.unlink(lockPath).catch((error) => {
|
|
3250
|
-
if (error.code !== "ENOENT") {
|
|
3251
|
-
throw error;
|
|
3252
|
-
}
|
|
3253
|
-
});
|
|
3254
|
-
}
|
|
3255
|
-
async function tryAcquireQueueOwnerLease(sessionId, nowIso4 = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3256
|
-
await ensureQueueDir();
|
|
3257
|
-
const lockPath = queueLockFilePath(sessionId);
|
|
3258
|
-
const socketPath = queueSocketPath(sessionId);
|
|
3259
|
-
const payload = JSON.stringify(
|
|
3260
|
-
{
|
|
3261
|
-
pid: process.pid,
|
|
3262
|
-
sessionId,
|
|
3263
|
-
socketPath,
|
|
3264
|
-
createdAt: nowIso4()
|
|
3265
|
-
},
|
|
3266
|
-
null,
|
|
3267
|
-
2
|
|
3268
|
-
);
|
|
3269
|
-
try {
|
|
3270
|
-
await fs3.writeFile(lockPath, `${payload}
|
|
3271
|
-
`, {
|
|
3272
|
-
encoding: "utf8",
|
|
3273
|
-
flag: "wx"
|
|
3274
|
-
});
|
|
3275
|
-
await removeSocketFile(socketPath).catch(() => {
|
|
3276
|
-
});
|
|
3277
|
-
return { lockPath, socketPath };
|
|
3278
|
-
} catch (error) {
|
|
3279
|
-
if (error.code !== "EEXIST") {
|
|
3280
|
-
throw error;
|
|
3281
|
-
}
|
|
3282
|
-
const owner = await readQueueOwnerRecord(sessionId);
|
|
3283
|
-
if (!owner || !isProcessAlive(owner.pid)) {
|
|
3284
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
3285
|
-
}
|
|
3286
|
-
return void 0;
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
async function releaseQueueOwnerLease(lease) {
|
|
3290
|
-
await removeSocketFile(lease.socketPath).catch(() => {
|
|
3291
|
-
});
|
|
3292
|
-
await fs3.unlink(lease.lockPath).catch((error) => {
|
|
3293
|
-
if (error.code !== "ENOENT") {
|
|
3294
|
-
throw error;
|
|
3295
|
-
}
|
|
3296
|
-
});
|
|
3297
|
-
}
|
|
3298
|
-
function shouldRetryQueueConnect(error) {
|
|
3299
|
-
const code = error.code;
|
|
3300
|
-
return code === "ENOENT" || code === "ECONNREFUSED";
|
|
3301
|
-
}
|
|
3302
|
-
async function waitMs2(ms) {
|
|
3303
|
-
await new Promise((resolve) => {
|
|
3304
|
-
setTimeout(resolve, ms);
|
|
3667
|
+
options.outputFormatter.setContext({
|
|
3668
|
+
sessionId: options.sessionId,
|
|
3669
|
+
requestId,
|
|
3670
|
+
stream: "prompt"
|
|
3305
3671
|
});
|
|
3306
|
-
}
|
|
3307
|
-
async function connectToSocket(socketPath) {
|
|
3308
3672
|
return await new Promise((resolve, reject) => {
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3673
|
+
let settled = false;
|
|
3674
|
+
let acknowledged = false;
|
|
3675
|
+
let buffer = "";
|
|
3676
|
+
let sawDone = false;
|
|
3677
|
+
const finishResolve = (result) => {
|
|
3678
|
+
if (settled) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
settled = true;
|
|
3682
|
+
socket.removeAllListeners();
|
|
3683
|
+
if (!socket.destroyed) {
|
|
3684
|
+
socket.end();
|
|
3685
|
+
}
|
|
3686
|
+
resolve(result);
|
|
3313
3687
|
};
|
|
3314
|
-
const
|
|
3315
|
-
|
|
3688
|
+
const finishReject = (error) => {
|
|
3689
|
+
if (settled) {
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
settled = true;
|
|
3693
|
+
socket.removeAllListeners();
|
|
3694
|
+
if (!socket.destroyed) {
|
|
3695
|
+
socket.destroy();
|
|
3696
|
+
}
|
|
3316
3697
|
reject(error);
|
|
3317
3698
|
};
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3699
|
+
const processLine = (line) => {
|
|
3700
|
+
let parsed;
|
|
3701
|
+
try {
|
|
3702
|
+
parsed = JSON.parse(line);
|
|
3703
|
+
} catch {
|
|
3704
|
+
finishReject(
|
|
3705
|
+
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3706
|
+
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3707
|
+
origin: "queue",
|
|
3708
|
+
retryable: true
|
|
3709
|
+
})
|
|
3710
|
+
);
|
|
3711
|
+
return;
|
|
3331
3712
|
}
|
|
3332
|
-
|
|
3333
|
-
|
|
3713
|
+
const message = parseQueueOwnerMessage(parsed);
|
|
3714
|
+
if (!message || message.requestId !== requestId) {
|
|
3715
|
+
finishReject(
|
|
3716
|
+
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3717
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3718
|
+
origin: "queue",
|
|
3719
|
+
retryable: true
|
|
3720
|
+
})
|
|
3721
|
+
);
|
|
3722
|
+
return;
|
|
3334
3723
|
}
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
server.listen(lease.socketPath);
|
|
3378
|
-
});
|
|
3379
|
-
return ownerRef.current;
|
|
3380
|
-
}
|
|
3381
|
-
async close() {
|
|
3382
|
-
if (this.closed) {
|
|
3383
|
-
return;
|
|
3384
|
-
}
|
|
3385
|
-
this.closed = true;
|
|
3386
|
-
for (const waiter of this.waiters.splice(0)) {
|
|
3387
|
-
waiter(void 0);
|
|
3388
|
-
}
|
|
3389
|
-
for (const task of this.pending.splice(0)) {
|
|
3390
|
-
if (task.waitForCompletion) {
|
|
3391
|
-
task.send(
|
|
3392
|
-
makeQueueOwnerError(
|
|
3393
|
-
task.requestId,
|
|
3394
|
-
"Queue owner shutting down before prompt execution",
|
|
3395
|
-
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
3396
|
-
{
|
|
3397
|
-
retryable: true
|
|
3398
|
-
}
|
|
3399
|
-
)
|
|
3724
|
+
if (message.type === "accepted") {
|
|
3725
|
+
acknowledged = true;
|
|
3726
|
+
options.outputFormatter.setContext({
|
|
3727
|
+
sessionId: options.sessionId,
|
|
3728
|
+
requestId: message.requestId,
|
|
3729
|
+
stream: "prompt"
|
|
3730
|
+
});
|
|
3731
|
+
if (!options.waitForCompletion) {
|
|
3732
|
+
const queued = {
|
|
3733
|
+
queued: true,
|
|
3734
|
+
sessionId: options.sessionId,
|
|
3735
|
+
requestId
|
|
3736
|
+
};
|
|
3737
|
+
finishResolve(queued);
|
|
3738
|
+
}
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
if (message.type === "error") {
|
|
3742
|
+
options.outputFormatter.setContext({
|
|
3743
|
+
sessionId: options.sessionId,
|
|
3744
|
+
requestId: message.requestId,
|
|
3745
|
+
stream: "prompt"
|
|
3746
|
+
});
|
|
3747
|
+
options.outputFormatter.onError({
|
|
3748
|
+
code: message.code ?? "RUNTIME",
|
|
3749
|
+
detailCode: message.detailCode,
|
|
3750
|
+
origin: message.origin ?? "queue",
|
|
3751
|
+
message: message.message,
|
|
3752
|
+
retryable: message.retryable,
|
|
3753
|
+
acp: message.acp
|
|
3754
|
+
});
|
|
3755
|
+
options.outputFormatter.flush();
|
|
3756
|
+
const queueErrorAlreadyEmitted = options.errorEmissionPolicy?.queueErrorAlreadyEmitted ?? true;
|
|
3757
|
+
finishReject(
|
|
3758
|
+
new QueueConnectionError(message.message, {
|
|
3759
|
+
outputCode: message.code,
|
|
3760
|
+
detailCode: message.detailCode,
|
|
3761
|
+
origin: message.origin ?? "queue",
|
|
3762
|
+
retryable: message.retryable,
|
|
3763
|
+
acp: message.acp,
|
|
3764
|
+
...queueErrorAlreadyEmitted ? { outputAlreadyEmitted: true } : {}
|
|
3765
|
+
})
|
|
3400
3766
|
);
|
|
3767
|
+
return;
|
|
3401
3768
|
}
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3769
|
+
if (!acknowledged) {
|
|
3770
|
+
finishReject(
|
|
3771
|
+
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3772
|
+
detailCode: "QUEUE_ACK_MISSING",
|
|
3773
|
+
origin: "queue",
|
|
3774
|
+
retryable: true
|
|
3775
|
+
})
|
|
3776
|
+
);
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
if (message.type === "session_update") {
|
|
3780
|
+
options.outputFormatter.onSessionUpdate(message.notification);
|
|
3781
|
+
return;
|
|
3782
|
+
}
|
|
3783
|
+
if (message.type === "client_operation") {
|
|
3784
|
+
options.outputFormatter.onClientOperation(message.operation);
|
|
3785
|
+
return;
|
|
3786
|
+
}
|
|
3787
|
+
if (message.type === "done") {
|
|
3788
|
+
options.outputFormatter.onDone(message.stopReason);
|
|
3789
|
+
sawDone = true;
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
if (message.type === "result") {
|
|
3793
|
+
if (!sawDone) {
|
|
3794
|
+
options.outputFormatter.onDone(message.result.stopReason);
|
|
3795
|
+
}
|
|
3796
|
+
options.outputFormatter.flush();
|
|
3797
|
+
finishResolve(message.result);
|
|
3798
|
+
return;
|
|
3799
|
+
}
|
|
3800
|
+
finishReject(
|
|
3801
|
+
new QueueProtocolError("Queue owner returned unexpected response", {
|
|
3802
|
+
detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
|
|
3803
|
+
origin: "queue",
|
|
3804
|
+
retryable: true
|
|
3805
|
+
})
|
|
3426
3806
|
);
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3807
|
+
};
|
|
3808
|
+
socket.on("data", (chunk) => {
|
|
3809
|
+
buffer += chunk;
|
|
3810
|
+
let index = buffer.indexOf("\n");
|
|
3811
|
+
while (index >= 0) {
|
|
3812
|
+
const line = buffer.slice(0, index).trim();
|
|
3813
|
+
buffer = buffer.slice(index + 1);
|
|
3814
|
+
if (line.length > 0) {
|
|
3815
|
+
processLine(line);
|
|
3430
3816
|
}
|
|
3431
|
-
|
|
3432
|
-
}
|
|
3433
|
-
this.waiters.push(waiter);
|
|
3817
|
+
index = buffer.indexOf("\n");
|
|
3818
|
+
}
|
|
3434
3819
|
});
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3820
|
+
socket.once("error", (error) => {
|
|
3821
|
+
finishReject(error);
|
|
3822
|
+
});
|
|
3823
|
+
socket.once("close", () => {
|
|
3824
|
+
if (settled) {
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
if (!acknowledged) {
|
|
3828
|
+
finishReject(
|
|
3829
|
+
new QueueConnectionError(
|
|
3830
|
+
"Queue owner disconnected before acknowledging request",
|
|
3444
3831
|
{
|
|
3832
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
3833
|
+
origin: "queue",
|
|
3445
3834
|
retryable: true
|
|
3446
3835
|
}
|
|
3447
3836
|
)
|
|
3448
3837
|
);
|
|
3838
|
+
return;
|
|
3449
3839
|
}
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
writeQueueMessage(
|
|
3464
|
-
socket,
|
|
3465
|
-
makeQueueOwnerError("unknown", "Queue owner is closed", "QUEUE_OWNER_CLOSED", {
|
|
3840
|
+
if (!options.waitForCompletion) {
|
|
3841
|
+
const queued = {
|
|
3842
|
+
queued: true,
|
|
3843
|
+
sessionId: options.sessionId,
|
|
3844
|
+
requestId
|
|
3845
|
+
};
|
|
3846
|
+
finishResolve(queued);
|
|
3847
|
+
return;
|
|
3848
|
+
}
|
|
3849
|
+
finishReject(
|
|
3850
|
+
new QueueConnectionError("Queue owner disconnected before prompt completion", {
|
|
3851
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
3852
|
+
origin: "queue",
|
|
3466
3853
|
retryable: true
|
|
3467
3854
|
})
|
|
3468
3855
|
);
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3856
|
+
});
|
|
3857
|
+
socket.write(`${JSON.stringify(request)}
|
|
3858
|
+
`);
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
3862
|
+
const socket = await connectToQueueOwner(owner);
|
|
3863
|
+
if (!socket) {
|
|
3864
|
+
return void 0;
|
|
3865
|
+
}
|
|
3866
|
+
socket.setEncoding("utf8");
|
|
3867
|
+
return await new Promise((resolve, reject) => {
|
|
3868
|
+
let settled = false;
|
|
3869
|
+
let acknowledged = false;
|
|
3472
3870
|
let buffer = "";
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3871
|
+
const finishResolve = (result) => {
|
|
3872
|
+
if (settled) {
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
settled = true;
|
|
3876
|
+
socket.removeAllListeners();
|
|
3877
|
+
if (!socket.destroyed) {
|
|
3878
|
+
socket.end();
|
|
3879
|
+
}
|
|
3880
|
+
resolve(result);
|
|
3482
3881
|
};
|
|
3483
|
-
const
|
|
3484
|
-
if (
|
|
3882
|
+
const finishReject = (error) => {
|
|
3883
|
+
if (settled) {
|
|
3485
3884
|
return;
|
|
3486
3885
|
}
|
|
3487
|
-
|
|
3886
|
+
settled = true;
|
|
3887
|
+
socket.removeAllListeners();
|
|
3888
|
+
if (!socket.destroyed) {
|
|
3889
|
+
socket.destroy();
|
|
3890
|
+
}
|
|
3891
|
+
reject(error);
|
|
3892
|
+
};
|
|
3893
|
+
const processLine = (line) => {
|
|
3488
3894
|
let parsed;
|
|
3489
3895
|
try {
|
|
3490
3896
|
parsed = JSON.parse(line);
|
|
3491
3897
|
} catch {
|
|
3492
|
-
|
|
3493
|
-
"
|
|
3494
|
-
|
|
3495
|
-
|
|
3898
|
+
finishReject(
|
|
3899
|
+
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3900
|
+
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3901
|
+
origin: "queue",
|
|
3902
|
+
retryable: true
|
|
3903
|
+
})
|
|
3496
3904
|
);
|
|
3497
3905
|
return;
|
|
3498
3906
|
}
|
|
3499
|
-
const
|
|
3500
|
-
if (!request) {
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
});
|
|
3509
|
-
void this.controlHandlers.cancelPrompt().then((cancelled2) => {
|
|
3510
|
-
writeQueueMessage(socket, {
|
|
3511
|
-
type: "cancel_result",
|
|
3512
|
-
requestId: request.requestId,
|
|
3513
|
-
cancelled: cancelled2
|
|
3514
|
-
});
|
|
3515
|
-
}).catch((error) => {
|
|
3516
|
-
writeQueueMessage(
|
|
3517
|
-
socket,
|
|
3518
|
-
makeQueueOwnerErrorFromUnknown(
|
|
3519
|
-
request.requestId,
|
|
3520
|
-
error,
|
|
3521
|
-
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
3522
|
-
)
|
|
3523
|
-
);
|
|
3524
|
-
}).finally(() => {
|
|
3525
|
-
if (!socket.destroyed) {
|
|
3526
|
-
socket.end();
|
|
3527
|
-
}
|
|
3528
|
-
});
|
|
3907
|
+
const message = parseQueueOwnerMessage(parsed);
|
|
3908
|
+
if (!message || message.requestId !== request.requestId) {
|
|
3909
|
+
finishReject(
|
|
3910
|
+
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3911
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3912
|
+
origin: "queue",
|
|
3913
|
+
retryable: true
|
|
3914
|
+
})
|
|
3915
|
+
);
|
|
3529
3916
|
return;
|
|
3530
3917
|
}
|
|
3531
|
-
if (
|
|
3532
|
-
|
|
3533
|
-
type: "accepted",
|
|
3534
|
-
requestId: request.requestId
|
|
3535
|
-
});
|
|
3536
|
-
void this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs).then(() => {
|
|
3537
|
-
writeQueueMessage(socket, {
|
|
3538
|
-
type: "set_mode_result",
|
|
3539
|
-
requestId: request.requestId,
|
|
3540
|
-
modeId: request.modeId
|
|
3541
|
-
});
|
|
3542
|
-
}).catch((error) => {
|
|
3543
|
-
writeQueueMessage(
|
|
3544
|
-
socket,
|
|
3545
|
-
makeQueueOwnerErrorFromUnknown(
|
|
3546
|
-
request.requestId,
|
|
3547
|
-
error,
|
|
3548
|
-
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
3549
|
-
)
|
|
3550
|
-
);
|
|
3551
|
-
}).finally(() => {
|
|
3552
|
-
if (!socket.destroyed) {
|
|
3553
|
-
socket.end();
|
|
3554
|
-
}
|
|
3555
|
-
});
|
|
3918
|
+
if (message.type === "accepted") {
|
|
3919
|
+
acknowledged = true;
|
|
3556
3920
|
return;
|
|
3557
3921
|
}
|
|
3558
|
-
if (
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
});
|
|
3569
|
-
}).catch((error) => {
|
|
3570
|
-
writeQueueMessage(
|
|
3571
|
-
socket,
|
|
3572
|
-
makeQueueOwnerErrorFromUnknown(
|
|
3573
|
-
request.requestId,
|
|
3574
|
-
error,
|
|
3575
|
-
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
3576
|
-
)
|
|
3577
|
-
);
|
|
3578
|
-
}).finally(() => {
|
|
3579
|
-
if (!socket.destroyed) {
|
|
3580
|
-
socket.end();
|
|
3581
|
-
}
|
|
3582
|
-
});
|
|
3922
|
+
if (message.type === "error") {
|
|
3923
|
+
finishReject(
|
|
3924
|
+
new QueueConnectionError(message.message, {
|
|
3925
|
+
outputCode: message.code,
|
|
3926
|
+
detailCode: message.detailCode,
|
|
3927
|
+
origin: message.origin ?? "queue",
|
|
3928
|
+
retryable: message.retryable,
|
|
3929
|
+
acp: message.acp
|
|
3930
|
+
})
|
|
3931
|
+
);
|
|
3583
3932
|
return;
|
|
3584
3933
|
}
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
writeQueueMessage(socket, message);
|
|
3595
|
-
},
|
|
3596
|
-
close: () => {
|
|
3597
|
-
if (!socket.destroyed) {
|
|
3598
|
-
socket.end();
|
|
3599
|
-
}
|
|
3600
|
-
}
|
|
3601
|
-
};
|
|
3602
|
-
writeQueueMessage(socket, {
|
|
3603
|
-
type: "accepted",
|
|
3604
|
-
requestId: request.requestId
|
|
3605
|
-
});
|
|
3606
|
-
if (!request.waitForCompletion) {
|
|
3607
|
-
task.close();
|
|
3934
|
+
if (!acknowledged) {
|
|
3935
|
+
finishReject(
|
|
3936
|
+
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3937
|
+
detailCode: "QUEUE_ACK_MISSING",
|
|
3938
|
+
origin: "queue",
|
|
3939
|
+
retryable: true
|
|
3940
|
+
})
|
|
3941
|
+
);
|
|
3942
|
+
return;
|
|
3608
3943
|
}
|
|
3609
|
-
|
|
3944
|
+
if (!isExpectedResponse(message)) {
|
|
3945
|
+
finishReject(
|
|
3946
|
+
new QueueProtocolError("Queue owner returned unexpected response", {
|
|
3947
|
+
detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
|
|
3948
|
+
origin: "queue",
|
|
3949
|
+
retryable: true
|
|
3950
|
+
})
|
|
3951
|
+
);
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
finishResolve(message);
|
|
3610
3955
|
};
|
|
3611
3956
|
socket.on("data", (chunk) => {
|
|
3612
3957
|
buffer += chunk;
|
|
@@ -3620,315 +3965,539 @@ var SessionQueueOwner = class _SessionQueueOwner {
|
|
|
3620
3965
|
index = buffer.indexOf("\n");
|
|
3621
3966
|
}
|
|
3622
3967
|
});
|
|
3623
|
-
socket.
|
|
3968
|
+
socket.once("error", (error) => {
|
|
3969
|
+
finishReject(error);
|
|
3970
|
+
});
|
|
3971
|
+
socket.once("close", () => {
|
|
3972
|
+
if (settled) {
|
|
3973
|
+
return;
|
|
3974
|
+
}
|
|
3975
|
+
if (!acknowledged) {
|
|
3976
|
+
finishReject(
|
|
3977
|
+
new QueueConnectionError(
|
|
3978
|
+
"Queue owner disconnected before acknowledging request",
|
|
3979
|
+
{
|
|
3980
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
3981
|
+
origin: "queue",
|
|
3982
|
+
retryable: true
|
|
3983
|
+
}
|
|
3984
|
+
)
|
|
3985
|
+
);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3988
|
+
finishReject(
|
|
3989
|
+
new QueueConnectionError("Queue owner disconnected before responding", {
|
|
3990
|
+
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
3991
|
+
origin: "queue",
|
|
3992
|
+
retryable: true
|
|
3993
|
+
})
|
|
3994
|
+
);
|
|
3995
|
+
});
|
|
3996
|
+
socket.write(`${JSON.stringify(request)}
|
|
3997
|
+
`);
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
async function submitCancelToQueueOwner(owner) {
|
|
4001
|
+
const request = {
|
|
4002
|
+
type: "cancel_prompt",
|
|
4003
|
+
requestId: randomUUID2()
|
|
4004
|
+
};
|
|
4005
|
+
const response = await submitControlToQueueOwner(
|
|
4006
|
+
owner,
|
|
4007
|
+
request,
|
|
4008
|
+
(message) => message.type === "cancel_result"
|
|
4009
|
+
);
|
|
4010
|
+
if (!response) {
|
|
4011
|
+
return void 0;
|
|
4012
|
+
}
|
|
4013
|
+
if (response.requestId !== request.requestId) {
|
|
4014
|
+
throw new QueueProtocolError("Queue owner returned mismatched cancel response", {
|
|
4015
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4016
|
+
origin: "queue",
|
|
4017
|
+
retryable: true
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
return response.cancelled;
|
|
4021
|
+
}
|
|
4022
|
+
async function submitSetModeToQueueOwner(owner, modeId, timeoutMs) {
|
|
4023
|
+
const request = {
|
|
4024
|
+
type: "set_mode",
|
|
4025
|
+
requestId: randomUUID2(),
|
|
4026
|
+
modeId,
|
|
4027
|
+
timeoutMs
|
|
4028
|
+
};
|
|
4029
|
+
const response = await submitControlToQueueOwner(
|
|
4030
|
+
owner,
|
|
4031
|
+
request,
|
|
4032
|
+
(message) => message.type === "set_mode_result"
|
|
4033
|
+
);
|
|
4034
|
+
if (!response) {
|
|
4035
|
+
return void 0;
|
|
4036
|
+
}
|
|
4037
|
+
if (response.requestId !== request.requestId) {
|
|
4038
|
+
throw new QueueProtocolError("Queue owner returned mismatched set_mode response", {
|
|
4039
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4040
|
+
origin: "queue",
|
|
4041
|
+
retryable: true
|
|
4042
|
+
});
|
|
4043
|
+
}
|
|
4044
|
+
return true;
|
|
4045
|
+
}
|
|
4046
|
+
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
|
|
4047
|
+
const request = {
|
|
4048
|
+
type: "set_config_option",
|
|
4049
|
+
requestId: randomUUID2(),
|
|
4050
|
+
configId,
|
|
4051
|
+
value,
|
|
4052
|
+
timeoutMs
|
|
4053
|
+
};
|
|
4054
|
+
const response = await submitControlToQueueOwner(
|
|
4055
|
+
owner,
|
|
4056
|
+
request,
|
|
4057
|
+
(message) => message.type === "set_config_option_result"
|
|
4058
|
+
);
|
|
4059
|
+
if (!response) {
|
|
4060
|
+
return void 0;
|
|
4061
|
+
}
|
|
4062
|
+
if (response.requestId !== request.requestId) {
|
|
4063
|
+
throw new QueueProtocolError(
|
|
4064
|
+
"Queue owner returned mismatched set_config_option response",
|
|
4065
|
+
{
|
|
4066
|
+
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4067
|
+
origin: "queue",
|
|
4068
|
+
retryable: true
|
|
4069
|
+
}
|
|
4070
|
+
);
|
|
4071
|
+
}
|
|
4072
|
+
return response.response;
|
|
4073
|
+
}
|
|
4074
|
+
async function trySubmitToRunningOwner(options) {
|
|
4075
|
+
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4076
|
+
if (!owner) {
|
|
4077
|
+
return void 0;
|
|
4078
|
+
}
|
|
4079
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4080
|
+
return void 0;
|
|
4081
|
+
}
|
|
4082
|
+
const submitted = await submitToQueueOwner(owner, options);
|
|
4083
|
+
if (submitted) {
|
|
4084
|
+
if (options.verbose) {
|
|
4085
|
+
process.stderr.write(
|
|
4086
|
+
`[acpx] queued prompt on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4087
|
+
`
|
|
4088
|
+
);
|
|
4089
|
+
}
|
|
4090
|
+
return submitted;
|
|
4091
|
+
}
|
|
4092
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4093
|
+
return void 0;
|
|
4094
|
+
}
|
|
4095
|
+
throw new QueueConnectionError(
|
|
4096
|
+
"Session queue owner is running but not accepting queue requests",
|
|
4097
|
+
{
|
|
4098
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4099
|
+
origin: "queue",
|
|
4100
|
+
retryable: true
|
|
4101
|
+
}
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
async function tryCancelOnRunningOwner(options) {
|
|
4105
|
+
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4106
|
+
if (!owner) {
|
|
4107
|
+
return void 0;
|
|
4108
|
+
}
|
|
4109
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4110
|
+
return void 0;
|
|
4111
|
+
}
|
|
4112
|
+
const cancelled2 = await submitCancelToQueueOwner(owner);
|
|
4113
|
+
if (cancelled2 !== void 0) {
|
|
4114
|
+
if (options.verbose) {
|
|
4115
|
+
process.stderr.write(
|
|
4116
|
+
`[acpx] requested cancel on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4117
|
+
`
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
return cancelled2;
|
|
4121
|
+
}
|
|
4122
|
+
if (!await ensureOwnerIsUsable(options.sessionId, owner)) {
|
|
4123
|
+
return void 0;
|
|
4124
|
+
}
|
|
4125
|
+
throw new QueueConnectionError(
|
|
4126
|
+
"Session queue owner is running but not accepting cancel requests",
|
|
4127
|
+
{
|
|
4128
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4129
|
+
origin: "queue",
|
|
4130
|
+
retryable: true
|
|
4131
|
+
}
|
|
4132
|
+
);
|
|
4133
|
+
}
|
|
4134
|
+
async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
|
|
4135
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
4136
|
+
if (!owner) {
|
|
4137
|
+
return void 0;
|
|
4138
|
+
}
|
|
4139
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4140
|
+
return void 0;
|
|
4141
|
+
}
|
|
4142
|
+
const submitted = await submitSetModeToQueueOwner(owner, modeId, timeoutMs);
|
|
4143
|
+
if (submitted) {
|
|
4144
|
+
if (verbose) {
|
|
4145
|
+
process.stderr.write(
|
|
4146
|
+
`[acpx] requested session/set_mode on owner pid ${owner.pid} for session ${sessionId}
|
|
4147
|
+
`
|
|
4148
|
+
);
|
|
4149
|
+
}
|
|
4150
|
+
return true;
|
|
4151
|
+
}
|
|
4152
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4153
|
+
return void 0;
|
|
4154
|
+
}
|
|
4155
|
+
throw new QueueConnectionError(
|
|
4156
|
+
"Session queue owner is running but not accepting set_mode requests",
|
|
4157
|
+
{
|
|
4158
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4159
|
+
origin: "queue",
|
|
4160
|
+
retryable: true
|
|
4161
|
+
}
|
|
4162
|
+
);
|
|
4163
|
+
}
|
|
4164
|
+
async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
|
|
4165
|
+
const owner = await readQueueOwnerRecord(sessionId);
|
|
4166
|
+
if (!owner) {
|
|
4167
|
+
return void 0;
|
|
4168
|
+
}
|
|
4169
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4170
|
+
return void 0;
|
|
4171
|
+
}
|
|
4172
|
+
const response = await submitSetConfigOptionToQueueOwner(
|
|
4173
|
+
owner,
|
|
4174
|
+
configId,
|
|
4175
|
+
value,
|
|
4176
|
+
timeoutMs
|
|
4177
|
+
);
|
|
4178
|
+
if (response) {
|
|
4179
|
+
if (verbose) {
|
|
4180
|
+
process.stderr.write(
|
|
4181
|
+
`[acpx] requested session/set_config_option on owner pid ${owner.pid} for session ${sessionId}
|
|
4182
|
+
`
|
|
4183
|
+
);
|
|
4184
|
+
}
|
|
4185
|
+
return response;
|
|
4186
|
+
}
|
|
4187
|
+
if (!await ensureOwnerIsUsable(sessionId, owner)) {
|
|
4188
|
+
return void 0;
|
|
4189
|
+
}
|
|
4190
|
+
throw new QueueConnectionError(
|
|
4191
|
+
"Session queue owner is running but not accepting set_config_option requests",
|
|
4192
|
+
{
|
|
4193
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4194
|
+
origin: "queue",
|
|
4195
|
+
retryable: true
|
|
4196
|
+
}
|
|
4197
|
+
);
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
// src/queue-ipc-server.ts
|
|
4201
|
+
import net2 from "net";
|
|
4202
|
+
function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
|
|
4203
|
+
return {
|
|
4204
|
+
type: "error",
|
|
4205
|
+
requestId,
|
|
4206
|
+
code: "RUNTIME",
|
|
4207
|
+
detailCode,
|
|
4208
|
+
origin: "queue",
|
|
4209
|
+
retryable: options.retryable,
|
|
4210
|
+
message
|
|
4211
|
+
};
|
|
4212
|
+
}
|
|
4213
|
+
function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
|
|
4214
|
+
const normalized = normalizeOutputError(error, {
|
|
4215
|
+
defaultCode: "RUNTIME",
|
|
4216
|
+
origin: "queue",
|
|
4217
|
+
detailCode,
|
|
4218
|
+
retryable: options.retryable
|
|
4219
|
+
});
|
|
4220
|
+
return {
|
|
4221
|
+
type: "error",
|
|
4222
|
+
requestId,
|
|
4223
|
+
code: normalized.code,
|
|
4224
|
+
detailCode: normalized.detailCode,
|
|
4225
|
+
origin: normalized.origin,
|
|
4226
|
+
message: normalized.message,
|
|
4227
|
+
retryable: normalized.retryable,
|
|
4228
|
+
acp: normalized.acp
|
|
4229
|
+
};
|
|
4230
|
+
}
|
|
4231
|
+
function writeQueueMessage(socket, message) {
|
|
4232
|
+
if (socket.destroyed || !socket.writable) {
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
socket.write(`${JSON.stringify(message)}
|
|
4236
|
+
`);
|
|
4237
|
+
}
|
|
4238
|
+
var SessionQueueOwner = class _SessionQueueOwner {
|
|
4239
|
+
server;
|
|
4240
|
+
controlHandlers;
|
|
4241
|
+
pending = [];
|
|
4242
|
+
waiters = [];
|
|
4243
|
+
closed = false;
|
|
4244
|
+
constructor(server, controlHandlers) {
|
|
4245
|
+
this.server = server;
|
|
4246
|
+
this.controlHandlers = controlHandlers;
|
|
4247
|
+
}
|
|
4248
|
+
static async start(lease, controlHandlers) {
|
|
4249
|
+
const ownerRef = { current: void 0 };
|
|
4250
|
+
const server = net2.createServer((socket) => {
|
|
4251
|
+
ownerRef.current?.handleConnection(socket);
|
|
4252
|
+
});
|
|
4253
|
+
ownerRef.current = new _SessionQueueOwner(server, controlHandlers);
|
|
4254
|
+
await new Promise((resolve, reject) => {
|
|
4255
|
+
const onListening = () => {
|
|
4256
|
+
server.off("error", onError);
|
|
4257
|
+
resolve();
|
|
4258
|
+
};
|
|
4259
|
+
const onError = (error) => {
|
|
4260
|
+
server.off("listening", onListening);
|
|
4261
|
+
reject(error);
|
|
4262
|
+
};
|
|
4263
|
+
server.once("listening", onListening);
|
|
4264
|
+
server.once("error", onError);
|
|
4265
|
+
server.listen(lease.socketPath);
|
|
3624
4266
|
});
|
|
4267
|
+
return ownerRef.current;
|
|
3625
4268
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
sessionId: options.sessionId,
|
|
3646
|
-
requestId,
|
|
3647
|
-
stream: "prompt"
|
|
3648
|
-
});
|
|
3649
|
-
return await new Promise((resolve, reject) => {
|
|
3650
|
-
let settled = false;
|
|
3651
|
-
let acknowledged = false;
|
|
3652
|
-
let buffer = "";
|
|
3653
|
-
let sawDone = false;
|
|
3654
|
-
const finishResolve = (result) => {
|
|
3655
|
-
if (settled) {
|
|
3656
|
-
return;
|
|
3657
|
-
}
|
|
3658
|
-
settled = true;
|
|
3659
|
-
socket.removeAllListeners();
|
|
3660
|
-
if (!socket.destroyed) {
|
|
3661
|
-
socket.end();
|
|
3662
|
-
}
|
|
3663
|
-
resolve(result);
|
|
3664
|
-
};
|
|
3665
|
-
const finishReject = (error) => {
|
|
3666
|
-
if (settled) {
|
|
3667
|
-
return;
|
|
3668
|
-
}
|
|
3669
|
-
settled = true;
|
|
3670
|
-
socket.removeAllListeners();
|
|
3671
|
-
if (!socket.destroyed) {
|
|
3672
|
-
socket.destroy();
|
|
3673
|
-
}
|
|
3674
|
-
reject(error);
|
|
3675
|
-
};
|
|
3676
|
-
const processLine = (line) => {
|
|
3677
|
-
let parsed;
|
|
3678
|
-
try {
|
|
3679
|
-
parsed = JSON.parse(line);
|
|
3680
|
-
} catch {
|
|
3681
|
-
finishReject(
|
|
3682
|
-
new QueueProtocolError("Queue owner sent invalid JSON payload", {
|
|
3683
|
-
detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
|
|
3684
|
-
origin: "queue",
|
|
3685
|
-
retryable: true
|
|
3686
|
-
})
|
|
3687
|
-
);
|
|
3688
|
-
return;
|
|
3689
|
-
}
|
|
3690
|
-
const message = parseQueueOwnerMessage(parsed);
|
|
3691
|
-
if (!message || message.requestId !== requestId) {
|
|
3692
|
-
finishReject(
|
|
3693
|
-
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3694
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3695
|
-
origin: "queue",
|
|
3696
|
-
retryable: true
|
|
3697
|
-
})
|
|
3698
|
-
);
|
|
3699
|
-
return;
|
|
3700
|
-
}
|
|
3701
|
-
if (message.type === "accepted") {
|
|
3702
|
-
acknowledged = true;
|
|
3703
|
-
options.outputFormatter.setContext({
|
|
3704
|
-
sessionId: options.sessionId,
|
|
3705
|
-
requestId: message.requestId,
|
|
3706
|
-
stream: "prompt"
|
|
3707
|
-
});
|
|
3708
|
-
if (!options.waitForCompletion) {
|
|
3709
|
-
const queued = {
|
|
3710
|
-
queued: true,
|
|
3711
|
-
sessionId: options.sessionId,
|
|
3712
|
-
requestId
|
|
3713
|
-
};
|
|
3714
|
-
finishResolve(queued);
|
|
3715
|
-
}
|
|
3716
|
-
return;
|
|
3717
|
-
}
|
|
3718
|
-
if (message.type === "error") {
|
|
3719
|
-
options.outputFormatter.setContext({
|
|
3720
|
-
sessionId: options.sessionId,
|
|
3721
|
-
requestId: message.requestId,
|
|
3722
|
-
stream: "prompt"
|
|
3723
|
-
});
|
|
3724
|
-
options.outputFormatter.onError({
|
|
3725
|
-
code: message.code ?? "RUNTIME",
|
|
3726
|
-
detailCode: message.detailCode,
|
|
3727
|
-
origin: message.origin ?? "queue",
|
|
3728
|
-
message: message.message,
|
|
3729
|
-
retryable: message.retryable,
|
|
3730
|
-
acp: message.acp
|
|
3731
|
-
});
|
|
3732
|
-
options.outputFormatter.flush();
|
|
3733
|
-
const queueErrorAlreadyEmitted = options.errorEmissionPolicy?.queueErrorAlreadyEmitted ?? true;
|
|
3734
|
-
finishReject(
|
|
3735
|
-
new QueueConnectionError(message.message, {
|
|
3736
|
-
outputCode: message.code,
|
|
3737
|
-
detailCode: message.detailCode,
|
|
3738
|
-
origin: message.origin ?? "queue",
|
|
3739
|
-
retryable: message.retryable,
|
|
3740
|
-
acp: message.acp,
|
|
3741
|
-
...queueErrorAlreadyEmitted ? { outputAlreadyEmitted: true } : {}
|
|
3742
|
-
})
|
|
3743
|
-
);
|
|
3744
|
-
return;
|
|
3745
|
-
}
|
|
3746
|
-
if (!acknowledged) {
|
|
3747
|
-
finishReject(
|
|
3748
|
-
new QueueConnectionError("Queue owner did not acknowledge request", {
|
|
3749
|
-
detailCode: "QUEUE_ACK_MISSING",
|
|
3750
|
-
origin: "queue",
|
|
3751
|
-
retryable: true
|
|
3752
|
-
})
|
|
4269
|
+
async close() {
|
|
4270
|
+
if (this.closed) {
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
this.closed = true;
|
|
4274
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
4275
|
+
waiter(void 0);
|
|
4276
|
+
}
|
|
4277
|
+
for (const task of this.pending.splice(0)) {
|
|
4278
|
+
if (task.waitForCompletion) {
|
|
4279
|
+
task.send(
|
|
4280
|
+
makeQueueOwnerError(
|
|
4281
|
+
task.requestId,
|
|
4282
|
+
"Queue owner shutting down before prompt execution",
|
|
4283
|
+
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
4284
|
+
{
|
|
4285
|
+
retryable: true
|
|
4286
|
+
}
|
|
4287
|
+
)
|
|
3753
4288
|
);
|
|
3754
|
-
return;
|
|
3755
|
-
}
|
|
3756
|
-
if (message.type === "session_update") {
|
|
3757
|
-
options.outputFormatter.onSessionUpdate(message.notification);
|
|
3758
|
-
return;
|
|
3759
|
-
}
|
|
3760
|
-
if (message.type === "client_operation") {
|
|
3761
|
-
options.outputFormatter.onClientOperation(message.operation);
|
|
3762
|
-
return;
|
|
3763
|
-
}
|
|
3764
|
-
if (message.type === "done") {
|
|
3765
|
-
options.outputFormatter.onDone(message.stopReason);
|
|
3766
|
-
sawDone = true;
|
|
3767
|
-
return;
|
|
3768
|
-
}
|
|
3769
|
-
if (message.type === "result") {
|
|
3770
|
-
if (!sawDone) {
|
|
3771
|
-
options.outputFormatter.onDone(message.result.stopReason);
|
|
3772
|
-
}
|
|
3773
|
-
options.outputFormatter.flush();
|
|
3774
|
-
finishResolve(message.result);
|
|
3775
|
-
return;
|
|
3776
4289
|
}
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
4290
|
+
task.close();
|
|
4291
|
+
}
|
|
4292
|
+
await new Promise((resolve) => {
|
|
4293
|
+
this.server.close(() => resolve());
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
async nextTask(timeoutMs) {
|
|
4297
|
+
if (this.pending.length > 0) {
|
|
4298
|
+
return this.pending.shift();
|
|
4299
|
+
}
|
|
4300
|
+
if (this.closed) {
|
|
4301
|
+
return void 0;
|
|
4302
|
+
}
|
|
4303
|
+
return await new Promise((resolve) => {
|
|
4304
|
+
const shouldTimeout = timeoutMs != null;
|
|
4305
|
+
const timer = shouldTimeout && setTimeout(
|
|
4306
|
+
() => {
|
|
4307
|
+
const index = this.waiters.indexOf(waiter);
|
|
4308
|
+
if (index >= 0) {
|
|
4309
|
+
this.waiters.splice(index, 1);
|
|
4310
|
+
}
|
|
4311
|
+
resolve(void 0);
|
|
4312
|
+
},
|
|
4313
|
+
Math.max(0, timeoutMs)
|
|
3783
4314
|
);
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
let index = buffer.indexOf("\n");
|
|
3788
|
-
while (index >= 0) {
|
|
3789
|
-
const line = buffer.slice(0, index).trim();
|
|
3790
|
-
buffer = buffer.slice(index + 1);
|
|
3791
|
-
if (line.length > 0) {
|
|
3792
|
-
processLine(line);
|
|
4315
|
+
const waiter = (task) => {
|
|
4316
|
+
if (timer) {
|
|
4317
|
+
clearTimeout(timer);
|
|
3793
4318
|
}
|
|
3794
|
-
|
|
3795
|
-
}
|
|
3796
|
-
|
|
3797
|
-
socket.once("error", (error) => {
|
|
3798
|
-
finishReject(error);
|
|
4319
|
+
resolve(task);
|
|
4320
|
+
};
|
|
4321
|
+
this.waiters.push(waiter);
|
|
3799
4322
|
});
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
4323
|
+
}
|
|
4324
|
+
queueDepth() {
|
|
4325
|
+
return this.pending.length;
|
|
4326
|
+
}
|
|
4327
|
+
enqueue(task) {
|
|
4328
|
+
if (this.closed) {
|
|
4329
|
+
if (task.waitForCompletion) {
|
|
4330
|
+
task.send(
|
|
4331
|
+
makeQueueOwnerError(
|
|
4332
|
+
task.requestId,
|
|
4333
|
+
"Queue owner is shutting down",
|
|
4334
|
+
"QUEUE_OWNER_SHUTTING_DOWN",
|
|
4335
|
+
{
|
|
3811
4336
|
retryable: true
|
|
3812
4337
|
}
|
|
3813
4338
|
)
|
|
3814
4339
|
);
|
|
3815
|
-
return;
|
|
3816
|
-
}
|
|
3817
|
-
if (!options.waitForCompletion) {
|
|
3818
|
-
const queued = {
|
|
3819
|
-
queued: true,
|
|
3820
|
-
sessionId: options.sessionId,
|
|
3821
|
-
requestId
|
|
3822
|
-
};
|
|
3823
|
-
finishResolve(queued);
|
|
3824
|
-
return;
|
|
3825
4340
|
}
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
4341
|
+
task.close();
|
|
4342
|
+
return;
|
|
4343
|
+
}
|
|
4344
|
+
const waiter = this.waiters.shift();
|
|
4345
|
+
if (waiter) {
|
|
4346
|
+
waiter(task);
|
|
4347
|
+
return;
|
|
4348
|
+
}
|
|
4349
|
+
this.pending.push(task);
|
|
4350
|
+
}
|
|
4351
|
+
handleConnection(socket) {
|
|
4352
|
+
socket.setEncoding("utf8");
|
|
4353
|
+
if (this.closed) {
|
|
4354
|
+
writeQueueMessage(
|
|
4355
|
+
socket,
|
|
4356
|
+
makeQueueOwnerError("unknown", "Queue owner is closed", "QUEUE_OWNER_CLOSED", {
|
|
3830
4357
|
retryable: true
|
|
3831
4358
|
})
|
|
3832
4359
|
);
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
});
|
|
3837
|
-
}
|
|
3838
|
-
async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
3839
|
-
const socket = await connectToQueueOwner(owner);
|
|
3840
|
-
if (!socket) {
|
|
3841
|
-
return void 0;
|
|
3842
|
-
}
|
|
3843
|
-
socket.setEncoding("utf8");
|
|
3844
|
-
return await new Promise((resolve, reject) => {
|
|
3845
|
-
let settled = false;
|
|
3846
|
-
let acknowledged = false;
|
|
4360
|
+
socket.end();
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
3847
4363
|
let buffer = "";
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
resolve(result);
|
|
4364
|
+
let handled = false;
|
|
4365
|
+
const fail = (requestId, message, detailCode) => {
|
|
4366
|
+
writeQueueMessage(
|
|
4367
|
+
socket,
|
|
4368
|
+
makeQueueOwnerError(requestId, message, detailCode, {
|
|
4369
|
+
retryable: false
|
|
4370
|
+
})
|
|
4371
|
+
);
|
|
4372
|
+
socket.end();
|
|
3858
4373
|
};
|
|
3859
|
-
const
|
|
3860
|
-
if (
|
|
4374
|
+
const processLine = (line) => {
|
|
4375
|
+
if (handled) {
|
|
3861
4376
|
return;
|
|
3862
4377
|
}
|
|
3863
|
-
|
|
3864
|
-
socket.removeAllListeners();
|
|
3865
|
-
if (!socket.destroyed) {
|
|
3866
|
-
socket.destroy();
|
|
3867
|
-
}
|
|
3868
|
-
reject(error);
|
|
3869
|
-
};
|
|
3870
|
-
const processLine = (line) => {
|
|
4378
|
+
handled = true;
|
|
3871
4379
|
let parsed;
|
|
3872
4380
|
try {
|
|
3873
4381
|
parsed = JSON.parse(line);
|
|
3874
4382
|
} catch {
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
retryable: true
|
|
3880
|
-
})
|
|
4383
|
+
fail(
|
|
4384
|
+
"unknown",
|
|
4385
|
+
"Invalid queue request payload",
|
|
4386
|
+
"QUEUE_REQUEST_PAYLOAD_INVALID_JSON"
|
|
3881
4387
|
);
|
|
3882
4388
|
return;
|
|
3883
4389
|
}
|
|
3884
|
-
const
|
|
3885
|
-
if (!
|
|
3886
|
-
|
|
3887
|
-
new QueueProtocolError("Queue owner sent malformed message", {
|
|
3888
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3889
|
-
origin: "queue",
|
|
3890
|
-
retryable: true
|
|
3891
|
-
})
|
|
3892
|
-
);
|
|
4390
|
+
const request = parseQueueRequest(parsed);
|
|
4391
|
+
if (!request) {
|
|
4392
|
+
fail("unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
|
|
3893
4393
|
return;
|
|
3894
4394
|
}
|
|
3895
|
-
if (
|
|
3896
|
-
|
|
4395
|
+
if (request.type === "cancel_prompt") {
|
|
4396
|
+
writeQueueMessage(socket, {
|
|
4397
|
+
type: "accepted",
|
|
4398
|
+
requestId: request.requestId
|
|
4399
|
+
});
|
|
4400
|
+
void this.controlHandlers.cancelPrompt().then((cancelled2) => {
|
|
4401
|
+
writeQueueMessage(socket, {
|
|
4402
|
+
type: "cancel_result",
|
|
4403
|
+
requestId: request.requestId,
|
|
4404
|
+
cancelled: cancelled2
|
|
4405
|
+
});
|
|
4406
|
+
}).catch((error) => {
|
|
4407
|
+
writeQueueMessage(
|
|
4408
|
+
socket,
|
|
4409
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4410
|
+
request.requestId,
|
|
4411
|
+
error,
|
|
4412
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4413
|
+
)
|
|
4414
|
+
);
|
|
4415
|
+
}).finally(() => {
|
|
4416
|
+
if (!socket.destroyed) {
|
|
4417
|
+
socket.end();
|
|
4418
|
+
}
|
|
4419
|
+
});
|
|
3897
4420
|
return;
|
|
3898
4421
|
}
|
|
3899
|
-
if (
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
4422
|
+
if (request.type === "set_mode") {
|
|
4423
|
+
writeQueueMessage(socket, {
|
|
4424
|
+
type: "accepted",
|
|
4425
|
+
requestId: request.requestId
|
|
4426
|
+
});
|
|
4427
|
+
void this.controlHandlers.setSessionMode(request.modeId, request.timeoutMs).then(() => {
|
|
4428
|
+
writeQueueMessage(socket, {
|
|
4429
|
+
type: "set_mode_result",
|
|
4430
|
+
requestId: request.requestId,
|
|
4431
|
+
modeId: request.modeId
|
|
4432
|
+
});
|
|
4433
|
+
}).catch((error) => {
|
|
4434
|
+
writeQueueMessage(
|
|
4435
|
+
socket,
|
|
4436
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4437
|
+
request.requestId,
|
|
4438
|
+
error,
|
|
4439
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4440
|
+
)
|
|
4441
|
+
);
|
|
4442
|
+
}).finally(() => {
|
|
4443
|
+
if (!socket.destroyed) {
|
|
4444
|
+
socket.end();
|
|
4445
|
+
}
|
|
4446
|
+
});
|
|
3909
4447
|
return;
|
|
3910
4448
|
}
|
|
3911
|
-
if (
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
4449
|
+
if (request.type === "set_config_option") {
|
|
4450
|
+
writeQueueMessage(socket, {
|
|
4451
|
+
type: "accepted",
|
|
4452
|
+
requestId: request.requestId
|
|
4453
|
+
});
|
|
4454
|
+
void this.controlHandlers.setSessionConfigOption(request.configId, request.value, request.timeoutMs).then((response) => {
|
|
4455
|
+
writeQueueMessage(socket, {
|
|
4456
|
+
type: "set_config_option_result",
|
|
4457
|
+
requestId: request.requestId,
|
|
4458
|
+
response
|
|
4459
|
+
});
|
|
4460
|
+
}).catch((error) => {
|
|
4461
|
+
writeQueueMessage(
|
|
4462
|
+
socket,
|
|
4463
|
+
makeQueueOwnerErrorFromUnknown(
|
|
4464
|
+
request.requestId,
|
|
4465
|
+
error,
|
|
4466
|
+
"QUEUE_CONTROL_REQUEST_FAILED"
|
|
4467
|
+
)
|
|
4468
|
+
);
|
|
4469
|
+
}).finally(() => {
|
|
4470
|
+
if (!socket.destroyed) {
|
|
4471
|
+
socket.end();
|
|
4472
|
+
}
|
|
4473
|
+
});
|
|
3919
4474
|
return;
|
|
3920
4475
|
}
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
4476
|
+
const task = {
|
|
4477
|
+
requestId: request.requestId,
|
|
4478
|
+
message: request.message,
|
|
4479
|
+
permissionMode: request.permissionMode,
|
|
4480
|
+
nonInteractivePermissions: request.nonInteractivePermissions,
|
|
4481
|
+
timeoutMs: request.timeoutMs,
|
|
4482
|
+
suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
|
|
4483
|
+
waitForCompletion: request.waitForCompletion,
|
|
4484
|
+
send: (message) => {
|
|
4485
|
+
writeQueueMessage(socket, message);
|
|
4486
|
+
},
|
|
4487
|
+
close: () => {
|
|
4488
|
+
if (!socket.destroyed) {
|
|
4489
|
+
socket.end();
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4493
|
+
writeQueueMessage(socket, {
|
|
4494
|
+
type: "accepted",
|
|
4495
|
+
requestId: request.requestId
|
|
4496
|
+
});
|
|
4497
|
+
if (!request.waitForCompletion) {
|
|
4498
|
+
task.close();
|
|
3930
4499
|
}
|
|
3931
|
-
|
|
4500
|
+
this.enqueue(task);
|
|
3932
4501
|
};
|
|
3933
4502
|
socket.on("data", (chunk) => {
|
|
3934
4503
|
buffer += chunk;
|
|
@@ -3936,262 +4505,270 @@ async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
|
|
|
3936
4505
|
while (index >= 0) {
|
|
3937
4506
|
const line = buffer.slice(0, index).trim();
|
|
3938
4507
|
buffer = buffer.slice(index + 1);
|
|
3939
|
-
if (line.length > 0) {
|
|
3940
|
-
processLine(line);
|
|
3941
|
-
}
|
|
3942
|
-
index = buffer.indexOf("\n");
|
|
3943
|
-
}
|
|
3944
|
-
});
|
|
3945
|
-
socket.once("error", (error) => {
|
|
3946
|
-
finishReject(error);
|
|
3947
|
-
});
|
|
3948
|
-
socket.once("close", () => {
|
|
3949
|
-
if (settled) {
|
|
3950
|
-
return;
|
|
3951
|
-
}
|
|
3952
|
-
if (!acknowledged) {
|
|
3953
|
-
finishReject(
|
|
3954
|
-
new QueueConnectionError(
|
|
3955
|
-
"Queue owner disconnected before acknowledging request",
|
|
3956
|
-
{
|
|
3957
|
-
detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
|
|
3958
|
-
origin: "queue",
|
|
3959
|
-
retryable: true
|
|
3960
|
-
}
|
|
3961
|
-
)
|
|
3962
|
-
);
|
|
3963
|
-
return;
|
|
4508
|
+
if (line.length > 0) {
|
|
4509
|
+
processLine(line);
|
|
4510
|
+
}
|
|
4511
|
+
index = buffer.indexOf("\n");
|
|
3964
4512
|
}
|
|
3965
|
-
finishReject(
|
|
3966
|
-
new QueueConnectionError("Queue owner disconnected before responding", {
|
|
3967
|
-
detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
|
|
3968
|
-
origin: "queue",
|
|
3969
|
-
retryable: true
|
|
3970
|
-
})
|
|
3971
|
-
);
|
|
3972
4513
|
});
|
|
3973
|
-
socket.
|
|
3974
|
-
`);
|
|
3975
|
-
});
|
|
3976
|
-
}
|
|
3977
|
-
async function submitCancelToQueueOwner(owner) {
|
|
3978
|
-
const request = {
|
|
3979
|
-
type: "cancel_prompt",
|
|
3980
|
-
requestId: randomUUID2()
|
|
3981
|
-
};
|
|
3982
|
-
const response = await submitControlToQueueOwner(
|
|
3983
|
-
owner,
|
|
3984
|
-
request,
|
|
3985
|
-
(message) => message.type === "cancel_result"
|
|
3986
|
-
);
|
|
3987
|
-
if (!response) {
|
|
3988
|
-
return void 0;
|
|
3989
|
-
}
|
|
3990
|
-
if (response.requestId !== request.requestId) {
|
|
3991
|
-
throw new QueueProtocolError("Queue owner returned mismatched cancel response", {
|
|
3992
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
3993
|
-
origin: "queue",
|
|
3994
|
-
retryable: true
|
|
4514
|
+
socket.on("error", () => {
|
|
3995
4515
|
});
|
|
3996
4516
|
}
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
owner,
|
|
4008
|
-
request,
|
|
4009
|
-
(message) => message.type === "set_mode_result"
|
|
4010
|
-
);
|
|
4011
|
-
if (!response) {
|
|
4012
|
-
return void 0;
|
|
4517
|
+
};
|
|
4518
|
+
|
|
4519
|
+
// src/queue-owner-turn-controller.ts
|
|
4520
|
+
var QueueOwnerTurnController = class {
|
|
4521
|
+
options;
|
|
4522
|
+
state = "idle";
|
|
4523
|
+
pendingCancel = false;
|
|
4524
|
+
activeController;
|
|
4525
|
+
constructor(options) {
|
|
4526
|
+
this.options = options;
|
|
4013
4527
|
}
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4017
|
-
origin: "queue",
|
|
4018
|
-
retryable: true
|
|
4019
|
-
});
|
|
4528
|
+
get lifecycleState() {
|
|
4529
|
+
return this.state;
|
|
4020
4530
|
}
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
|
|
4024
|
-
const request = {
|
|
4025
|
-
type: "set_config_option",
|
|
4026
|
-
requestId: randomUUID2(),
|
|
4027
|
-
configId,
|
|
4028
|
-
value,
|
|
4029
|
-
timeoutMs
|
|
4030
|
-
};
|
|
4031
|
-
const response = await submitControlToQueueOwner(
|
|
4032
|
-
owner,
|
|
4033
|
-
request,
|
|
4034
|
-
(message) => message.type === "set_config_option_result"
|
|
4035
|
-
);
|
|
4036
|
-
if (!response) {
|
|
4037
|
-
return void 0;
|
|
4531
|
+
get hasPendingCancel() {
|
|
4532
|
+
return this.pendingCancel;
|
|
4038
4533
|
}
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
{
|
|
4043
|
-
detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
|
|
4044
|
-
origin: "queue",
|
|
4045
|
-
retryable: true
|
|
4046
|
-
}
|
|
4047
|
-
);
|
|
4534
|
+
beginTurn() {
|
|
4535
|
+
this.state = "starting";
|
|
4536
|
+
this.pendingCancel = false;
|
|
4048
4537
|
}
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
if (!owner) {
|
|
4054
|
-
return void 0;
|
|
4538
|
+
markPromptActive() {
|
|
4539
|
+
if (this.state === "starting" || this.state === "active") {
|
|
4540
|
+
this.state = "active";
|
|
4541
|
+
}
|
|
4055
4542
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4543
|
+
endTurn() {
|
|
4544
|
+
this.state = "idle";
|
|
4545
|
+
this.pendingCancel = false;
|
|
4059
4546
|
}
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
`[acpx] queued prompt on active owner pid ${owner.pid} for session ${options.sessionId}
|
|
4065
|
-
`
|
|
4066
|
-
);
|
|
4067
|
-
}
|
|
4068
|
-
return submitted;
|
|
4547
|
+
beginClosing() {
|
|
4548
|
+
this.state = "closing";
|
|
4549
|
+
this.pendingCancel = false;
|
|
4550
|
+
this.activeController = void 0;
|
|
4069
4551
|
}
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
return void 0;
|
|
4552
|
+
setActiveController(controller) {
|
|
4553
|
+
this.activeController = controller;
|
|
4073
4554
|
}
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4555
|
+
clearActiveController() {
|
|
4556
|
+
this.activeController = void 0;
|
|
4557
|
+
}
|
|
4558
|
+
assertCanHandleControlRequest() {
|
|
4559
|
+
if (this.state === "closing") {
|
|
4560
|
+
throw new QueueConnectionError("Queue owner is closing", {
|
|
4561
|
+
detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
|
|
4562
|
+
origin: "queue",
|
|
4563
|
+
retryable: true
|
|
4564
|
+
});
|
|
4080
4565
|
}
|
|
4081
|
-
);
|
|
4082
|
-
}
|
|
4083
|
-
async function tryCancelOnRunningOwner(options) {
|
|
4084
|
-
const owner = await readQueueOwnerRecord(options.sessionId);
|
|
4085
|
-
if (!owner) {
|
|
4086
|
-
return void 0;
|
|
4087
4566
|
}
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4567
|
+
async requestCancel() {
|
|
4568
|
+
const activeController = this.activeController;
|
|
4569
|
+
if (activeController?.hasActivePrompt()) {
|
|
4570
|
+
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
4571
|
+
if (cancelled2) {
|
|
4572
|
+
this.pendingCancel = false;
|
|
4573
|
+
}
|
|
4574
|
+
return cancelled2;
|
|
4575
|
+
}
|
|
4576
|
+
if (this.state === "starting" || this.state === "active") {
|
|
4577
|
+
this.pendingCancel = true;
|
|
4578
|
+
return true;
|
|
4579
|
+
}
|
|
4580
|
+
return false;
|
|
4091
4581
|
}
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
if (
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4582
|
+
async applyPendingCancel() {
|
|
4583
|
+
const activeController = this.activeController;
|
|
4584
|
+
if (!this.pendingCancel || !activeController || !activeController.hasActivePrompt()) {
|
|
4585
|
+
return false;
|
|
4586
|
+
}
|
|
4587
|
+
const cancelled2 = await activeController.requestCancelActivePrompt();
|
|
4588
|
+
if (cancelled2) {
|
|
4589
|
+
this.pendingCancel = false;
|
|
4099
4590
|
}
|
|
4100
4591
|
return cancelled2;
|
|
4101
4592
|
}
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4593
|
+
async setSessionMode(modeId, timeoutMs) {
|
|
4594
|
+
this.assertCanHandleControlRequest();
|
|
4595
|
+
const activeController = this.activeController;
|
|
4596
|
+
if (activeController) {
|
|
4597
|
+
await this.options.withTimeout(
|
|
4598
|
+
async () => await activeController.setSessionMode(modeId),
|
|
4599
|
+
timeoutMs
|
|
4600
|
+
);
|
|
4601
|
+
return;
|
|
4602
|
+
}
|
|
4603
|
+
await this.options.setSessionModeFallback(modeId, timeoutMs);
|
|
4105
4604
|
}
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4605
|
+
async setSessionConfigOption(configId, value, timeoutMs) {
|
|
4606
|
+
this.assertCanHandleControlRequest();
|
|
4607
|
+
const activeController = this.activeController;
|
|
4608
|
+
if (activeController) {
|
|
4609
|
+
return await this.options.withTimeout(
|
|
4610
|
+
async () => await activeController.setSessionConfigOption(configId, value),
|
|
4611
|
+
timeoutMs
|
|
4612
|
+
);
|
|
4112
4613
|
}
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
return void 0;
|
|
4614
|
+
return await this.options.setSessionConfigOptionFallback(
|
|
4615
|
+
configId,
|
|
4616
|
+
value,
|
|
4617
|
+
timeoutMs
|
|
4618
|
+
);
|
|
4119
4619
|
}
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4620
|
+
};
|
|
4621
|
+
|
|
4622
|
+
// src/session-owner-runtime.ts
|
|
4623
|
+
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4624
|
+
var QUEUE_OWNER_HEARTBEAT_INTERVAL_MS = 2e3;
|
|
4625
|
+
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
4626
|
+
if (ttlMs == null) {
|
|
4627
|
+
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4123
4628
|
}
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4629
|
+
if (!Number.isFinite(ttlMs) || ttlMs < 0) {
|
|
4630
|
+
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4631
|
+
}
|
|
4632
|
+
return Math.round(ttlMs);
|
|
4633
|
+
}
|
|
4634
|
+
function createQueueOwnerTurnRuntime(options, deps) {
|
|
4635
|
+
const turnController = new QueueOwnerTurnController({
|
|
4636
|
+
withTimeout: async (run, timeoutMs) => await deps.withTimeout(run, timeoutMs),
|
|
4637
|
+
setSessionModeFallback: deps.setSessionModeFallback,
|
|
4638
|
+
setSessionConfigOptionFallback: deps.setSessionConfigOptionFallback
|
|
4639
|
+
});
|
|
4640
|
+
const applyPendingCancel = async () => {
|
|
4641
|
+
return await turnController.applyPendingCancel();
|
|
4642
|
+
};
|
|
4643
|
+
const scheduleApplyPendingCancel = () => {
|
|
4644
|
+
void applyPendingCancel().catch((error) => {
|
|
4645
|
+
if (options.verbose) {
|
|
4646
|
+
process.stderr.write(
|
|
4647
|
+
`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}
|
|
4648
|
+
`
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
4651
|
+
});
|
|
4652
|
+
};
|
|
4653
|
+
return {
|
|
4654
|
+
beginClosing: () => {
|
|
4655
|
+
turnController.beginClosing();
|
|
4656
|
+
},
|
|
4657
|
+
onClientAvailable: (controller) => {
|
|
4658
|
+
turnController.setActiveController(controller);
|
|
4659
|
+
scheduleApplyPendingCancel();
|
|
4660
|
+
},
|
|
4661
|
+
onClientClosed: () => {
|
|
4662
|
+
turnController.clearActiveController();
|
|
4663
|
+
},
|
|
4664
|
+
onPromptActive: async () => {
|
|
4665
|
+
turnController.markPromptActive();
|
|
4666
|
+
await applyPendingCancel();
|
|
4667
|
+
},
|
|
4668
|
+
runPromptTurn: async (run) => {
|
|
4669
|
+
turnController.beginTurn();
|
|
4670
|
+
try {
|
|
4671
|
+
return await run();
|
|
4672
|
+
} finally {
|
|
4673
|
+
turnController.endTurn();
|
|
4674
|
+
}
|
|
4675
|
+
},
|
|
4676
|
+
controlHandlers: {
|
|
4677
|
+
cancelPrompt: async () => {
|
|
4678
|
+
const accepted = await turnController.requestCancel();
|
|
4679
|
+
if (!accepted) {
|
|
4680
|
+
return false;
|
|
4681
|
+
}
|
|
4682
|
+
await applyPendingCancel();
|
|
4683
|
+
return true;
|
|
4684
|
+
},
|
|
4685
|
+
setSessionMode: async (modeId, timeoutMs) => {
|
|
4686
|
+
await turnController.setSessionMode(modeId, timeoutMs);
|
|
4687
|
+
},
|
|
4688
|
+
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
4689
|
+
return await turnController.setSessionConfigOption(configId, value, timeoutMs);
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
async function runQueueOwnerProcess(options, deps) {
|
|
4695
|
+
const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
|
|
4696
|
+
const lease = await tryAcquireQueueOwnerLease(options.sessionId);
|
|
4697
|
+
if (!lease) {
|
|
4698
|
+
if (options.verbose) {
|
|
4127
4699
|
process.stderr.write(
|
|
4128
|
-
`[acpx]
|
|
4700
|
+
`[acpx] queue owner already active for session ${options.sessionId}; skipping spawn
|
|
4129
4701
|
`
|
|
4130
4702
|
);
|
|
4131
4703
|
}
|
|
4132
|
-
return
|
|
4133
|
-
}
|
|
4134
|
-
if (!isProcessAlive(owner.pid)) {
|
|
4135
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4136
|
-
return void 0;
|
|
4704
|
+
return;
|
|
4137
4705
|
}
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4706
|
+
const runtime = createQueueOwnerTurnRuntime(options, deps);
|
|
4707
|
+
let owner;
|
|
4708
|
+
let heartbeatTimer;
|
|
4709
|
+
const refreshHeartbeat = async () => {
|
|
4710
|
+
if (!owner) {
|
|
4711
|
+
return;
|
|
4144
4712
|
}
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
}
|
|
4152
|
-
if (!isProcessAlive(owner.pid)) {
|
|
4153
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4154
|
-
return void 0;
|
|
4155
|
-
}
|
|
4156
|
-
const response = await submitSetConfigOptionToQueueOwner(
|
|
4157
|
-
owner,
|
|
4158
|
-
configId,
|
|
4159
|
-
value,
|
|
4160
|
-
timeoutMs
|
|
4161
|
-
);
|
|
4162
|
-
if (response) {
|
|
4163
|
-
if (verbose) {
|
|
4164
|
-
process.stderr.write(
|
|
4165
|
-
`[acpx] requested session/set_config_option on owner pid ${owner.pid} for session ${sessionId}
|
|
4713
|
+
await refreshQueueOwnerLease(lease, {
|
|
4714
|
+
queueDepth: owner.queueDepth()
|
|
4715
|
+
}).catch((error) => {
|
|
4716
|
+
if (options.verbose) {
|
|
4717
|
+
process.stderr.write(
|
|
4718
|
+
`[acpx] queue owner heartbeat update failed: ${formatErrorMessage(error)}
|
|
4166
4719
|
`
|
|
4167
|
-
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4722
|
+
});
|
|
4723
|
+
};
|
|
4724
|
+
try {
|
|
4725
|
+
owner = await SessionQueueOwner.start(lease, runtime.controlHandlers);
|
|
4726
|
+
await refreshHeartbeat();
|
|
4727
|
+
heartbeatTimer = setInterval(() => {
|
|
4728
|
+
void refreshHeartbeat();
|
|
4729
|
+
}, QUEUE_OWNER_HEARTBEAT_INTERVAL_MS);
|
|
4730
|
+
heartbeatTimer.unref();
|
|
4731
|
+
const idleWaitMs = queueOwnerTtlMs === 0 ? void 0 : Math.max(0, queueOwnerTtlMs);
|
|
4732
|
+
while (true) {
|
|
4733
|
+
const task = await owner.nextTask(idleWaitMs);
|
|
4734
|
+
if (!task) {
|
|
4735
|
+
if (queueOwnerTtlMs > 0 && options.verbose) {
|
|
4736
|
+
process.stderr.write(
|
|
4737
|
+
`[acpx] queue owner TTL expired after ${Math.round(queueOwnerTtlMs / 1e3)}s for session ${options.sessionId}; shutting down
|
|
4738
|
+
`
|
|
4739
|
+
);
|
|
4740
|
+
}
|
|
4741
|
+
break;
|
|
4742
|
+
}
|
|
4743
|
+
await runtime.runPromptTurn(async () => {
|
|
4744
|
+
await deps.runQueuedTask(options.sessionId, task, {
|
|
4745
|
+
verbose: options.verbose,
|
|
4746
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
4747
|
+
authCredentials: options.authCredentials,
|
|
4748
|
+
authPolicy: options.authPolicy,
|
|
4749
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
4750
|
+
onClientAvailable: runtime.onClientAvailable,
|
|
4751
|
+
onClientClosed: runtime.onClientClosed,
|
|
4752
|
+
onPromptActive: runtime.onPromptActive
|
|
4753
|
+
});
|
|
4754
|
+
});
|
|
4755
|
+
await refreshHeartbeat();
|
|
4168
4756
|
}
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4173
|
-
return void 0;
|
|
4174
|
-
}
|
|
4175
|
-
throw new QueueConnectionError(
|
|
4176
|
-
"Session queue owner is running but not accepting set_config_option requests",
|
|
4177
|
-
{
|
|
4178
|
-
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
4179
|
-
origin: "queue",
|
|
4180
|
-
retryable: true
|
|
4757
|
+
} finally {
|
|
4758
|
+
if (heartbeatTimer) {
|
|
4759
|
+
clearInterval(heartbeatTimer);
|
|
4181
4760
|
}
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
return;
|
|
4188
|
-
}
|
|
4189
|
-
if (isProcessAlive(owner.pid)) {
|
|
4190
|
-
await terminateProcess(owner.pid);
|
|
4761
|
+
runtime.beginClosing();
|
|
4762
|
+
if (owner) {
|
|
4763
|
+
await owner.close();
|
|
4764
|
+
}
|
|
4765
|
+
await releaseQueueOwnerLease(lease);
|
|
4191
4766
|
}
|
|
4192
|
-
await cleanupStaleQueueOwner(sessionId, owner);
|
|
4193
4767
|
}
|
|
4194
4768
|
|
|
4769
|
+
// src/session-owner-spawn.ts
|
|
4770
|
+
import { spawn as spawn3 } from "child_process";
|
|
4771
|
+
|
|
4195
4772
|
// src/session-persistence.ts
|
|
4196
4773
|
import { statSync } from "fs";
|
|
4197
4774
|
import fs4 from "fs/promises";
|
|
@@ -4363,88 +4940,390 @@ function normalizeName(value) {
|
|
|
4363
4940
|
function isoNow2() {
|
|
4364
4941
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4365
4942
|
}
|
|
4366
|
-
async function listSessions() {
|
|
4367
|
-
await ensureSessionDir();
|
|
4368
|
-
const entries = await fs4.readdir(sessionBaseDir(), { withFileTypes: true });
|
|
4369
|
-
const records = [];
|
|
4943
|
+
async function listSessions() {
|
|
4944
|
+
await ensureSessionDir();
|
|
4945
|
+
const entries = await fs4.readdir(sessionBaseDir(), { withFileTypes: true });
|
|
4946
|
+
const records = [];
|
|
4947
|
+
for (const entry of entries) {
|
|
4948
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
4949
|
+
continue;
|
|
4950
|
+
}
|
|
4951
|
+
const fullPath = path5.join(sessionBaseDir(), entry.name);
|
|
4952
|
+
try {
|
|
4953
|
+
const payload = await fs4.readFile(fullPath, "utf8");
|
|
4954
|
+
const parsed = parseSessionRecord(JSON.parse(payload));
|
|
4955
|
+
if (parsed) {
|
|
4956
|
+
records.push(parsed);
|
|
4957
|
+
}
|
|
4958
|
+
} catch {
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
records.sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));
|
|
4962
|
+
return records;
|
|
4963
|
+
}
|
|
4964
|
+
async function listSessionsForAgent(agentCommand) {
|
|
4965
|
+
const sessions = await listSessions();
|
|
4966
|
+
return sessions.filter((session) => session.agentCommand === agentCommand);
|
|
4967
|
+
}
|
|
4968
|
+
async function findSession(options) {
|
|
4969
|
+
const normalizedCwd = absolutePath(options.cwd);
|
|
4970
|
+
const normalizedName = normalizeName(options.name);
|
|
4971
|
+
const sessions = await listSessionsForAgent(options.agentCommand);
|
|
4972
|
+
return sessions.find((session) => {
|
|
4973
|
+
if (session.cwd !== normalizedCwd) {
|
|
4974
|
+
return false;
|
|
4975
|
+
}
|
|
4976
|
+
if (!options.includeClosed && session.closed) {
|
|
4977
|
+
return false;
|
|
4978
|
+
}
|
|
4979
|
+
if (normalizedName == null) {
|
|
4980
|
+
return session.name == null;
|
|
4981
|
+
}
|
|
4982
|
+
return session.name === normalizedName;
|
|
4983
|
+
});
|
|
4984
|
+
}
|
|
4985
|
+
async function findSessionByDirectoryWalk(options) {
|
|
4986
|
+
const normalizedName = normalizeName(options.name);
|
|
4987
|
+
const normalizedStart = absolutePath(options.cwd);
|
|
4988
|
+
const normalizedBoundary = absolutePath(options.boundary ?? normalizedStart);
|
|
4989
|
+
const walkBoundary = isWithinBoundary(normalizedBoundary, normalizedStart) ? normalizedBoundary : normalizedStart;
|
|
4990
|
+
const sessions = await listSessionsForAgent(options.agentCommand);
|
|
4991
|
+
const matchesScope = (session, dir2) => {
|
|
4992
|
+
if (session.cwd !== dir2) {
|
|
4993
|
+
return false;
|
|
4994
|
+
}
|
|
4995
|
+
if (session.closed) {
|
|
4996
|
+
return false;
|
|
4997
|
+
}
|
|
4998
|
+
if (normalizedName == null) {
|
|
4999
|
+
return session.name == null;
|
|
5000
|
+
}
|
|
5001
|
+
return session.name === normalizedName;
|
|
5002
|
+
};
|
|
5003
|
+
let dir = normalizedStart;
|
|
5004
|
+
for (; ; ) {
|
|
5005
|
+
const match = sessions.find((session) => matchesScope(session, dir));
|
|
5006
|
+
if (match) {
|
|
5007
|
+
return match;
|
|
5008
|
+
}
|
|
5009
|
+
if (dir === walkBoundary) {
|
|
5010
|
+
return void 0;
|
|
5011
|
+
}
|
|
5012
|
+
const parent = path5.dirname(dir);
|
|
5013
|
+
if (parent === dir) {
|
|
5014
|
+
return void 0;
|
|
5015
|
+
}
|
|
5016
|
+
dir = parent;
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
|
|
5020
|
+
// src/session-owner-spawn.ts
|
|
5021
|
+
var QUEUE_OWNER_STARTUP_TIMEOUT_MS = 1e4;
|
|
5022
|
+
var QUEUE_OWNER_RESPAWN_BACKOFF_MS = 250;
|
|
5023
|
+
function isQueueNotAcceptingError(error) {
|
|
5024
|
+
return error instanceof QueueConnectionError && error.detailCode === "QUEUE_NOT_ACCEPTING_REQUESTS";
|
|
5025
|
+
}
|
|
5026
|
+
function spawnDetachedQueueOwner(ownerSpawn) {
|
|
5027
|
+
const child = spawn3(ownerSpawn.command, ownerSpawn.args, {
|
|
5028
|
+
cwd: ownerSpawn.cwd,
|
|
5029
|
+
env: ownerSpawn.env,
|
|
5030
|
+
stdio: "ignore",
|
|
5031
|
+
detached: true
|
|
5032
|
+
});
|
|
5033
|
+
child.unref();
|
|
5034
|
+
}
|
|
5035
|
+
async function buildDefaultQueueOwnerSpawn(options, queueOwnerTtlMs) {
|
|
5036
|
+
const entrypoint = process.argv[1];
|
|
5037
|
+
if (!entrypoint) {
|
|
5038
|
+
throw new Error("Cannot spawn queue owner process: CLI entrypoint is missing");
|
|
5039
|
+
}
|
|
5040
|
+
const record = await resolveSessionRecord(options.sessionId);
|
|
5041
|
+
const args = [
|
|
5042
|
+
entrypoint,
|
|
5043
|
+
"__queue-owner",
|
|
5044
|
+
"--session-id",
|
|
5045
|
+
options.sessionId,
|
|
5046
|
+
"--ttl-ms",
|
|
5047
|
+
String(queueOwnerTtlMs),
|
|
5048
|
+
"--permission-mode",
|
|
5049
|
+
options.permissionMode
|
|
5050
|
+
];
|
|
5051
|
+
if (options.nonInteractivePermissions) {
|
|
5052
|
+
args.push("--non-interactive-permissions", options.nonInteractivePermissions);
|
|
5053
|
+
}
|
|
5054
|
+
if (options.authPolicy) {
|
|
5055
|
+
args.push("--auth-policy", options.authPolicy);
|
|
5056
|
+
}
|
|
5057
|
+
if (options.timeoutMs != null && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
5058
|
+
args.push("--timeout-ms", String(Math.round(options.timeoutMs)));
|
|
5059
|
+
}
|
|
5060
|
+
if (options.verbose) {
|
|
5061
|
+
args.push("--verbose");
|
|
5062
|
+
}
|
|
5063
|
+
if (options.suppressSdkConsoleErrors) {
|
|
5064
|
+
args.push("--suppress-sdk-console-errors");
|
|
5065
|
+
}
|
|
5066
|
+
return {
|
|
5067
|
+
command: process.execPath,
|
|
5068
|
+
args,
|
|
5069
|
+
cwd: absolutePath(record.cwd)
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
async function sendViaDetachedQueueOwner(options) {
|
|
5073
|
+
const waitForCompletion = options.waitForCompletion !== false;
|
|
5074
|
+
const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
|
|
5075
|
+
const ownerSpawn = options.queueOwnerSpawn ?? await buildDefaultQueueOwnerSpawn(options, queueOwnerTtlMs);
|
|
5076
|
+
const startupDeadline = Date.now() + QUEUE_OWNER_STARTUP_TIMEOUT_MS;
|
|
5077
|
+
let lastSpawnAttemptAt = 0;
|
|
5078
|
+
for (; ; ) {
|
|
5079
|
+
try {
|
|
5080
|
+
const queuedToOwner = await trySubmitToRunningOwner({
|
|
5081
|
+
sessionId: options.sessionId,
|
|
5082
|
+
message: options.message,
|
|
5083
|
+
permissionMode: options.permissionMode,
|
|
5084
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5085
|
+
outputFormatter: options.outputFormatter,
|
|
5086
|
+
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
5087
|
+
timeoutMs: options.timeoutMs,
|
|
5088
|
+
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5089
|
+
waitForCompletion,
|
|
5090
|
+
verbose: options.verbose
|
|
5091
|
+
});
|
|
5092
|
+
if (queuedToOwner) {
|
|
5093
|
+
return queuedToOwner;
|
|
5094
|
+
}
|
|
5095
|
+
} catch (error) {
|
|
5096
|
+
if (!isQueueNotAcceptingError(error)) {
|
|
5097
|
+
throw error;
|
|
5098
|
+
}
|
|
5099
|
+
if (Date.now() >= startupDeadline) {
|
|
5100
|
+
throw new QueueConnectionError(
|
|
5101
|
+
"Timed out waiting for detached queue owner to accept prompt requests",
|
|
5102
|
+
{
|
|
5103
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
5104
|
+
origin: "queue",
|
|
5105
|
+
retryable: true,
|
|
5106
|
+
cause: error instanceof Error ? error : void 0
|
|
5107
|
+
}
|
|
5108
|
+
);
|
|
5109
|
+
}
|
|
5110
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
5111
|
+
continue;
|
|
5112
|
+
}
|
|
5113
|
+
const now = Date.now();
|
|
5114
|
+
if (now >= startupDeadline) {
|
|
5115
|
+
throw new QueueConnectionError(
|
|
5116
|
+
"Timed out waiting for detached queue owner to start",
|
|
5117
|
+
{
|
|
5118
|
+
detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
|
|
5119
|
+
origin: "queue",
|
|
5120
|
+
retryable: true
|
|
5121
|
+
}
|
|
5122
|
+
);
|
|
5123
|
+
}
|
|
5124
|
+
if (now - lastSpawnAttemptAt >= QUEUE_OWNER_RESPAWN_BACKOFF_MS) {
|
|
5125
|
+
spawnDetachedQueueOwner(ownerSpawn);
|
|
5126
|
+
lastSpawnAttemptAt = now;
|
|
5127
|
+
if (options.verbose) {
|
|
5128
|
+
process.stderr.write(
|
|
5129
|
+
`[acpx] starting detached queue owner for session ${options.sessionId}
|
|
5130
|
+
`
|
|
5131
|
+
);
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5134
|
+
await waitMs2(QUEUE_CONNECT_RETRY_MS);
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
|
|
5138
|
+
// src/session-runtime-history.ts
|
|
5139
|
+
var SESSION_HISTORY_MAX_ENTRIES = 500;
|
|
5140
|
+
var SESSION_HISTORY_PREVIEW_CHARS = 220;
|
|
5141
|
+
function collapseWhitespace2(value) {
|
|
5142
|
+
return value.replace(/\s+/g, " ").trim();
|
|
5143
|
+
}
|
|
5144
|
+
function toPreviewText(value) {
|
|
5145
|
+
const collapsed = collapseWhitespace2(value);
|
|
5146
|
+
if (collapsed.length <= SESSION_HISTORY_PREVIEW_CHARS) {
|
|
5147
|
+
return collapsed;
|
|
5148
|
+
}
|
|
5149
|
+
if (SESSION_HISTORY_PREVIEW_CHARS <= 3) {
|
|
5150
|
+
return collapsed.slice(0, SESSION_HISTORY_PREVIEW_CHARS);
|
|
5151
|
+
}
|
|
5152
|
+
return `${collapsed.slice(0, SESSION_HISTORY_PREVIEW_CHARS - 3)}...`;
|
|
5153
|
+
}
|
|
5154
|
+
function textFromContent(content) {
|
|
5155
|
+
if (content.type === "text") {
|
|
5156
|
+
return content.text;
|
|
5157
|
+
}
|
|
5158
|
+
if (content.type === "resource_link") {
|
|
5159
|
+
return content.title ?? content.name ?? content.uri;
|
|
5160
|
+
}
|
|
5161
|
+
if (content.type === "resource") {
|
|
5162
|
+
if ("text" in content.resource && typeof content.resource.text === "string") {
|
|
5163
|
+
return content.resource.text;
|
|
5164
|
+
}
|
|
5165
|
+
return content.resource.uri;
|
|
5166
|
+
}
|
|
5167
|
+
return void 0;
|
|
5168
|
+
}
|
|
5169
|
+
function toHistoryEntryFromUpdate(notification) {
|
|
5170
|
+
const update = notification.update;
|
|
5171
|
+
if (update.sessionUpdate !== "user_message_chunk" && update.sessionUpdate !== "agent_message_chunk") {
|
|
5172
|
+
return void 0;
|
|
5173
|
+
}
|
|
5174
|
+
const text = textFromContent(update.content);
|
|
5175
|
+
if (!text) {
|
|
5176
|
+
return void 0;
|
|
5177
|
+
}
|
|
5178
|
+
const textPreview = toPreviewText(text);
|
|
5179
|
+
if (!textPreview) {
|
|
5180
|
+
return void 0;
|
|
5181
|
+
}
|
|
5182
|
+
return {
|
|
5183
|
+
role: update.sessionUpdate === "user_message_chunk" ? "user" : "assistant",
|
|
5184
|
+
timestamp: isoNow2(),
|
|
5185
|
+
textPreview
|
|
5186
|
+
};
|
|
5187
|
+
}
|
|
5188
|
+
function appendHistoryEntries(current, entries) {
|
|
5189
|
+
const base = current ? [...current] : [];
|
|
4370
5190
|
for (const entry of entries) {
|
|
4371
|
-
if (!entry.
|
|
5191
|
+
if (!entry.textPreview.trim()) {
|
|
4372
5192
|
continue;
|
|
4373
5193
|
}
|
|
4374
|
-
|
|
4375
|
-
try {
|
|
4376
|
-
const payload = await fs4.readFile(fullPath, "utf8");
|
|
4377
|
-
const parsed = parseSessionRecord(JSON.parse(payload));
|
|
4378
|
-
if (parsed) {
|
|
4379
|
-
records.push(parsed);
|
|
4380
|
-
}
|
|
4381
|
-
} catch {
|
|
4382
|
-
}
|
|
5194
|
+
base.push(entry);
|
|
4383
5195
|
}
|
|
4384
|
-
|
|
4385
|
-
|
|
5196
|
+
if (base.length <= SESSION_HISTORY_MAX_ENTRIES) {
|
|
5197
|
+
return base;
|
|
5198
|
+
}
|
|
5199
|
+
return base.slice(base.length - SESSION_HISTORY_MAX_ENTRIES);
|
|
4386
5200
|
}
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
5201
|
+
|
|
5202
|
+
// src/session-runtime-lifecycle.ts
|
|
5203
|
+
function applyLifecycleSnapshotToRecord(record, snapshot) {
|
|
5204
|
+
record.pid = snapshot.pid;
|
|
5205
|
+
record.agentStartedAt = snapshot.startedAt;
|
|
5206
|
+
if (snapshot.lastExit) {
|
|
5207
|
+
record.lastAgentExitCode = snapshot.lastExit.exitCode;
|
|
5208
|
+
record.lastAgentExitSignal = snapshot.lastExit.signal;
|
|
5209
|
+
record.lastAgentExitAt = snapshot.lastExit.exitedAt;
|
|
5210
|
+
record.lastAgentDisconnectReason = snapshot.lastExit.reason;
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
record.lastAgentExitCode = void 0;
|
|
5214
|
+
record.lastAgentExitSignal = void 0;
|
|
5215
|
+
record.lastAgentExitAt = void 0;
|
|
5216
|
+
record.lastAgentDisconnectReason = void 0;
|
|
4390
5217
|
}
|
|
4391
|
-
|
|
4392
|
-
const
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
return false;
|
|
4398
|
-
}
|
|
4399
|
-
if (!options.includeClosed && session.closed) {
|
|
4400
|
-
return false;
|
|
4401
|
-
}
|
|
4402
|
-
if (normalizedName == null) {
|
|
4403
|
-
return session.name == null;
|
|
4404
|
-
}
|
|
4405
|
-
return session.name === normalizedName;
|
|
4406
|
-
});
|
|
5218
|
+
function reconcileAgentSessionId(record, agentSessionId) {
|
|
5219
|
+
const normalized = normalizeAgentSessionId(agentSessionId);
|
|
5220
|
+
if (!normalized) {
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
record.agentSessionId = normalized;
|
|
4407
5224
|
}
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
const
|
|
4412
|
-
const
|
|
4413
|
-
const
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
return false;
|
|
4417
|
-
}
|
|
4418
|
-
if (session.closed) {
|
|
4419
|
-
return false;
|
|
4420
|
-
}
|
|
4421
|
-
if (normalizedName == null) {
|
|
4422
|
-
return session.name == null;
|
|
5225
|
+
|
|
5226
|
+
// src/session-runtime-reconnect.ts
|
|
5227
|
+
function loadSessionCandidates(record) {
|
|
5228
|
+
const candidates = [normalizeAgentSessionId(record.agentSessionId), record.sessionId];
|
|
5229
|
+
const unique = [];
|
|
5230
|
+
for (const candidate of candidates) {
|
|
5231
|
+
if (!candidate || unique.includes(candidate)) {
|
|
5232
|
+
continue;
|
|
4423
5233
|
}
|
|
4424
|
-
|
|
4425
|
-
}
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
5234
|
+
unique.push(candidate);
|
|
5235
|
+
}
|
|
5236
|
+
return unique;
|
|
5237
|
+
}
|
|
5238
|
+
async function connectAndLoadSession(options) {
|
|
5239
|
+
const record = options.record;
|
|
5240
|
+
const client = options.client;
|
|
5241
|
+
const storedProcessAlive = isProcessAlive(record.pid);
|
|
5242
|
+
const shouldReconnect = Boolean(record.pid) && !storedProcessAlive;
|
|
5243
|
+
if (options.verbose) {
|
|
5244
|
+
if (storedProcessAlive) {
|
|
5245
|
+
process.stderr.write(
|
|
5246
|
+
`[acpx] saved session pid ${record.pid} is running; reconnecting with loadSession
|
|
5247
|
+
`
|
|
5248
|
+
);
|
|
5249
|
+
} else if (shouldReconnect) {
|
|
5250
|
+
process.stderr.write(
|
|
5251
|
+
`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session/load
|
|
5252
|
+
`
|
|
5253
|
+
);
|
|
4431
5254
|
}
|
|
4432
|
-
|
|
4433
|
-
|
|
5255
|
+
}
|
|
5256
|
+
await options.withTimeout(client.start(), options.timeoutMs);
|
|
5257
|
+
options.onClientAvailable?.(options.activeController);
|
|
5258
|
+
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
5259
|
+
record.closed = false;
|
|
5260
|
+
record.closedAt = void 0;
|
|
5261
|
+
options.onConnectedRecord?.(record);
|
|
5262
|
+
await writeSessionRecord(record);
|
|
5263
|
+
let resumed = false;
|
|
5264
|
+
let loadError;
|
|
5265
|
+
let sessionId = record.sessionId;
|
|
5266
|
+
if (client.supportsLoadSession()) {
|
|
5267
|
+
const candidates = loadSessionCandidates(record);
|
|
5268
|
+
for (const candidate of candidates) {
|
|
5269
|
+
if (options.verbose && candidates.length > 1) {
|
|
5270
|
+
process.stderr.write(`[acpx] attempting session/load with ${candidate}
|
|
5271
|
+
`);
|
|
5272
|
+
}
|
|
5273
|
+
try {
|
|
5274
|
+
const loadResult = await options.withTimeout(
|
|
5275
|
+
client.loadSessionWithOptions(candidate, record.cwd, {
|
|
5276
|
+
suppressReplayUpdates: true
|
|
5277
|
+
}),
|
|
5278
|
+
options.timeoutMs
|
|
5279
|
+
);
|
|
5280
|
+
reconcileAgentSessionId(record, loadResult.agentSessionId);
|
|
5281
|
+
resumed = true;
|
|
5282
|
+
sessionId = candidate;
|
|
5283
|
+
loadError = void 0;
|
|
5284
|
+
break;
|
|
5285
|
+
} catch (error) {
|
|
5286
|
+
loadError = formatErrorMessage(error);
|
|
5287
|
+
if (!options.shouldFallbackToNewSession(error)) {
|
|
5288
|
+
throw error;
|
|
5289
|
+
}
|
|
5290
|
+
if (options.verbose) {
|
|
5291
|
+
process.stderr.write(
|
|
5292
|
+
`[acpx] session/load failed for ${candidate}: ${loadError}
|
|
5293
|
+
`
|
|
5294
|
+
);
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
4434
5297
|
}
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
5298
|
+
if (!resumed) {
|
|
5299
|
+
const createdSession = await options.withTimeout(
|
|
5300
|
+
client.createSession(record.cwd),
|
|
5301
|
+
options.timeoutMs
|
|
5302
|
+
);
|
|
5303
|
+
sessionId = createdSession.sessionId;
|
|
5304
|
+
record.sessionId = sessionId;
|
|
5305
|
+
reconcileAgentSessionId(record, createdSession.agentSessionId);
|
|
4438
5306
|
}
|
|
4439
|
-
|
|
5307
|
+
} else {
|
|
5308
|
+
const createdSession = await options.withTimeout(
|
|
5309
|
+
client.createSession(record.cwd),
|
|
5310
|
+
options.timeoutMs
|
|
5311
|
+
);
|
|
5312
|
+
sessionId = createdSession.sessionId;
|
|
5313
|
+
record.sessionId = sessionId;
|
|
5314
|
+
reconcileAgentSessionId(record, createdSession.agentSessionId);
|
|
4440
5315
|
}
|
|
5316
|
+
options.onSessionIdResolved?.(sessionId);
|
|
5317
|
+
return {
|
|
5318
|
+
sessionId,
|
|
5319
|
+
agentSessionId: record.agentSessionId,
|
|
5320
|
+
resumed,
|
|
5321
|
+
loadError
|
|
5322
|
+
};
|
|
4441
5323
|
}
|
|
4442
5324
|
|
|
4443
5325
|
// src/session-runtime.ts
|
|
4444
|
-
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4445
5326
|
var INTERRUPT_CANCEL_WAIT_MS = 2500;
|
|
4446
|
-
var SESSION_HISTORY_MAX_ENTRIES = 500;
|
|
4447
|
-
var SESSION_HISTORY_PREVIEW_CHARS = 220;
|
|
4448
5327
|
var TimeoutError = class extends Error {
|
|
4449
5328
|
constructor(timeoutMs) {
|
|
4450
5329
|
super(`Timed out after ${timeoutMs}ms`);
|
|
@@ -4539,203 +5418,44 @@ var QueueTaskOutputFormatter = class {
|
|
|
4539
5418
|
this.send({
|
|
4540
5419
|
type: "done",
|
|
4541
5420
|
requestId: this.requestId,
|
|
4542
|
-
stopReason
|
|
4543
|
-
});
|
|
4544
|
-
}
|
|
4545
|
-
onError(params) {
|
|
4546
|
-
this.send({
|
|
4547
|
-
type: "error",
|
|
4548
|
-
requestId: this.requestId,
|
|
4549
|
-
code: params.code,
|
|
4550
|
-
detailCode: params.detailCode,
|
|
4551
|
-
origin: params.origin,
|
|
4552
|
-
message: params.message,
|
|
4553
|
-
retryable: params.retryable,
|
|
4554
|
-
acp: params.acp
|
|
4555
|
-
});
|
|
4556
|
-
}
|
|
4557
|
-
flush() {
|
|
4558
|
-
}
|
|
4559
|
-
};
|
|
4560
|
-
var DISCARD_OUTPUT_FORMATTER = {
|
|
4561
|
-
setContext() {
|
|
4562
|
-
},
|
|
4563
|
-
onSessionUpdate() {
|
|
4564
|
-
},
|
|
4565
|
-
onClientOperation() {
|
|
4566
|
-
},
|
|
4567
|
-
onDone() {
|
|
4568
|
-
},
|
|
4569
|
-
onError() {
|
|
4570
|
-
},
|
|
4571
|
-
flush() {
|
|
4572
|
-
}
|
|
4573
|
-
};
|
|
4574
|
-
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
4575
|
-
if (ttlMs == null) {
|
|
4576
|
-
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4577
|
-
}
|
|
4578
|
-
if (!Number.isFinite(ttlMs) || ttlMs < 0) {
|
|
4579
|
-
return DEFAULT_QUEUE_OWNER_TTL_MS;
|
|
4580
|
-
}
|
|
4581
|
-
return Math.round(ttlMs);
|
|
4582
|
-
}
|
|
4583
|
-
function collapseWhitespace2(value) {
|
|
4584
|
-
return value.replace(/\s+/g, " ").trim();
|
|
4585
|
-
}
|
|
4586
|
-
function toPreviewText(value) {
|
|
4587
|
-
const collapsed = collapseWhitespace2(value);
|
|
4588
|
-
if (collapsed.length <= SESSION_HISTORY_PREVIEW_CHARS) {
|
|
4589
|
-
return collapsed;
|
|
4590
|
-
}
|
|
4591
|
-
if (SESSION_HISTORY_PREVIEW_CHARS <= 3) {
|
|
4592
|
-
return collapsed.slice(0, SESSION_HISTORY_PREVIEW_CHARS);
|
|
4593
|
-
}
|
|
4594
|
-
return `${collapsed.slice(0, SESSION_HISTORY_PREVIEW_CHARS - 3)}...`;
|
|
4595
|
-
}
|
|
4596
|
-
function textFromContent(content) {
|
|
4597
|
-
if (content.type === "text") {
|
|
4598
|
-
return content.text;
|
|
4599
|
-
}
|
|
4600
|
-
if (content.type === "resource_link") {
|
|
4601
|
-
return content.title ?? content.name ?? content.uri;
|
|
4602
|
-
}
|
|
4603
|
-
if (content.type === "resource") {
|
|
4604
|
-
if ("text" in content.resource && typeof content.resource.text === "string") {
|
|
4605
|
-
return content.resource.text;
|
|
4606
|
-
}
|
|
4607
|
-
return content.resource.uri;
|
|
4608
|
-
}
|
|
4609
|
-
return void 0;
|
|
4610
|
-
}
|
|
4611
|
-
function toHistoryEntryFromUpdate(notification) {
|
|
4612
|
-
const update = notification.update;
|
|
4613
|
-
if (update.sessionUpdate !== "user_message_chunk" && update.sessionUpdate !== "agent_message_chunk") {
|
|
4614
|
-
return void 0;
|
|
4615
|
-
}
|
|
4616
|
-
const text = textFromContent(update.content);
|
|
4617
|
-
if (!text) {
|
|
4618
|
-
return void 0;
|
|
4619
|
-
}
|
|
4620
|
-
const textPreview = toPreviewText(text);
|
|
4621
|
-
if (!textPreview) {
|
|
4622
|
-
return void 0;
|
|
4623
|
-
}
|
|
4624
|
-
return {
|
|
4625
|
-
role: update.sessionUpdate === "user_message_chunk" ? "user" : "assistant",
|
|
4626
|
-
timestamp: isoNow2(),
|
|
4627
|
-
textPreview
|
|
4628
|
-
};
|
|
4629
|
-
}
|
|
4630
|
-
function appendHistoryEntries(current, entries) {
|
|
4631
|
-
const base = current ? [...current] : [];
|
|
4632
|
-
for (const entry of entries) {
|
|
4633
|
-
if (!entry.textPreview.trim()) {
|
|
4634
|
-
continue;
|
|
4635
|
-
}
|
|
4636
|
-
base.push(entry);
|
|
5421
|
+
stopReason
|
|
5422
|
+
});
|
|
4637
5423
|
}
|
|
4638
|
-
|
|
4639
|
-
|
|
5424
|
+
onError(params) {
|
|
5425
|
+
this.send({
|
|
5426
|
+
type: "error",
|
|
5427
|
+
requestId: this.requestId,
|
|
5428
|
+
code: params.code,
|
|
5429
|
+
detailCode: params.detailCode,
|
|
5430
|
+
origin: params.origin,
|
|
5431
|
+
message: params.message,
|
|
5432
|
+
retryable: params.retryable,
|
|
5433
|
+
acp: params.acp
|
|
5434
|
+
});
|
|
4640
5435
|
}
|
|
4641
|
-
|
|
4642
|
-
}
|
|
4643
|
-
function applyLifecycleSnapshotToRecord(record, snapshot) {
|
|
4644
|
-
record.pid = snapshot.pid;
|
|
4645
|
-
record.agentStartedAt = snapshot.startedAt;
|
|
4646
|
-
if (snapshot.lastExit) {
|
|
4647
|
-
record.lastAgentExitCode = snapshot.lastExit.exitCode;
|
|
4648
|
-
record.lastAgentExitSignal = snapshot.lastExit.signal;
|
|
4649
|
-
record.lastAgentExitAt = snapshot.lastExit.exitedAt;
|
|
4650
|
-
record.lastAgentDisconnectReason = snapshot.lastExit.reason;
|
|
4651
|
-
return;
|
|
5436
|
+
flush() {
|
|
4652
5437
|
}
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
5438
|
+
};
|
|
5439
|
+
var DISCARD_OUTPUT_FORMATTER = {
|
|
5440
|
+
setContext() {
|
|
5441
|
+
},
|
|
5442
|
+
onSessionUpdate() {
|
|
5443
|
+
},
|
|
5444
|
+
onClientOperation() {
|
|
5445
|
+
},
|
|
5446
|
+
onDone() {
|
|
5447
|
+
},
|
|
5448
|
+
onError() {
|
|
5449
|
+
},
|
|
5450
|
+
flush() {
|
|
4662
5451
|
}
|
|
4663
|
-
|
|
4664
|
-
}
|
|
5452
|
+
};
|
|
4665
5453
|
function shouldFallbackToNewSession(error) {
|
|
4666
5454
|
if (error instanceof TimeoutError || error instanceof InterruptedError) {
|
|
4667
5455
|
return false;
|
|
4668
5456
|
}
|
|
4669
5457
|
return isAcpResourceNotFoundError(error);
|
|
4670
5458
|
}
|
|
4671
|
-
async function connectAndLoadSession(options) {
|
|
4672
|
-
const record = options.record;
|
|
4673
|
-
const client = options.client;
|
|
4674
|
-
const storedProcessAlive = isProcessAlive(record.pid);
|
|
4675
|
-
const shouldReconnect = Boolean(record.pid) && !storedProcessAlive;
|
|
4676
|
-
if (options.verbose) {
|
|
4677
|
-
if (storedProcessAlive) {
|
|
4678
|
-
process.stderr.write(
|
|
4679
|
-
`[acpx] saved session pid ${record.pid} is running; reconnecting with loadSession
|
|
4680
|
-
`
|
|
4681
|
-
);
|
|
4682
|
-
} else if (shouldReconnect) {
|
|
4683
|
-
process.stderr.write(
|
|
4684
|
-
`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session/load
|
|
4685
|
-
`
|
|
4686
|
-
);
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
await withTimeout(client.start(), options.timeoutMs);
|
|
4690
|
-
options.onClientAvailable?.(options.activeController);
|
|
4691
|
-
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
4692
|
-
record.closed = false;
|
|
4693
|
-
record.closedAt = void 0;
|
|
4694
|
-
options.onConnectedRecord?.(record);
|
|
4695
|
-
await writeSessionRecord(record);
|
|
4696
|
-
let resumed = false;
|
|
4697
|
-
let loadError;
|
|
4698
|
-
let sessionId = record.sessionId;
|
|
4699
|
-
if (client.supportsLoadSession()) {
|
|
4700
|
-
try {
|
|
4701
|
-
const loadResult = await withTimeout(
|
|
4702
|
-
client.loadSessionWithOptions(record.sessionId, record.cwd, {
|
|
4703
|
-
suppressReplayUpdates: true
|
|
4704
|
-
}),
|
|
4705
|
-
options.timeoutMs
|
|
4706
|
-
);
|
|
4707
|
-
reconcileAgentSessionId(record, loadResult.agentSessionId);
|
|
4708
|
-
resumed = true;
|
|
4709
|
-
} catch (error) {
|
|
4710
|
-
loadError = formatErrorMessage(error);
|
|
4711
|
-
if (!shouldFallbackToNewSession(error)) {
|
|
4712
|
-
throw error;
|
|
4713
|
-
}
|
|
4714
|
-
const createdSession = await withTimeout(
|
|
4715
|
-
client.createSession(record.cwd),
|
|
4716
|
-
options.timeoutMs
|
|
4717
|
-
);
|
|
4718
|
-
sessionId = createdSession.sessionId;
|
|
4719
|
-
record.sessionId = sessionId;
|
|
4720
|
-
reconcileAgentSessionId(record, createdSession.agentSessionId);
|
|
4721
|
-
}
|
|
4722
|
-
} else {
|
|
4723
|
-
const createdSession = await withTimeout(
|
|
4724
|
-
client.createSession(record.cwd),
|
|
4725
|
-
options.timeoutMs
|
|
4726
|
-
);
|
|
4727
|
-
sessionId = createdSession.sessionId;
|
|
4728
|
-
record.sessionId = sessionId;
|
|
4729
|
-
reconcileAgentSessionId(record, createdSession.agentSessionId);
|
|
4730
|
-
}
|
|
4731
|
-
options.onSessionIdResolved?.(sessionId);
|
|
4732
|
-
return {
|
|
4733
|
-
sessionId,
|
|
4734
|
-
agentSessionId: record.agentSessionId,
|
|
4735
|
-
resumed,
|
|
4736
|
-
loadError
|
|
4737
|
-
};
|
|
4738
|
-
}
|
|
4739
5459
|
async function runQueuedTask(sessionRecordId, task, options) {
|
|
4740
5460
|
const outputFormatter = task.waitForCompletion ? new QueueTaskOutputFormatter(task) : DISCARD_OUTPUT_FORMATTER;
|
|
4741
5461
|
try {
|
|
@@ -4842,6 +5562,8 @@ async function runSessionPrompt(options) {
|
|
|
4842
5562
|
timeoutMs: options.timeoutMs,
|
|
4843
5563
|
verbose: options.verbose,
|
|
4844
5564
|
activeController,
|
|
5565
|
+
withTimeout,
|
|
5566
|
+
shouldFallbackToNewSession,
|
|
4845
5567
|
onClientAvailable: (controller) => {
|
|
4846
5568
|
options.onClientAvailable?.(controller);
|
|
4847
5569
|
notifiedClientAvailable = true;
|
|
@@ -4976,6 +5698,8 @@ async function withConnectedSession(options) {
|
|
|
4976
5698
|
timeoutMs: options.timeoutMs,
|
|
4977
5699
|
verbose: options.verbose,
|
|
4978
5700
|
activeController,
|
|
5701
|
+
withTimeout,
|
|
5702
|
+
shouldFallbackToNewSession,
|
|
4979
5703
|
onClientAvailable: (controller) => {
|
|
4980
5704
|
options.onClientAvailable?.(controller);
|
|
4981
5705
|
notifiedClientAvailable = true;
|
|
@@ -5191,179 +5915,41 @@ async function ensureSession(options) {
|
|
|
5191
5915
|
created: true
|
|
5192
5916
|
};
|
|
5193
5917
|
}
|
|
5194
|
-
async function
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
outputFormatter: options.outputFormatter,
|
|
5203
|
-
errorEmissionPolicy: options.errorEmissionPolicy,
|
|
5204
|
-
timeoutMs: options.timeoutMs,
|
|
5205
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5206
|
-
waitForCompletion,
|
|
5207
|
-
verbose: options.verbose
|
|
5208
|
-
});
|
|
5209
|
-
if (queuedToOwner) {
|
|
5210
|
-
return queuedToOwner;
|
|
5211
|
-
}
|
|
5212
|
-
for (; ; ) {
|
|
5213
|
-
const lease = await tryAcquireQueueOwnerLease(options.sessionId);
|
|
5214
|
-
if (!lease) {
|
|
5215
|
-
const retryQueued = await trySubmitToRunningOwner({
|
|
5216
|
-
sessionId: options.sessionId,
|
|
5217
|
-
message: options.message,
|
|
5218
|
-
permissionMode: options.permissionMode,
|
|
5918
|
+
async function runQueueOwnerProcess2(options) {
|
|
5919
|
+
await runQueueOwnerProcess(options, {
|
|
5920
|
+
runQueuedTask,
|
|
5921
|
+
withTimeout: async (run, timeoutMs) => await withTimeout(run(), timeoutMs),
|
|
5922
|
+
setSessionModeFallback: async (modeId, timeoutMs) => {
|
|
5923
|
+
await runSessionSetModeDirect({
|
|
5924
|
+
sessionRecordId: options.sessionId,
|
|
5925
|
+
modeId,
|
|
5219
5926
|
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
timeoutMs
|
|
5223
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5224
|
-
waitForCompletion,
|
|
5927
|
+
authCredentials: options.authCredentials,
|
|
5928
|
+
authPolicy: options.authPolicy,
|
|
5929
|
+
timeoutMs,
|
|
5225
5930
|
verbose: options.verbose
|
|
5226
5931
|
});
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
sessionRecordId: options.sessionId,
|
|
5239
|
-
modeId,
|
|
5240
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5241
|
-
authCredentials: options.authCredentials,
|
|
5242
|
-
authPolicy: options.authPolicy,
|
|
5243
|
-
timeoutMs,
|
|
5244
|
-
verbose: options.verbose
|
|
5245
|
-
});
|
|
5246
|
-
},
|
|
5247
|
-
setSessionConfigOptionFallback: async (configId, value, timeoutMs) => {
|
|
5248
|
-
const result = await runSessionSetConfigOptionDirect({
|
|
5249
|
-
sessionRecordId: options.sessionId,
|
|
5250
|
-
configId,
|
|
5251
|
-
value,
|
|
5252
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5253
|
-
authCredentials: options.authCredentials,
|
|
5254
|
-
authPolicy: options.authPolicy,
|
|
5255
|
-
timeoutMs,
|
|
5256
|
-
verbose: options.verbose
|
|
5257
|
-
});
|
|
5258
|
-
return result.response;
|
|
5259
|
-
}
|
|
5260
|
-
});
|
|
5261
|
-
const applyPendingCancel = async () => {
|
|
5262
|
-
return await turnController.applyPendingCancel();
|
|
5263
|
-
};
|
|
5264
|
-
const scheduleApplyPendingCancel = () => {
|
|
5265
|
-
void applyPendingCancel().catch((error) => {
|
|
5266
|
-
if (options.verbose) {
|
|
5267
|
-
process.stderr.write(
|
|
5268
|
-
`[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}
|
|
5269
|
-
`
|
|
5270
|
-
);
|
|
5271
|
-
}
|
|
5272
|
-
});
|
|
5273
|
-
};
|
|
5274
|
-
const setActiveController = (controller) => {
|
|
5275
|
-
turnController.setActiveController(controller);
|
|
5276
|
-
scheduleApplyPendingCancel();
|
|
5277
|
-
};
|
|
5278
|
-
const clearActiveController = () => {
|
|
5279
|
-
turnController.clearActiveController();
|
|
5280
|
-
};
|
|
5281
|
-
const runPromptTurn = async (run) => {
|
|
5282
|
-
turnController.beginTurn();
|
|
5283
|
-
try {
|
|
5284
|
-
return await run();
|
|
5285
|
-
} finally {
|
|
5286
|
-
turnController.endTurn();
|
|
5287
|
-
}
|
|
5288
|
-
};
|
|
5289
|
-
try {
|
|
5290
|
-
owner = await SessionQueueOwner.start(lease, {
|
|
5291
|
-
cancelPrompt: async () => {
|
|
5292
|
-
const accepted = await turnController.requestCancel();
|
|
5293
|
-
if (!accepted) {
|
|
5294
|
-
return false;
|
|
5295
|
-
}
|
|
5296
|
-
await applyPendingCancel();
|
|
5297
|
-
return true;
|
|
5298
|
-
},
|
|
5299
|
-
setSessionMode: async (modeId, timeoutMs) => {
|
|
5300
|
-
await turnController.setSessionMode(modeId, timeoutMs);
|
|
5301
|
-
},
|
|
5302
|
-
setSessionConfigOption: async (configId, value, timeoutMs) => {
|
|
5303
|
-
return await turnController.setSessionConfigOption(
|
|
5304
|
-
configId,
|
|
5305
|
-
value,
|
|
5306
|
-
timeoutMs
|
|
5307
|
-
);
|
|
5308
|
-
}
|
|
5309
|
-
});
|
|
5310
|
-
const localResult = await runPromptTurn(async () => {
|
|
5311
|
-
return await runSessionPrompt({
|
|
5312
|
-
sessionRecordId: options.sessionId,
|
|
5313
|
-
message: options.message,
|
|
5314
|
-
permissionMode: options.permissionMode,
|
|
5315
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5316
|
-
authCredentials: options.authCredentials,
|
|
5317
|
-
authPolicy: options.authPolicy,
|
|
5318
|
-
outputFormatter: options.outputFormatter,
|
|
5319
|
-
timeoutMs: options.timeoutMs,
|
|
5320
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5321
|
-
verbose: options.verbose,
|
|
5322
|
-
onClientAvailable: setActiveController,
|
|
5323
|
-
onClientClosed: clearActiveController,
|
|
5324
|
-
onPromptActive: async () => {
|
|
5325
|
-
turnController.markPromptActive();
|
|
5326
|
-
await applyPendingCancel();
|
|
5327
|
-
}
|
|
5328
|
-
});
|
|
5932
|
+
},
|
|
5933
|
+
setSessionConfigOptionFallback: async (configId, value, timeoutMs) => {
|
|
5934
|
+
const result = await runSessionSetConfigOptionDirect({
|
|
5935
|
+
sessionRecordId: options.sessionId,
|
|
5936
|
+
configId,
|
|
5937
|
+
value,
|
|
5938
|
+
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5939
|
+
authCredentials: options.authCredentials,
|
|
5940
|
+
authPolicy: options.authPolicy,
|
|
5941
|
+
timeoutMs,
|
|
5942
|
+
verbose: options.verbose
|
|
5329
5943
|
});
|
|
5330
|
-
|
|
5331
|
-
while (true) {
|
|
5332
|
-
const task = await owner.nextTask(idleWaitMs);
|
|
5333
|
-
if (!task) {
|
|
5334
|
-
if (queueOwnerTtlMs > 0 && options.verbose) {
|
|
5335
|
-
process.stderr.write(
|
|
5336
|
-
`[acpx] queue owner TTL expired after ${Math.round(queueOwnerTtlMs / 1e3)}s for session ${options.sessionId}; shutting down
|
|
5337
|
-
`
|
|
5338
|
-
);
|
|
5339
|
-
}
|
|
5340
|
-
break;
|
|
5341
|
-
}
|
|
5342
|
-
await runPromptTurn(async () => {
|
|
5343
|
-
await runQueuedTask(options.sessionId, task, {
|
|
5344
|
-
verbose: options.verbose,
|
|
5345
|
-
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
5346
|
-
authCredentials: options.authCredentials,
|
|
5347
|
-
authPolicy: options.authPolicy,
|
|
5348
|
-
suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
|
|
5349
|
-
onClientAvailable: setActiveController,
|
|
5350
|
-
onClientClosed: clearActiveController,
|
|
5351
|
-
onPromptActive: async () => {
|
|
5352
|
-
turnController.markPromptActive();
|
|
5353
|
-
await applyPendingCancel();
|
|
5354
|
-
}
|
|
5355
|
-
});
|
|
5356
|
-
});
|
|
5357
|
-
}
|
|
5358
|
-
return localResult;
|
|
5359
|
-
} finally {
|
|
5360
|
-
turnController.beginClosing();
|
|
5361
|
-
if (owner) {
|
|
5362
|
-
await owner.close();
|
|
5363
|
-
}
|
|
5364
|
-
await releaseQueueOwnerLease(lease);
|
|
5944
|
+
return result.response;
|
|
5365
5945
|
}
|
|
5366
|
-
}
|
|
5946
|
+
});
|
|
5947
|
+
}
|
|
5948
|
+
async function sendSession(options) {
|
|
5949
|
+
return await sendViaDetachedQueueOwner(options);
|
|
5950
|
+
}
|
|
5951
|
+
async function readSessionQueueOwnerStatus(sessionId) {
|
|
5952
|
+
return await readQueueOwnerStatus(sessionId);
|
|
5367
5953
|
}
|
|
5368
5954
|
async function cancelSessionPrompt(options) {
|
|
5369
5955
|
const cancelled2 = await tryCancelOnRunningOwner(options);
|
|
@@ -5469,6 +6055,7 @@ var NoSessionError = class extends Error {
|
|
|
5469
6055
|
}
|
|
5470
6056
|
};
|
|
5471
6057
|
var TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
|
|
6058
|
+
"__queue-owner",
|
|
5472
6059
|
"prompt",
|
|
5473
6060
|
"exec",
|
|
5474
6061
|
"cancel",
|
|
@@ -5481,25 +6068,25 @@ var TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
|
|
|
5481
6068
|
]);
|
|
5482
6069
|
function parseOutputFormat2(value) {
|
|
5483
6070
|
if (!OUTPUT_FORMATS.includes(value)) {
|
|
5484
|
-
throw new
|
|
6071
|
+
throw new InvalidArgumentError3(
|
|
5485
6072
|
`Invalid format "${value}". Expected one of: ${OUTPUT_FORMATS.join(", ")}`
|
|
5486
6073
|
);
|
|
5487
6074
|
}
|
|
5488
6075
|
return value;
|
|
5489
6076
|
}
|
|
5490
|
-
function
|
|
6077
|
+
function parseAuthPolicy3(value) {
|
|
5491
6078
|
if (!AUTH_POLICIES.includes(value)) {
|
|
5492
|
-
throw new
|
|
6079
|
+
throw new InvalidArgumentError3(
|
|
5493
6080
|
`Invalid auth policy "${value}". Expected one of: ${AUTH_POLICIES.join(", ")}`
|
|
5494
6081
|
);
|
|
5495
6082
|
}
|
|
5496
6083
|
return value;
|
|
5497
6084
|
}
|
|
5498
|
-
function
|
|
6085
|
+
function parseNonInteractivePermissionPolicy3(value) {
|
|
5499
6086
|
if (!NON_INTERACTIVE_PERMISSION_POLICIES.includes(
|
|
5500
6087
|
value
|
|
5501
6088
|
)) {
|
|
5502
|
-
throw new
|
|
6089
|
+
throw new InvalidArgumentError3(
|
|
5503
6090
|
`Invalid non-interactive permission policy "${value}". Expected one of: ${NON_INTERACTIVE_PERMISSION_POLICIES.join(", ")}`
|
|
5504
6091
|
);
|
|
5505
6092
|
}
|
|
@@ -5508,35 +6095,35 @@ function parseNonInteractivePermissionPolicy2(value) {
|
|
|
5508
6095
|
function parseTimeoutSeconds(value) {
|
|
5509
6096
|
const parsed = Number(value);
|
|
5510
6097
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
5511
|
-
throw new
|
|
6098
|
+
throw new InvalidArgumentError3("Timeout must be a positive number of seconds");
|
|
5512
6099
|
}
|
|
5513
6100
|
return Math.round(parsed * 1e3);
|
|
5514
6101
|
}
|
|
5515
6102
|
function parseTtlSeconds(value) {
|
|
5516
6103
|
const parsed = Number(value);
|
|
5517
6104
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
5518
|
-
throw new
|
|
6105
|
+
throw new InvalidArgumentError3("TTL must be a non-negative number of seconds");
|
|
5519
6106
|
}
|
|
5520
6107
|
return Math.round(parsed * 1e3);
|
|
5521
6108
|
}
|
|
5522
6109
|
function parseSessionName(value) {
|
|
5523
6110
|
const trimmed = value.trim();
|
|
5524
6111
|
if (trimmed.length === 0) {
|
|
5525
|
-
throw new
|
|
6112
|
+
throw new InvalidArgumentError3("Session name must not be empty");
|
|
5526
6113
|
}
|
|
5527
6114
|
return trimmed;
|
|
5528
6115
|
}
|
|
5529
|
-
function
|
|
6116
|
+
function parseNonEmptyValue2(label, value) {
|
|
5530
6117
|
const trimmed = value.trim();
|
|
5531
6118
|
if (trimmed.length === 0) {
|
|
5532
|
-
throw new
|
|
6119
|
+
throw new InvalidArgumentError3(`${label} must not be empty`);
|
|
5533
6120
|
}
|
|
5534
6121
|
return trimmed;
|
|
5535
6122
|
}
|
|
5536
6123
|
function parseHistoryLimit(value) {
|
|
5537
6124
|
const parsed = Number(value);
|
|
5538
6125
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
5539
|
-
throw new
|
|
6126
|
+
throw new InvalidArgumentError3("Limit must be a positive integer");
|
|
5540
6127
|
}
|
|
5541
6128
|
return parsed;
|
|
5542
6129
|
}
|
|
@@ -5545,7 +6132,7 @@ function resolvePermissionMode(flags, defaultMode) {
|
|
|
5545
6132
|
Boolean
|
|
5546
6133
|
).length;
|
|
5547
6134
|
if (selected2 > 1) {
|
|
5548
|
-
throw new
|
|
6135
|
+
throw new InvalidArgumentError3(
|
|
5549
6136
|
"Use only one permission mode: --approve-all, --approve-reads, or --deny-all"
|
|
5550
6137
|
);
|
|
5551
6138
|
}
|
|
@@ -5572,7 +6159,7 @@ async function readPrompt(promptParts, filePath, cwd) {
|
|
|
5572
6159
|
);
|
|
5573
6160
|
const prompt2 = pieces.join("\n\n").trim();
|
|
5574
6161
|
if (!prompt2) {
|
|
5575
|
-
throw new
|
|
6162
|
+
throw new InvalidArgumentError3("Prompt from --file is empty");
|
|
5576
6163
|
}
|
|
5577
6164
|
return prompt2;
|
|
5578
6165
|
}
|
|
@@ -5581,13 +6168,13 @@ async function readPrompt(promptParts, filePath, cwd) {
|
|
|
5581
6168
|
return joined;
|
|
5582
6169
|
}
|
|
5583
6170
|
if (process.stdin.isTTY) {
|
|
5584
|
-
throw new
|
|
6171
|
+
throw new InvalidArgumentError3(
|
|
5585
6172
|
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
5586
6173
|
);
|
|
5587
6174
|
}
|
|
5588
6175
|
const prompt = (await readPromptInputFromStdin()).trim();
|
|
5589
6176
|
if (!prompt) {
|
|
5590
|
-
throw new
|
|
6177
|
+
throw new InvalidArgumentError3("Prompt from stdin is empty");
|
|
5591
6178
|
}
|
|
5592
6179
|
return prompt;
|
|
5593
6180
|
}
|
|
@@ -5602,14 +6189,14 @@ function addGlobalFlags(command) {
|
|
|
5602
6189
|
return command.option("--agent <command>", "Raw ACP agent command (escape hatch)").option("--cwd <dir>", "Working directory", process.cwd()).option(
|
|
5603
6190
|
"--auth-policy <policy>",
|
|
5604
6191
|
"Authentication policy: skip or fail when auth is required",
|
|
5605
|
-
|
|
6192
|
+
parseAuthPolicy3
|
|
5606
6193
|
).option("--approve-all", "Auto-approve all permission requests").option(
|
|
5607
6194
|
"--approve-reads",
|
|
5608
6195
|
"Auto-approve read/search requests and prompt for writes"
|
|
5609
6196
|
).option("--deny-all", "Deny all permission requests").option(
|
|
5610
6197
|
"--non-interactive-permissions <policy>",
|
|
5611
6198
|
"When prompting is unavailable: deny or fail",
|
|
5612
|
-
|
|
6199
|
+
parseNonInteractivePermissionPolicy3
|
|
5613
6200
|
).option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat2).option(
|
|
5614
6201
|
"--json-strict",
|
|
5615
6202
|
"Strict JSON mode: requires --format json and suppresses non-JSON stderr output"
|
|
@@ -5666,10 +6253,10 @@ function resolveGlobalFlags(command, config) {
|
|
|
5666
6253
|
const jsonStrict = opts.jsonStrict === true;
|
|
5667
6254
|
const verbose = opts.verbose === true;
|
|
5668
6255
|
if (jsonStrict && format !== "json") {
|
|
5669
|
-
throw new
|
|
6256
|
+
throw new InvalidArgumentError3("--json-strict requires --format json");
|
|
5670
6257
|
}
|
|
5671
6258
|
if (jsonStrict && verbose) {
|
|
5672
|
-
throw new
|
|
6259
|
+
throw new InvalidArgumentError3("--json-strict cannot be combined with --verbose");
|
|
5673
6260
|
}
|
|
5674
6261
|
return {
|
|
5675
6262
|
agent: opts.agent,
|
|
@@ -5698,7 +6285,7 @@ function resolveOutputPolicy(format, jsonStrict) {
|
|
|
5698
6285
|
function resolveAgentInvocation(explicitAgentName, globalFlags, config) {
|
|
5699
6286
|
const override = globalFlags.agent?.trim();
|
|
5700
6287
|
if (override && explicitAgentName) {
|
|
5701
|
-
throw new
|
|
6288
|
+
throw new InvalidArgumentError3(
|
|
5702
6289
|
"Do not combine positional agent with --agent override"
|
|
5703
6290
|
);
|
|
5704
6291
|
}
|
|
@@ -6376,7 +6963,12 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6376
6963
|
uptime: null,
|
|
6377
6964
|
lastPromptTime: null,
|
|
6378
6965
|
exitCode: null,
|
|
6379
|
-
signal: null
|
|
6966
|
+
signal: null,
|
|
6967
|
+
ownerPid: null,
|
|
6968
|
+
ownerStatus: null,
|
|
6969
|
+
ownerGeneration: null,
|
|
6970
|
+
ownerHeartbeatAt: null,
|
|
6971
|
+
ownerQueueDepth: null
|
|
6380
6972
|
};
|
|
6381
6973
|
if (globalFlags.format === "json") {
|
|
6382
6974
|
process.stdout.write(`${JSON.stringify(payload2)}
|
|
@@ -6398,10 +6990,14 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6398
6990
|
process.stdout.write(`uptime: -
|
|
6399
6991
|
`);
|
|
6400
6992
|
process.stdout.write(`lastPromptTime: -
|
|
6993
|
+
`);
|
|
6994
|
+
process.stdout.write(`ownerStatus: -
|
|
6401
6995
|
`);
|
|
6402
6996
|
return;
|
|
6403
6997
|
}
|
|
6404
6998
|
const running = isProcessAlive(record.pid);
|
|
6999
|
+
const owner = await readSessionQueueOwnerStatus(record.id);
|
|
7000
|
+
const ownerStatus = owner ? owner.stale ? "stale" : "active" : "inactive";
|
|
6405
7001
|
const payload = {
|
|
6406
7002
|
...canonicalSessionIdentity(record),
|
|
6407
7003
|
agentCommand: record.agentCommand,
|
|
@@ -6410,7 +7006,12 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6410
7006
|
uptime: running ? formatUptime(record.agentStartedAt) ?? null : null,
|
|
6411
7007
|
lastPromptTime: record.lastPromptAt ?? null,
|
|
6412
7008
|
exitCode: running ? null : record.lastAgentExitCode ?? null,
|
|
6413
|
-
signal: running ? null : record.lastAgentExitSignal ?? null
|
|
7009
|
+
signal: running ? null : record.lastAgentExitSignal ?? null,
|
|
7010
|
+
ownerPid: owner?.pid ?? null,
|
|
7011
|
+
ownerStatus,
|
|
7012
|
+
ownerGeneration: owner?.ownerGeneration ?? null,
|
|
7013
|
+
ownerHeartbeatAt: owner?.heartbeatAt ?? null,
|
|
7014
|
+
ownerQueueDepth: owner?.queueDepth ?? null
|
|
6414
7015
|
};
|
|
6415
7016
|
if (globalFlags.format === "json") {
|
|
6416
7017
|
process.stdout.write(`${JSON.stringify(payload)}
|
|
@@ -6437,6 +7038,16 @@ async function handleStatus(explicitAgentName, flags, command, config) {
|
|
|
6437
7038
|
process.stdout.write(`uptime: ${payload.uptime ?? "-"}
|
|
6438
7039
|
`);
|
|
6439
7040
|
process.stdout.write(`lastPromptTime: ${payload.lastPromptTime ?? "-"}
|
|
7041
|
+
`);
|
|
7042
|
+
process.stdout.write(`ownerStatus: ${payload.ownerStatus}
|
|
7043
|
+
`);
|
|
7044
|
+
process.stdout.write(`ownerPid: ${payload.ownerPid ?? "-"}
|
|
7045
|
+
`);
|
|
7046
|
+
process.stdout.write(`ownerGeneration: ${payload.ownerGeneration ?? "-"}
|
|
7047
|
+
`);
|
|
7048
|
+
process.stdout.write(`ownerHeartbeatAt: ${payload.ownerHeartbeatAt ?? "-"}
|
|
7049
|
+
`);
|
|
7050
|
+
process.stdout.write(`ownerQueueDepth: ${payload.ownerQueueDepth ?? "-"}
|
|
6440
7051
|
`);
|
|
6441
7052
|
if (payload.status === "dead") {
|
|
6442
7053
|
process.stdout.write(`exitCode: ${payload.exitCode ?? "-"}
|
|
@@ -6541,7 +7152,7 @@ function registerSharedAgentSubcommands(parent, explicitAgentName, config, descr
|
|
|
6541
7152
|
const setModeCommand = parent.command("set-mode").description(descriptions.setMode).argument(
|
|
6542
7153
|
"<mode>",
|
|
6543
7154
|
"Mode id",
|
|
6544
|
-
(value) =>
|
|
7155
|
+
(value) => parseNonEmptyValue2("Mode", value)
|
|
6545
7156
|
);
|
|
6546
7157
|
addSessionNameOption(setModeCommand);
|
|
6547
7158
|
setModeCommand.action(async function(modeId, flags) {
|
|
@@ -6550,11 +7161,11 @@ function registerSharedAgentSubcommands(parent, explicitAgentName, config, descr
|
|
|
6550
7161
|
const setConfigCommand = parent.command("set").description(descriptions.setConfig).argument(
|
|
6551
7162
|
"<key>",
|
|
6552
7163
|
"Config option id",
|
|
6553
|
-
(value) =>
|
|
7164
|
+
(value) => parseNonEmptyValue2("Config option key", value)
|
|
6554
7165
|
).argument(
|
|
6555
7166
|
"<value>",
|
|
6556
7167
|
"Config option value",
|
|
6557
|
-
(value) =>
|
|
7168
|
+
(value) => parseNonEmptyValue2("Config option value", value)
|
|
6558
7169
|
);
|
|
6559
7170
|
addSessionNameOption(setConfigCommand);
|
|
6560
7171
|
setConfigCommand.action(async function(key, value, flags) {
|
|
@@ -6749,7 +7360,10 @@ async function main(argv = process.argv) {
|
|
|
6749
7360
|
requestedOutputFormat,
|
|
6750
7361
|
requestedJsonStrict
|
|
6751
7362
|
);
|
|
6752
|
-
const
|
|
7363
|
+
const internalQueueOwnerFlags = parseQueueOwnerFlags(
|
|
7364
|
+
argv.slice(2),
|
|
7365
|
+
DEFAULT_QUEUE_OWNER_TTL_MS
|
|
7366
|
+
);
|
|
6753
7367
|
const program = new Command();
|
|
6754
7368
|
program.name("acpx").description("Headless CLI client for the Agent Client Protocol").enablePositionalOptions().showHelpAfterError();
|
|
6755
7369
|
if (requestedJsonStrict) {
|
|
@@ -6761,56 +7375,39 @@ async function main(argv = process.argv) {
|
|
|
6761
7375
|
});
|
|
6762
7376
|
}
|
|
6763
7377
|
addGlobalFlags(program);
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
"Prompt is required (pass as argument, --file, or pipe via stdin)"
|
|
6777
|
-
);
|
|
6778
|
-
}
|
|
6779
|
-
this.outputHelp();
|
|
6780
|
-
return;
|
|
7378
|
+
configurePublicCli({
|
|
7379
|
+
program,
|
|
7380
|
+
argv: argv.slice(2),
|
|
7381
|
+
config,
|
|
7382
|
+
requestedJsonStrict,
|
|
7383
|
+
topLevelVerbs: TOP_LEVEL_VERBS,
|
|
7384
|
+
listBuiltInAgents,
|
|
7385
|
+
detectAgentToken,
|
|
7386
|
+
registerAgentCommand,
|
|
7387
|
+
registerDefaultCommands,
|
|
7388
|
+
handlePromptAction: async (command, promptParts) => {
|
|
7389
|
+
await handlePrompt(void 0, promptParts, {}, command, config);
|
|
6781
7390
|
}
|
|
6782
|
-
await handlePrompt(void 0, promptParts, {}, this, config);
|
|
6783
7391
|
});
|
|
6784
|
-
program.addHelpText(
|
|
6785
|
-
"after",
|
|
6786
|
-
`
|
|
6787
|
-
Examples:
|
|
6788
|
-
acpx codex sessions new
|
|
6789
|
-
acpx codex "fix the tests"
|
|
6790
|
-
acpx codex prompt "fix the tests"
|
|
6791
|
-
acpx codex --no-wait "queue follow-up task"
|
|
6792
|
-
acpx codex exec "what does this repo do"
|
|
6793
|
-
acpx codex cancel
|
|
6794
|
-
acpx codex set-mode plan
|
|
6795
|
-
acpx codex set approval_policy conservative
|
|
6796
|
-
acpx codex -s backend "fix the API"
|
|
6797
|
-
acpx codex sessions
|
|
6798
|
-
acpx codex sessions new --name backend
|
|
6799
|
-
acpx codex sessions ensure --name backend
|
|
6800
|
-
acpx codex sessions close backend
|
|
6801
|
-
acpx codex status
|
|
6802
|
-
acpx config show
|
|
6803
|
-
acpx config init
|
|
6804
|
-
acpx --ttl 30 codex "investigate flaky tests"
|
|
6805
|
-
acpx claude "refactor auth"
|
|
6806
|
-
acpx gemini "add logging"
|
|
6807
|
-
acpx --agent ./my-custom-server "do something"`
|
|
6808
|
-
);
|
|
6809
7392
|
program.exitOverride((error) => {
|
|
6810
7393
|
throw error;
|
|
6811
7394
|
});
|
|
6812
7395
|
await runWithOutputPolicy(requestedOutputPolicy, async () => {
|
|
6813
7396
|
try {
|
|
7397
|
+
if (internalQueueOwnerFlags) {
|
|
7398
|
+
await runQueueOwnerProcess2({
|
|
7399
|
+
sessionId: internalQueueOwnerFlags.sessionId,
|
|
7400
|
+
ttlMs: internalQueueOwnerFlags.ttlMs,
|
|
7401
|
+
permissionMode: internalQueueOwnerFlags.permissionMode,
|
|
7402
|
+
nonInteractivePermissions: internalQueueOwnerFlags.nonInteractivePermissions,
|
|
7403
|
+
authCredentials: config.auth,
|
|
7404
|
+
authPolicy: internalQueueOwnerFlags.authPolicy,
|
|
7405
|
+
timeoutMs: internalQueueOwnerFlags.timeoutMs,
|
|
7406
|
+
suppressSdkConsoleErrors: internalQueueOwnerFlags.suppressSdkConsoleErrors,
|
|
7407
|
+
verbose: internalQueueOwnerFlags.verbose
|
|
7408
|
+
});
|
|
7409
|
+
return;
|
|
7410
|
+
}
|
|
6814
7411
|
await program.parseAsync(argv);
|
|
6815
7412
|
} catch (error) {
|
|
6816
7413
|
if (error instanceof CommanderError) {
|