acpx 0.1.9 → 0.1.10
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 +397 -212
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ 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
16
|
opencode: "npx opencode-ai",
|
|
17
17
|
pi: "npx pi-acp"
|
|
@@ -388,6 +388,117 @@ var PermissionPromptUnavailableError = class extends AcpxOperationalError {
|
|
|
388
388
|
}
|
|
389
389
|
};
|
|
390
390
|
|
|
391
|
+
// src/acp-error-shapes.ts
|
|
392
|
+
var RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
|
|
393
|
+
function asRecord(value) {
|
|
394
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
399
|
+
function toAcpErrorPayload(value) {
|
|
400
|
+
const record = asRecord(value);
|
|
401
|
+
if (!record) {
|
|
402
|
+
return void 0;
|
|
403
|
+
}
|
|
404
|
+
if (typeof record.code !== "number" || !Number.isFinite(record.code)) {
|
|
405
|
+
return void 0;
|
|
406
|
+
}
|
|
407
|
+
if (typeof record.message !== "string" || record.message.length === 0) {
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
code: record.code,
|
|
412
|
+
message: record.message,
|
|
413
|
+
data: record.data
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function extractAcpErrorInternal(value, depth) {
|
|
417
|
+
if (depth > 5) {
|
|
418
|
+
return void 0;
|
|
419
|
+
}
|
|
420
|
+
const direct = toAcpErrorPayload(value);
|
|
421
|
+
if (direct) {
|
|
422
|
+
return direct;
|
|
423
|
+
}
|
|
424
|
+
const record = asRecord(value);
|
|
425
|
+
if (!record) {
|
|
426
|
+
return void 0;
|
|
427
|
+
}
|
|
428
|
+
if ("error" in record) {
|
|
429
|
+
const nested = extractAcpErrorInternal(record.error, depth + 1);
|
|
430
|
+
if (nested) {
|
|
431
|
+
return nested;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if ("cause" in record) {
|
|
435
|
+
const nested = extractAcpErrorInternal(record.cause, depth + 1);
|
|
436
|
+
if (nested) {
|
|
437
|
+
return nested;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return void 0;
|
|
441
|
+
}
|
|
442
|
+
function formatUnknownErrorMessage(error) {
|
|
443
|
+
if (error instanceof Error) {
|
|
444
|
+
return error.message;
|
|
445
|
+
}
|
|
446
|
+
if (error && typeof error === "object") {
|
|
447
|
+
const maybeMessage = error.message;
|
|
448
|
+
if (typeof maybeMessage === "string" && maybeMessage.length > 0) {
|
|
449
|
+
return maybeMessage;
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
return JSON.stringify(error);
|
|
453
|
+
} catch {
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return String(error);
|
|
457
|
+
}
|
|
458
|
+
function isSessionNotFoundText(value) {
|
|
459
|
+
if (typeof value !== "string") {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
const normalized = value.toLowerCase();
|
|
463
|
+
return normalized.includes("resource_not_found") || normalized.includes("resource not found") || normalized.includes("session not found") || normalized.includes("unknown session");
|
|
464
|
+
}
|
|
465
|
+
function hasSessionNotFoundHint(value, depth = 0) {
|
|
466
|
+
if (depth > 4) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if (isSessionNotFoundText(value)) {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
if (Array.isArray(value)) {
|
|
473
|
+
return value.some((entry) => hasSessionNotFoundHint(entry, depth + 1));
|
|
474
|
+
}
|
|
475
|
+
const record = asRecord(value);
|
|
476
|
+
if (!record) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
return Object.values(record).some(
|
|
480
|
+
(entry) => hasSessionNotFoundHint(entry, depth + 1)
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
function extractAcpError(error) {
|
|
484
|
+
return extractAcpErrorInternal(error, 0);
|
|
485
|
+
}
|
|
486
|
+
function isAcpResourceNotFoundError(error) {
|
|
487
|
+
const acp = extractAcpError(error);
|
|
488
|
+
if (acp && RESOURCE_NOT_FOUND_ACP_CODES.has(acp.code)) {
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
if (acp) {
|
|
492
|
+
if (isSessionNotFoundText(acp.message)) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
if (hasSessionNotFoundHint(acp.data)) {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return isSessionNotFoundText(formatUnknownErrorMessage(error));
|
|
500
|
+
}
|
|
501
|
+
|
|
391
502
|
// src/types.ts
|
|
392
503
|
var EXIT_CODES = {
|
|
393
504
|
SUCCESS: 0,
|
|
@@ -412,9 +523,8 @@ var OUTPUT_ERROR_CODES = [
|
|
|
412
523
|
var OUTPUT_ERROR_ORIGINS = ["cli", "runtime", "queue", "acp"];
|
|
413
524
|
|
|
414
525
|
// src/error-normalization.ts
|
|
415
|
-
var RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
|
|
416
526
|
var AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
|
|
417
|
-
function
|
|
527
|
+
function asRecord2(value) {
|
|
418
528
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
419
529
|
return void 0;
|
|
420
530
|
}
|
|
@@ -437,7 +547,7 @@ function isAcpAuthRequiredPayload(acp) {
|
|
|
437
547
|
if (isAuthRequiredMessage(acp.message)) {
|
|
438
548
|
return true;
|
|
439
549
|
}
|
|
440
|
-
const data =
|
|
550
|
+
const data = asRecord2(acp.data);
|
|
441
551
|
if (!data) {
|
|
442
552
|
return false;
|
|
443
553
|
}
|
|
@@ -461,7 +571,7 @@ function isOutputErrorOrigin(value) {
|
|
|
461
571
|
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
462
572
|
}
|
|
463
573
|
function readOutputErrorMeta(error) {
|
|
464
|
-
const record =
|
|
574
|
+
const record = asRecord2(error);
|
|
465
575
|
if (!record) {
|
|
466
576
|
return {};
|
|
467
577
|
}
|
|
@@ -469,7 +579,7 @@ function readOutputErrorMeta(error) {
|
|
|
469
579
|
const detailCode = typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0;
|
|
470
580
|
const origin = isOutputErrorOrigin(record.origin) ? record.origin : void 0;
|
|
471
581
|
const retryable = typeof record.retryable === "boolean" ? record.retryable : void 0;
|
|
472
|
-
const acp =
|
|
582
|
+
const acp = toAcpErrorPayload2(record.acp);
|
|
473
583
|
return {
|
|
474
584
|
outputCode,
|
|
475
585
|
detailCode,
|
|
@@ -478,8 +588,8 @@ function readOutputErrorMeta(error) {
|
|
|
478
588
|
acp
|
|
479
589
|
};
|
|
480
590
|
}
|
|
481
|
-
function
|
|
482
|
-
const record =
|
|
591
|
+
function toAcpErrorPayload2(value) {
|
|
592
|
+
const record = asRecord2(value);
|
|
483
593
|
if (!record) {
|
|
484
594
|
return void 0;
|
|
485
595
|
}
|
|
@@ -495,32 +605,6 @@ function toAcpErrorPayload(value) {
|
|
|
495
605
|
data: record.data
|
|
496
606
|
};
|
|
497
607
|
}
|
|
498
|
-
function extractAcpErrorInternal(value, depth) {
|
|
499
|
-
if (depth > 5) {
|
|
500
|
-
return void 0;
|
|
501
|
-
}
|
|
502
|
-
const direct = toAcpErrorPayload(value);
|
|
503
|
-
if (direct) {
|
|
504
|
-
return direct;
|
|
505
|
-
}
|
|
506
|
-
const record = asRecord(value);
|
|
507
|
-
if (!record) {
|
|
508
|
-
return void 0;
|
|
509
|
-
}
|
|
510
|
-
if ("error" in record) {
|
|
511
|
-
const nested = extractAcpErrorInternal(record.error, depth + 1);
|
|
512
|
-
if (nested) {
|
|
513
|
-
return nested;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
if ("cause" in record) {
|
|
517
|
-
const nested = extractAcpErrorInternal(record.cause, depth + 1);
|
|
518
|
-
if (nested) {
|
|
519
|
-
return nested;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
return void 0;
|
|
523
|
-
}
|
|
524
608
|
function isTimeoutLike(error) {
|
|
525
609
|
return error instanceof Error && error.name === "TimeoutError";
|
|
526
610
|
}
|
|
@@ -531,7 +615,7 @@ function isUsageLike(error) {
|
|
|
531
615
|
if (!(error instanceof Error)) {
|
|
532
616
|
return false;
|
|
533
617
|
}
|
|
534
|
-
return error.name === "CommanderError" || error.name === "InvalidArgumentError" ||
|
|
618
|
+
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord2(error)?.code === "commander.invalidArgument";
|
|
535
619
|
}
|
|
536
620
|
function formatErrorMessage(error) {
|
|
537
621
|
if (error instanceof Error) {
|
|
@@ -549,17 +633,6 @@ function formatErrorMessage(error) {
|
|
|
549
633
|
}
|
|
550
634
|
return String(error);
|
|
551
635
|
}
|
|
552
|
-
function extractAcpError(error) {
|
|
553
|
-
return extractAcpErrorInternal(error, 0);
|
|
554
|
-
}
|
|
555
|
-
function isAcpResourceNotFoundError(error) {
|
|
556
|
-
const acp = extractAcpError(error);
|
|
557
|
-
if (acp && RESOURCE_NOT_FOUND_ACP_CODES.has(acp.code)) {
|
|
558
|
-
return true;
|
|
559
|
-
}
|
|
560
|
-
const message = formatErrorMessage(error).toLowerCase();
|
|
561
|
-
return message.includes("resource_not_found") || message.includes("resource not found") || message.includes("session not found") || message.includes("unknown session");
|
|
562
|
-
}
|
|
563
636
|
function mapErrorCode(error) {
|
|
564
637
|
if (error instanceof PermissionPromptUnavailableError) {
|
|
565
638
|
return "PERMISSION_PROMPT_UNAVAILABLE";
|
|
@@ -658,7 +731,7 @@ function toStatusLabel(status) {
|
|
|
658
731
|
return "running";
|
|
659
732
|
}
|
|
660
733
|
}
|
|
661
|
-
function
|
|
734
|
+
function asRecord3(value) {
|
|
662
735
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
663
736
|
return void 0;
|
|
664
737
|
}
|
|
@@ -752,7 +825,7 @@ function summarizeToolInput(rawInput) {
|
|
|
752
825
|
if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
|
|
753
826
|
return toInline(String(rawInput));
|
|
754
827
|
}
|
|
755
|
-
const record =
|
|
828
|
+
const record = asRecord3(rawInput);
|
|
756
829
|
if (record) {
|
|
757
830
|
const command = readFirstString(record, ["command", "cmd", "program"]);
|
|
758
831
|
const args = readFirstStringArray(record, ["args", "arguments"]);
|
|
@@ -886,7 +959,7 @@ function extractOutputText(value, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
|
886
959
|
}
|
|
887
960
|
return dedupeStrings(parts).join("\n");
|
|
888
961
|
}
|
|
889
|
-
const record =
|
|
962
|
+
const record = asRecord3(value);
|
|
890
963
|
if (!record) {
|
|
891
964
|
return void 0;
|
|
892
965
|
}
|
|
@@ -2086,6 +2159,9 @@ var TerminalManager = class {
|
|
|
2086
2159
|
var REPLAY_IDLE_MS = 80;
|
|
2087
2160
|
var REPLAY_DRAIN_TIMEOUT_MS = 5e3;
|
|
2088
2161
|
var DRAIN_POLL_INTERVAL_MS = 20;
|
|
2162
|
+
var AGENT_CLOSE_AFTER_STDIN_END_MS = 100;
|
|
2163
|
+
var AGENT_CLOSE_TERM_GRACE_MS = 1500;
|
|
2164
|
+
var AGENT_CLOSE_KILL_GRACE_MS = 1e3;
|
|
2089
2165
|
function shouldSuppressSdkConsoleError(args) {
|
|
2090
2166
|
if (args.length === 0) {
|
|
2091
2167
|
return false;
|
|
@@ -2121,6 +2197,38 @@ function waitForSpawn2(child) {
|
|
|
2121
2197
|
child.once("error", onError);
|
|
2122
2198
|
});
|
|
2123
2199
|
}
|
|
2200
|
+
function isChildProcessRunning(child) {
|
|
2201
|
+
return child.exitCode == null && child.signalCode == null;
|
|
2202
|
+
}
|
|
2203
|
+
function waitForChildExit(child, timeoutMs) {
|
|
2204
|
+
if (!isChildProcessRunning(child)) {
|
|
2205
|
+
return Promise.resolve(true);
|
|
2206
|
+
}
|
|
2207
|
+
return new Promise((resolve) => {
|
|
2208
|
+
let settled = false;
|
|
2209
|
+
const timer = setTimeout(
|
|
2210
|
+
() => {
|
|
2211
|
+
finish(false);
|
|
2212
|
+
},
|
|
2213
|
+
Math.max(0, timeoutMs)
|
|
2214
|
+
);
|
|
2215
|
+
const finish = (value) => {
|
|
2216
|
+
if (settled) {
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
settled = true;
|
|
2220
|
+
child.off("close", onExitLike);
|
|
2221
|
+
child.off("exit", onExitLike);
|
|
2222
|
+
clearTimeout(timer);
|
|
2223
|
+
resolve(value);
|
|
2224
|
+
};
|
|
2225
|
+
const onExitLike = () => {
|
|
2226
|
+
finish(true);
|
|
2227
|
+
};
|
|
2228
|
+
child.once("close", onExitLike);
|
|
2229
|
+
child.once("exit", onExitLike);
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2124
2232
|
function splitCommandLine(value) {
|
|
2125
2233
|
const parts = [];
|
|
2126
2234
|
let current = "";
|
|
@@ -2542,8 +2650,8 @@ var AcpClient = class {
|
|
|
2542
2650
|
async close() {
|
|
2543
2651
|
this.closing = true;
|
|
2544
2652
|
await this.terminalManager.shutdown();
|
|
2545
|
-
if (this.agent
|
|
2546
|
-
this.agent
|
|
2653
|
+
if (this.agent) {
|
|
2654
|
+
await this.terminateAgentProcess(this.agent);
|
|
2547
2655
|
}
|
|
2548
2656
|
this.sessionUpdateChain = Promise.resolve();
|
|
2549
2657
|
this.observedSessionUpdates = 0;
|
|
@@ -2555,6 +2663,44 @@ var AcpClient = class {
|
|
|
2555
2663
|
this.connection = void 0;
|
|
2556
2664
|
this.agent = void 0;
|
|
2557
2665
|
}
|
|
2666
|
+
async terminateAgentProcess(child) {
|
|
2667
|
+
if (!child.stdin.destroyed) {
|
|
2668
|
+
try {
|
|
2669
|
+
child.stdin.end();
|
|
2670
|
+
} catch {
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
let exited = await waitForChildExit(child, AGENT_CLOSE_AFTER_STDIN_END_MS);
|
|
2674
|
+
if (!exited && isChildProcessRunning(child)) {
|
|
2675
|
+
try {
|
|
2676
|
+
child.kill("SIGTERM");
|
|
2677
|
+
} catch {
|
|
2678
|
+
}
|
|
2679
|
+
exited = await waitForChildExit(child, AGENT_CLOSE_TERM_GRACE_MS);
|
|
2680
|
+
}
|
|
2681
|
+
if (!exited && isChildProcessRunning(child)) {
|
|
2682
|
+
this.log(
|
|
2683
|
+
`agent did not exit after ${AGENT_CLOSE_TERM_GRACE_MS}ms; forcing SIGKILL`
|
|
2684
|
+
);
|
|
2685
|
+
try {
|
|
2686
|
+
child.kill("SIGKILL");
|
|
2687
|
+
} catch {
|
|
2688
|
+
}
|
|
2689
|
+
exited = await waitForChildExit(child, AGENT_CLOSE_KILL_GRACE_MS);
|
|
2690
|
+
}
|
|
2691
|
+
if (!child.stdin.destroyed) {
|
|
2692
|
+
child.stdin.destroy();
|
|
2693
|
+
}
|
|
2694
|
+
if (!child.stdout.destroyed) {
|
|
2695
|
+
child.stdout.destroy();
|
|
2696
|
+
}
|
|
2697
|
+
if (!child.stderr.destroyed) {
|
|
2698
|
+
child.stderr.destroy();
|
|
2699
|
+
}
|
|
2700
|
+
if (!exited) {
|
|
2701
|
+
child.unref();
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2558
2704
|
getConnection() {
|
|
2559
2705
|
if (!this.connection) {
|
|
2560
2706
|
throw new Error("ACP client not started");
|
|
@@ -2879,7 +3025,7 @@ import os2 from "os";
|
|
|
2879
3025
|
import path4 from "path";
|
|
2880
3026
|
|
|
2881
3027
|
// src/queue-messages.ts
|
|
2882
|
-
function
|
|
3028
|
+
function asRecord4(value) {
|
|
2883
3029
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2884
3030
|
return void 0;
|
|
2885
3031
|
}
|
|
@@ -2898,7 +3044,7 @@ function isOutputErrorOrigin2(value) {
|
|
|
2898
3044
|
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
2899
3045
|
}
|
|
2900
3046
|
function parseAcpError(value) {
|
|
2901
|
-
const record =
|
|
3047
|
+
const record = asRecord4(value);
|
|
2902
3048
|
if (!record) {
|
|
2903
3049
|
return void 0;
|
|
2904
3050
|
}
|
|
@@ -2915,7 +3061,7 @@ function parseAcpError(value) {
|
|
|
2915
3061
|
};
|
|
2916
3062
|
}
|
|
2917
3063
|
function parseQueueRequest(raw) {
|
|
2918
|
-
const request =
|
|
3064
|
+
const request = asRecord4(raw);
|
|
2919
3065
|
if (!request) {
|
|
2920
3066
|
return null;
|
|
2921
3067
|
}
|
|
@@ -2973,15 +3119,15 @@ function parseQueueRequest(raw) {
|
|
|
2973
3119
|
return null;
|
|
2974
3120
|
}
|
|
2975
3121
|
function parseSessionSendResult(raw) {
|
|
2976
|
-
const result =
|
|
3122
|
+
const result = asRecord4(raw);
|
|
2977
3123
|
if (!result) {
|
|
2978
3124
|
return null;
|
|
2979
3125
|
}
|
|
2980
3126
|
if (typeof result.stopReason !== "string" || typeof result.sessionId !== "string" || typeof result.resumed !== "boolean") {
|
|
2981
3127
|
return null;
|
|
2982
3128
|
}
|
|
2983
|
-
const permissionStats =
|
|
2984
|
-
const record =
|
|
3129
|
+
const permissionStats = asRecord4(result.permissionStats);
|
|
3130
|
+
const record = asRecord4(result.record);
|
|
2985
3131
|
if (!permissionStats || !record) {
|
|
2986
3132
|
return null;
|
|
2987
3133
|
}
|
|
@@ -2996,7 +3142,7 @@ function parseSessionSendResult(raw) {
|
|
|
2996
3142
|
return result;
|
|
2997
3143
|
}
|
|
2998
3144
|
function parseQueueOwnerMessage(raw) {
|
|
2999
|
-
const message =
|
|
3145
|
+
const message = asRecord4(raw);
|
|
3000
3146
|
if (!message || typeof message.type !== "string") {
|
|
3001
3147
|
return null;
|
|
3002
3148
|
}
|
|
@@ -3021,7 +3167,7 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3021
3167
|
};
|
|
3022
3168
|
}
|
|
3023
3169
|
if (message.type === "client_operation") {
|
|
3024
|
-
const operation =
|
|
3170
|
+
const operation = asRecord4(message.operation);
|
|
3025
3171
|
if (!operation || typeof operation.method !== "string" || typeof operation.status !== "string" || typeof operation.summary !== "string" || typeof operation.timestamp !== "string") {
|
|
3026
3172
|
return null;
|
|
3027
3173
|
}
|
|
@@ -3076,7 +3222,7 @@ function parseQueueOwnerMessage(raw) {
|
|
|
3076
3222
|
};
|
|
3077
3223
|
}
|
|
3078
3224
|
if (message.type === "set_config_option_result") {
|
|
3079
|
-
const response =
|
|
3225
|
+
const response = asRecord4(message.response);
|
|
3080
3226
|
if (!response || !Array.isArray(response.configOptions)) {
|
|
3081
3227
|
return null;
|
|
3082
3228
|
}
|
|
@@ -4440,11 +4586,196 @@ async function findSessionByDirectoryWalk(options) {
|
|
|
4440
4586
|
}
|
|
4441
4587
|
}
|
|
4442
4588
|
|
|
4589
|
+
// src/session-runtime-history.ts
|
|
4590
|
+
var SESSION_HISTORY_MAX_ENTRIES = 500;
|
|
4591
|
+
var SESSION_HISTORY_PREVIEW_CHARS = 220;
|
|
4592
|
+
function collapseWhitespace2(value) {
|
|
4593
|
+
return value.replace(/\s+/g, " ").trim();
|
|
4594
|
+
}
|
|
4595
|
+
function toPreviewText(value) {
|
|
4596
|
+
const collapsed = collapseWhitespace2(value);
|
|
4597
|
+
if (collapsed.length <= SESSION_HISTORY_PREVIEW_CHARS) {
|
|
4598
|
+
return collapsed;
|
|
4599
|
+
}
|
|
4600
|
+
if (SESSION_HISTORY_PREVIEW_CHARS <= 3) {
|
|
4601
|
+
return collapsed.slice(0, SESSION_HISTORY_PREVIEW_CHARS);
|
|
4602
|
+
}
|
|
4603
|
+
return `${collapsed.slice(0, SESSION_HISTORY_PREVIEW_CHARS - 3)}...`;
|
|
4604
|
+
}
|
|
4605
|
+
function textFromContent(content) {
|
|
4606
|
+
if (content.type === "text") {
|
|
4607
|
+
return content.text;
|
|
4608
|
+
}
|
|
4609
|
+
if (content.type === "resource_link") {
|
|
4610
|
+
return content.title ?? content.name ?? content.uri;
|
|
4611
|
+
}
|
|
4612
|
+
if (content.type === "resource") {
|
|
4613
|
+
if ("text" in content.resource && typeof content.resource.text === "string") {
|
|
4614
|
+
return content.resource.text;
|
|
4615
|
+
}
|
|
4616
|
+
return content.resource.uri;
|
|
4617
|
+
}
|
|
4618
|
+
return void 0;
|
|
4619
|
+
}
|
|
4620
|
+
function toHistoryEntryFromUpdate(notification) {
|
|
4621
|
+
const update = notification.update;
|
|
4622
|
+
if (update.sessionUpdate !== "user_message_chunk" && update.sessionUpdate !== "agent_message_chunk") {
|
|
4623
|
+
return void 0;
|
|
4624
|
+
}
|
|
4625
|
+
const text = textFromContent(update.content);
|
|
4626
|
+
if (!text) {
|
|
4627
|
+
return void 0;
|
|
4628
|
+
}
|
|
4629
|
+
const textPreview = toPreviewText(text);
|
|
4630
|
+
if (!textPreview) {
|
|
4631
|
+
return void 0;
|
|
4632
|
+
}
|
|
4633
|
+
return {
|
|
4634
|
+
role: update.sessionUpdate === "user_message_chunk" ? "user" : "assistant",
|
|
4635
|
+
timestamp: isoNow2(),
|
|
4636
|
+
textPreview
|
|
4637
|
+
};
|
|
4638
|
+
}
|
|
4639
|
+
function appendHistoryEntries(current, entries) {
|
|
4640
|
+
const base = current ? [...current] : [];
|
|
4641
|
+
for (const entry of entries) {
|
|
4642
|
+
if (!entry.textPreview.trim()) {
|
|
4643
|
+
continue;
|
|
4644
|
+
}
|
|
4645
|
+
base.push(entry);
|
|
4646
|
+
}
|
|
4647
|
+
if (base.length <= SESSION_HISTORY_MAX_ENTRIES) {
|
|
4648
|
+
return base;
|
|
4649
|
+
}
|
|
4650
|
+
return base.slice(base.length - SESSION_HISTORY_MAX_ENTRIES);
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
// src/session-runtime-lifecycle.ts
|
|
4654
|
+
function applyLifecycleSnapshotToRecord(record, snapshot) {
|
|
4655
|
+
record.pid = snapshot.pid;
|
|
4656
|
+
record.agentStartedAt = snapshot.startedAt;
|
|
4657
|
+
if (snapshot.lastExit) {
|
|
4658
|
+
record.lastAgentExitCode = snapshot.lastExit.exitCode;
|
|
4659
|
+
record.lastAgentExitSignal = snapshot.lastExit.signal;
|
|
4660
|
+
record.lastAgentExitAt = snapshot.lastExit.exitedAt;
|
|
4661
|
+
record.lastAgentDisconnectReason = snapshot.lastExit.reason;
|
|
4662
|
+
return;
|
|
4663
|
+
}
|
|
4664
|
+
record.lastAgentExitCode = void 0;
|
|
4665
|
+
record.lastAgentExitSignal = void 0;
|
|
4666
|
+
record.lastAgentExitAt = void 0;
|
|
4667
|
+
record.lastAgentDisconnectReason = void 0;
|
|
4668
|
+
}
|
|
4669
|
+
function reconcileAgentSessionId(record, agentSessionId) {
|
|
4670
|
+
const normalized = normalizeAgentSessionId(agentSessionId);
|
|
4671
|
+
if (!normalized) {
|
|
4672
|
+
return;
|
|
4673
|
+
}
|
|
4674
|
+
record.agentSessionId = normalized;
|
|
4675
|
+
}
|
|
4676
|
+
|
|
4677
|
+
// src/session-runtime-reconnect.ts
|
|
4678
|
+
function loadSessionCandidates(record) {
|
|
4679
|
+
const candidates = [normalizeAgentSessionId(record.agentSessionId), record.sessionId];
|
|
4680
|
+
const unique = [];
|
|
4681
|
+
for (const candidate of candidates) {
|
|
4682
|
+
if (!candidate || unique.includes(candidate)) {
|
|
4683
|
+
continue;
|
|
4684
|
+
}
|
|
4685
|
+
unique.push(candidate);
|
|
4686
|
+
}
|
|
4687
|
+
return unique;
|
|
4688
|
+
}
|
|
4689
|
+
async function connectAndLoadSession(options) {
|
|
4690
|
+
const record = options.record;
|
|
4691
|
+
const client = options.client;
|
|
4692
|
+
const storedProcessAlive = isProcessAlive(record.pid);
|
|
4693
|
+
const shouldReconnect = Boolean(record.pid) && !storedProcessAlive;
|
|
4694
|
+
if (options.verbose) {
|
|
4695
|
+
if (storedProcessAlive) {
|
|
4696
|
+
process.stderr.write(
|
|
4697
|
+
`[acpx] saved session pid ${record.pid} is running; reconnecting with loadSession
|
|
4698
|
+
`
|
|
4699
|
+
);
|
|
4700
|
+
} else if (shouldReconnect) {
|
|
4701
|
+
process.stderr.write(
|
|
4702
|
+
`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session/load
|
|
4703
|
+
`
|
|
4704
|
+
);
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
await options.withTimeout(client.start(), options.timeoutMs);
|
|
4708
|
+
options.onClientAvailable?.(options.activeController);
|
|
4709
|
+
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
4710
|
+
record.closed = false;
|
|
4711
|
+
record.closedAt = void 0;
|
|
4712
|
+
options.onConnectedRecord?.(record);
|
|
4713
|
+
await writeSessionRecord(record);
|
|
4714
|
+
let resumed = false;
|
|
4715
|
+
let loadError;
|
|
4716
|
+
let sessionId = record.sessionId;
|
|
4717
|
+
if (client.supportsLoadSession()) {
|
|
4718
|
+
const candidates = loadSessionCandidates(record);
|
|
4719
|
+
for (const candidate of candidates) {
|
|
4720
|
+
if (options.verbose && candidates.length > 1) {
|
|
4721
|
+
process.stderr.write(`[acpx] attempting session/load with ${candidate}
|
|
4722
|
+
`);
|
|
4723
|
+
}
|
|
4724
|
+
try {
|
|
4725
|
+
const loadResult = await options.withTimeout(
|
|
4726
|
+
client.loadSessionWithOptions(candidate, record.cwd, {
|
|
4727
|
+
suppressReplayUpdates: true
|
|
4728
|
+
}),
|
|
4729
|
+
options.timeoutMs
|
|
4730
|
+
);
|
|
4731
|
+
reconcileAgentSessionId(record, loadResult.agentSessionId);
|
|
4732
|
+
resumed = true;
|
|
4733
|
+
sessionId = candidate;
|
|
4734
|
+
loadError = void 0;
|
|
4735
|
+
break;
|
|
4736
|
+
} catch (error) {
|
|
4737
|
+
loadError = formatErrorMessage(error);
|
|
4738
|
+
if (!options.shouldFallbackToNewSession(error)) {
|
|
4739
|
+
throw error;
|
|
4740
|
+
}
|
|
4741
|
+
if (options.verbose) {
|
|
4742
|
+
process.stderr.write(
|
|
4743
|
+
`[acpx] session/load failed for ${candidate}: ${loadError}
|
|
4744
|
+
`
|
|
4745
|
+
);
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
if (!resumed) {
|
|
4750
|
+
const createdSession = await options.withTimeout(
|
|
4751
|
+
client.createSession(record.cwd),
|
|
4752
|
+
options.timeoutMs
|
|
4753
|
+
);
|
|
4754
|
+
sessionId = createdSession.sessionId;
|
|
4755
|
+
record.sessionId = sessionId;
|
|
4756
|
+
reconcileAgentSessionId(record, createdSession.agentSessionId);
|
|
4757
|
+
}
|
|
4758
|
+
} else {
|
|
4759
|
+
const createdSession = await options.withTimeout(
|
|
4760
|
+
client.createSession(record.cwd),
|
|
4761
|
+
options.timeoutMs
|
|
4762
|
+
);
|
|
4763
|
+
sessionId = createdSession.sessionId;
|
|
4764
|
+
record.sessionId = sessionId;
|
|
4765
|
+
reconcileAgentSessionId(record, createdSession.agentSessionId);
|
|
4766
|
+
}
|
|
4767
|
+
options.onSessionIdResolved?.(sessionId);
|
|
4768
|
+
return {
|
|
4769
|
+
sessionId,
|
|
4770
|
+
agentSessionId: record.agentSessionId,
|
|
4771
|
+
resumed,
|
|
4772
|
+
loadError
|
|
4773
|
+
};
|
|
4774
|
+
}
|
|
4775
|
+
|
|
4443
4776
|
// src/session-runtime.ts
|
|
4444
4777
|
var DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
4445
4778
|
var INTERRUPT_CANCEL_WAIT_MS = 2500;
|
|
4446
|
-
var SESSION_HISTORY_MAX_ENTRIES = 500;
|
|
4447
|
-
var SESSION_HISTORY_PREVIEW_CHARS = 220;
|
|
4448
4779
|
var TimeoutError = class extends Error {
|
|
4449
4780
|
constructor(timeoutMs) {
|
|
4450
4781
|
super(`Timed out after ${timeoutMs}ms`);
|
|
@@ -4580,162 +4911,12 @@ function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
|
4580
4911
|
}
|
|
4581
4912
|
return Math.round(ttlMs);
|
|
4582
4913
|
}
|
|
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);
|
|
4637
|
-
}
|
|
4638
|
-
if (base.length <= SESSION_HISTORY_MAX_ENTRIES) {
|
|
4639
|
-
return base;
|
|
4640
|
-
}
|
|
4641
|
-
return base.slice(base.length - SESSION_HISTORY_MAX_ENTRIES);
|
|
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;
|
|
4652
|
-
}
|
|
4653
|
-
record.lastAgentExitCode = void 0;
|
|
4654
|
-
record.lastAgentExitSignal = void 0;
|
|
4655
|
-
record.lastAgentExitAt = void 0;
|
|
4656
|
-
record.lastAgentDisconnectReason = void 0;
|
|
4657
|
-
}
|
|
4658
|
-
function reconcileAgentSessionId(record, agentSessionId) {
|
|
4659
|
-
const normalized = normalizeAgentSessionId(agentSessionId);
|
|
4660
|
-
if (!normalized) {
|
|
4661
|
-
return;
|
|
4662
|
-
}
|
|
4663
|
-
record.agentSessionId = normalized;
|
|
4664
|
-
}
|
|
4665
4914
|
function shouldFallbackToNewSession(error) {
|
|
4666
4915
|
if (error instanceof TimeoutError || error instanceof InterruptedError) {
|
|
4667
4916
|
return false;
|
|
4668
4917
|
}
|
|
4669
4918
|
return isAcpResourceNotFoundError(error);
|
|
4670
4919
|
}
|
|
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
4920
|
async function runQueuedTask(sessionRecordId, task, options) {
|
|
4740
4921
|
const outputFormatter = task.waitForCompletion ? new QueueTaskOutputFormatter(task) : DISCARD_OUTPUT_FORMATTER;
|
|
4741
4922
|
try {
|
|
@@ -4842,6 +5023,8 @@ async function runSessionPrompt(options) {
|
|
|
4842
5023
|
timeoutMs: options.timeoutMs,
|
|
4843
5024
|
verbose: options.verbose,
|
|
4844
5025
|
activeController,
|
|
5026
|
+
withTimeout,
|
|
5027
|
+
shouldFallbackToNewSession,
|
|
4845
5028
|
onClientAvailable: (controller) => {
|
|
4846
5029
|
options.onClientAvailable?.(controller);
|
|
4847
5030
|
notifiedClientAvailable = true;
|
|
@@ -4976,6 +5159,8 @@ async function withConnectedSession(options) {
|
|
|
4976
5159
|
timeoutMs: options.timeoutMs,
|
|
4977
5160
|
verbose: options.verbose,
|
|
4978
5161
|
activeController,
|
|
5162
|
+
withTimeout,
|
|
5163
|
+
shouldFallbackToNewSession,
|
|
4979
5164
|
onClientAvailable: (controller) => {
|
|
4980
5165
|
options.onClientAvailable?.(controller);
|
|
4981
5166
|
notifiedClientAvailable = true;
|