acpx 0.19.2 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-registry-DWlx5BQ4.js → agent-registry-DWnqUICY.js} +20 -17
- package/dist/agent-registry-DWnqUICY.js.map +1 -0
- package/dist/agent-registry.js +1 -1
- package/dist/{cli-D-s_eTUy.js → cli-C1uofM8S.js} +5 -5
- package/dist/{cli-D-s_eTUy.js.map → cli-C1uofM8S.js.map} +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +56 -40
- package/dist/cli.js.map +1 -1
- package/dist/{client-BchvQRpz.d.ts → client-CdG6nVDG.d.ts} +4 -3
- package/dist/client-CdG6nVDG.d.ts.map +1 -0
- package/dist/{flows-DdxEaeZo.js → flows-hz_Pfsp-.js} +9 -5
- package/dist/flows-hz_Pfsp-.js.map +1 -0
- package/dist/flows.d.ts +2 -2
- package/dist/flows.js +1 -1
- package/dist/{invocation-options-HjxSBsK0.js → invocation-options-DbZzMvKM.js} +5 -5
- package/dist/{invocation-options-HjxSBsK0.js.map → invocation-options-DbZzMvKM.js.map} +1 -1
- package/dist/{ipc-D0ZMQ0q9.js → ipc-BW06Pbb8.js} +338 -289
- package/dist/ipc-BW06Pbb8.js.map +1 -0
- package/dist/{output-GQnhkJMU.js → output-BjeeG-cj.js} +3 -3
- package/dist/{output-GQnhkJMU.js.map → output-BjeeG-cj.js.map} +1 -1
- package/dist/{queue-owner-runtime-W0lly6ap.js → queue-owner-runtime-Dhr4gxNE.js} +427 -368
- package/dist/queue-owner-runtime-Dhr4gxNE.js.map +1 -0
- package/dist/runtime.d.ts +7 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +24 -17
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-Dp6mzNSy.d.ts → session-options-X7LhX9BF.d.ts} +2 -1
- package/dist/{session-options-Dp6mzNSy.d.ts.map → session-options-X7LhX9BF.d.ts.map} +1 -1
- package/dist/{session-watch-_8yxQ6ba.js → session-watch-CgfkUKLA.js} +4 -4
- package/dist/{session-watch-_8yxQ6ba.js.map → session-watch-CgfkUKLA.js.map} +1 -1
- package/dist/{watch-C0mwpJcN.js → watch-BGdqBiNX.js} +2 -2
- package/dist/{watch-C0mwpJcN.js.map → watch-BGdqBiNX.js.map} +1 -1
- package/package.json +11 -11
- package/skills/acpx/SKILL.md +7 -2
- package/dist/agent-registry-DWlx5BQ4.js.map +0 -1
- package/dist/client-BchvQRpz.d.ts.map +0 -1
- package/dist/flows-DdxEaeZo.js.map +0 -1
- package/dist/ipc-D0ZMQ0q9.js.map +0 -1
- package/dist/queue-owner-runtime-W0lly6ap.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as resetPerfMetrics, Bt as
|
|
2
|
-
import { A as requireAgentStdio, C as PROCESS_HELPER_TIMEOUT_MS, D as isoNow$2, E as isChildProcessRunning, F as waitForChildExit, I as waitForSpawn, M as resolveAgentSessionCwd, N as runTimedExecFile, P as splitCommandLine, T as basenameToken, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentCommandParts, v as buildTerminalShellSpawnCommand, w as asAbsoluteCwd, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-
|
|
1
|
+
import { $ as resetPerfMetrics, Bt as GeminiAcpStartupTimeoutError, C as textPrompt, Ct as normalizeOutputError, F as normalizeName, Ft as AgentSpawnError, G as SESSION_JOURNAL_SCHEMA, Gt as SessionModeReplayError, Ht as PermissionPromptUnavailableError, It as AgentStartupError, Jt as SessionResumeRequiredError, K as SessionJournalReader, Kt as SessionModelReplayError, L as readSessionRecord, Lt as AuthPolicyError, M as isoNow$1, Nt as AcpxOperationalError, O as absolutePath, Pt as AgentDisconnectedError, Q as measurePerf, R as resolveSessionRecord, Rt as ClaudeAcpSessionCreateTimeoutError, S as promptToDisplayText, St as isRetryablePromptError, T as lockOwnerPid, Tt as isAcpResourceNotFoundError, U as extractAgentSessionId, Ut as QueueConnectionError, Vt as PermissionDeniedError, W as normalizeAgentSessionId, Wt as SessionConfigOptionReplayError, X as getPerfMetricsSnapshot, Y as formatPerfMetric, Yt as UnsupportedPromptContentError, Z as incrementPerfCounter, _ as ProcessDescendants, a as trySetModelOnRunningOwner, at as isSessionUpdateNotification, bt as formatErrorMessage, d as releaseQueueOwnerLease, dt as sessionBaseDir, et as setPerfGauge, f as terminateProcess, ft as sessionEventActivePath, g as QueueLeaseGuardSettlementError, h as waitMs$1, i as trySetModeOnRunningOwner, it as isAcpMessageObject, j as findSessionByDirectoryWalk, k as findGitRepositoryRoot, m as tryAcquireQueueOwnerLease, mt as sessionEventSegmentPath, n as tryCloseSessionOnRunningOwner, o as trySubmitToRunningOwner, p as terminateQueueOwnerForSession, pt as sessionEventLockPath, r as trySetConfigOptionOnRunningOwner, rt as isAcpJsonRpcMessage, s as SessionQueueOwner, t as tryCancelOnRunningOwner, tt as startPerfTimer, u as refreshQueueOwnerLease, ut as defaultSessionEventLog, vt as isProcessAlive, w as createLockOwner, wt as extractAcpError, xt as isAcpQueryClosedBeforeResponseError, y as getUnsupportedPromptContentMessage, z as writeSessionRecord, zt as CopilotAcpUnsupportedError } from "./ipc-BW06Pbb8.js";
|
|
2
|
+
import { A as requireAgentStdio, C as PROCESS_HELPER_TIMEOUT_MS, D as isoNow$2, E as isChildProcessRunning, F as waitForChildExit, I as waitForSpawn, M as resolveAgentSessionCwd, N as runTimedExecFile, P as splitCommandLine, T as basenameToken, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentCommandParts, v as buildTerminalShellSpawnCommand, w as asAbsoluteCwd, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-DWnqUICY.js";
|
|
3
3
|
import fs, { readFileSync, realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import path from "node:path";
|
|
@@ -8,14 +8,14 @@ import fs$1 from "node:fs/promises";
|
|
|
8
8
|
import { appendRegularFile, appendRegularFileSync, withTempFile } from "@openclaw/fs-safe/advanced";
|
|
9
9
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
12
|
+
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
11
13
|
import { Readable, Writable } from "node:stream";
|
|
12
14
|
import { PROTOCOL_VERSION, RequestError, client, methods } from "@agentclientprotocol/sdk";
|
|
13
15
|
import { FsSafeError } from "@openclaw/fs-safe/errors";
|
|
14
16
|
import { isPathInside } from "@openclaw/fs-safe/path";
|
|
15
17
|
import { root } from "@openclaw/fs-safe/root";
|
|
16
18
|
import readline from "node:readline/promises";
|
|
17
|
-
import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
|
|
18
|
-
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
19
19
|
//#region src/async-control.ts
|
|
20
20
|
var TimeoutError = class extends Error {
|
|
21
21
|
constructor(timeoutMs) {
|
|
@@ -346,6 +346,246 @@ async function listSessionEvents(sessionId, maxSegments) {
|
|
|
346
346
|
}).readAcpMessages();
|
|
347
347
|
}
|
|
348
348
|
//#endregion
|
|
349
|
+
//#region src/session/turn-ownership.ts
|
|
350
|
+
const LOCK_RETRY_MS = 15;
|
|
351
|
+
const INCOMPLETE_RESERVATION_GRACE_MS = 15e3;
|
|
352
|
+
let lastCreatedAt = 0;
|
|
353
|
+
const localTurns = /* @__PURE__ */ new Map();
|
|
354
|
+
async function readLock(filePath) {
|
|
355
|
+
try {
|
|
356
|
+
const stat = await fs$1.lstat(filePath, { bigint: true });
|
|
357
|
+
if (!stat.isFile()) throw new Error(`Session turn lock is not a regular file: ${filePath}`);
|
|
358
|
+
return {
|
|
359
|
+
stat,
|
|
360
|
+
payload: await fs$1.readFile(filePath, "utf8")
|
|
361
|
+
};
|
|
362
|
+
} catch (error) {
|
|
363
|
+
if (error.code === "ENOENT") return;
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function parseLock(payload) {
|
|
368
|
+
try {
|
|
369
|
+
return JSON.parse(payload);
|
|
370
|
+
} catch {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function tryAcquireGuard(filePath, owner, signal) {
|
|
375
|
+
try {
|
|
376
|
+
return await acquireFileLock(filePath, {
|
|
377
|
+
managerKey: "acpx.session-turn",
|
|
378
|
+
lockPath: `${filePath}.guard`,
|
|
379
|
+
staleMs: Infinity,
|
|
380
|
+
timeoutMs: LOCK_RETRY_MS,
|
|
381
|
+
retry: {
|
|
382
|
+
retries: 8,
|
|
383
|
+
minTimeout: 1,
|
|
384
|
+
maxTimeout: 2,
|
|
385
|
+
factor: 1,
|
|
386
|
+
randomize: false
|
|
387
|
+
},
|
|
388
|
+
staleRecovery: "remove-if-unchanged",
|
|
389
|
+
payload: () => owner.payload,
|
|
390
|
+
shouldReclaim: ({ payload }) => owner.hasExited(payload, false, signal),
|
|
391
|
+
shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true, signal)
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (error.code === "file_lock_timeout") return;
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function removeObservedLock(filePath, observed) {
|
|
399
|
+
const current = await readLock(filePath);
|
|
400
|
+
if (!current) return true;
|
|
401
|
+
if (current.payload !== observed.payload || current.stat.mtimeNs !== observed.stat.mtimeNs || current.stat.size !== observed.stat.size) return false;
|
|
402
|
+
await fs$1.unlink(filePath).catch((error) => {
|
|
403
|
+
if (error.code !== "ENOENT") throw error;
|
|
404
|
+
});
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
async function recoverAbandonedLock(filePath, owner, signal) {
|
|
408
|
+
const observed = await readLock(filePath);
|
|
409
|
+
if (!observed) return true;
|
|
410
|
+
const payload = parseLock(observed.payload);
|
|
411
|
+
const pid = lockOwnerPid(payload);
|
|
412
|
+
if (pid && (!await owner.hasExited(payload, false, signal) || !await owner.hasExited(payload, true, signal))) return false;
|
|
413
|
+
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
414
|
+
if (await removeObservedLock(filePath, observed)) {
|
|
415
|
+
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
async function publishLock(filePath, payload, signal) {
|
|
421
|
+
await withTempFile({
|
|
422
|
+
rootDir: path.dirname(filePath),
|
|
423
|
+
prefix: "session-turn",
|
|
424
|
+
fileName: "lock"
|
|
425
|
+
}, async (temporaryPath) => {
|
|
426
|
+
await fs$1.writeFile(temporaryPath, payload, {
|
|
427
|
+
encoding: "utf8",
|
|
428
|
+
flag: "wx",
|
|
429
|
+
mode: 384
|
|
430
|
+
});
|
|
431
|
+
signal?.throwIfAborted();
|
|
432
|
+
try {
|
|
433
|
+
await fs$1.link(temporaryPath, filePath);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
if (!isHardlinkFallbackError(error)) throw error;
|
|
436
|
+
await fs$1.writeFile(filePath, payload, {
|
|
437
|
+
encoding: "utf8",
|
|
438
|
+
flag: "wx",
|
|
439
|
+
mode: 384
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
async function removePublishedLock(filePath, payload) {
|
|
445
|
+
const current = await readLock(filePath);
|
|
446
|
+
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
447
|
+
}
|
|
448
|
+
async function rejectAcquisition(filePath, receipt, error) {
|
|
449
|
+
const failures = [error];
|
|
450
|
+
await disposeTurn(filePath, receipt).catch((cleanupError) => {
|
|
451
|
+
failures.push(...cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError]);
|
|
452
|
+
});
|
|
453
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
async function releaseTurnGuard(receipt) {
|
|
457
|
+
await receipt.guard?.release();
|
|
458
|
+
receipt.guard = void 0;
|
|
459
|
+
}
|
|
460
|
+
async function removeTurnMarker(filePath, receipt) {
|
|
461
|
+
if (!receipt.removeMarker) return true;
|
|
462
|
+
if (receipt.guard && !await receipt.guard.verifyStillHeld()) await releaseTurnGuard(receipt);
|
|
463
|
+
receipt.guard ??= await tryAcquireGuard(filePath, receipt.owner);
|
|
464
|
+
if (!receipt.guard) return false;
|
|
465
|
+
await receipt.removeMarker();
|
|
466
|
+
receipt.removeMarker = void 0;
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
async function settleTurn(filePath, receipt) {
|
|
470
|
+
if (receipt.settled) return true;
|
|
471
|
+
const pending = receipt.pending ??= (async () => {
|
|
472
|
+
receipt.failed = false;
|
|
473
|
+
const failures = [];
|
|
474
|
+
try {
|
|
475
|
+
if (!await removeTurnMarker(filePath, receipt)) return false;
|
|
476
|
+
} catch (error) {
|
|
477
|
+
failures.push(error);
|
|
478
|
+
}
|
|
479
|
+
if (failures.length === 0 || !receipt.admitted) try {
|
|
480
|
+
await releaseTurnGuard(receipt);
|
|
481
|
+
} catch (error) {
|
|
482
|
+
failures.push(error);
|
|
483
|
+
}
|
|
484
|
+
if (failures.length > 1) throw new AggregateError(failures, "Session turn cleanup failed");
|
|
485
|
+
if (failures.length === 1) throw failures[0];
|
|
486
|
+
receipt.settled = true;
|
|
487
|
+
localTurns.delete(filePath);
|
|
488
|
+
return true;
|
|
489
|
+
})();
|
|
490
|
+
try {
|
|
491
|
+
return await pending;
|
|
492
|
+
} finally {
|
|
493
|
+
if (receipt.pending === pending) {
|
|
494
|
+
receipt.pending = void 0;
|
|
495
|
+
receipt.failed = !receipt.settled;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
async function disposeTurn(filePath, receipt) {
|
|
500
|
+
if (!await settleTurn(filePath, receipt)) throw new Error(`Session turn cleanup is waiting for its guard: ${filePath}`);
|
|
501
|
+
}
|
|
502
|
+
async function tryPublishLock(filePath, payload, owner, signal) {
|
|
503
|
+
for (;;) try {
|
|
504
|
+
await publishLock(filePath, payload, signal);
|
|
505
|
+
return true;
|
|
506
|
+
} catch (error) {
|
|
507
|
+
if (error.code !== "EEXIST") throw error;
|
|
508
|
+
if (!await recoverAbandonedLock(filePath, owner, signal)) return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function tryAcquireTurnCleanup(filePath, payload, owner, signal) {
|
|
512
|
+
const previous = localTurns.get(filePath);
|
|
513
|
+
if (previous) {
|
|
514
|
+
if (previous.failed) await settleTurn(filePath, previous);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const receipt = {
|
|
518
|
+
owner,
|
|
519
|
+
removeMarker: async () => await removePublishedLock(filePath, payload),
|
|
520
|
+
admitted: false,
|
|
521
|
+
failed: false,
|
|
522
|
+
settled: false
|
|
523
|
+
};
|
|
524
|
+
localTurns.set(filePath, receipt);
|
|
525
|
+
try {
|
|
526
|
+
receipt.guard = await tryAcquireGuard(filePath, owner, signal);
|
|
527
|
+
} catch (error) {
|
|
528
|
+
localTurns.delete(filePath);
|
|
529
|
+
throw error;
|
|
530
|
+
}
|
|
531
|
+
if (!receipt.guard) {
|
|
532
|
+
localTurns.delete(filePath);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
return receipt;
|
|
536
|
+
}
|
|
537
|
+
async function tryAcquireTurn(filePath, payload, owner, signal) {
|
|
538
|
+
const receipt = await tryAcquireTurnCleanup(filePath, payload, owner, signal);
|
|
539
|
+
if (!receipt) return;
|
|
540
|
+
try {
|
|
541
|
+
signal?.throwIfAborted();
|
|
542
|
+
if (!await tryPublishLock(filePath, payload, owner, signal)) {
|
|
543
|
+
await disposeTurn(filePath, receipt);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const observed = await readLock(filePath);
|
|
547
|
+
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
548
|
+
signal?.throwIfAborted();
|
|
549
|
+
receipt.admitted = true;
|
|
550
|
+
receipt.removeMarker = async () => await removeObservedLock(filePath, observed);
|
|
551
|
+
return { [Symbol.asyncDispose]: async () => await disposeTurn(filePath, receipt) };
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return await rejectAcquisition(filePath, receipt, error);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function acquireSessionTurn(recordId, signal) {
|
|
557
|
+
return await acquireSessionOwnership(sessionEventLockPath(recordId), signal);
|
|
558
|
+
}
|
|
559
|
+
async function acquireSessionScope(scope, signal) {
|
|
560
|
+
return await acquireSessionTurn(`ensure:${createHash("sha256").update(JSON.stringify([
|
|
561
|
+
scope.agentCommand,
|
|
562
|
+
scope.cwd,
|
|
563
|
+
scope.name
|
|
564
|
+
])).digest("hex")}`, signal);
|
|
565
|
+
}
|
|
566
|
+
async function acquireSessionImport(signal) {
|
|
567
|
+
return await acquireSessionOwnership(path.join(sessionBaseDir(), ".import-admission.lock"), signal);
|
|
568
|
+
}
|
|
569
|
+
async function acquireSessionOwnership(requestedPath, signal) {
|
|
570
|
+
await fs$1.mkdir(path.dirname(requestedPath), {
|
|
571
|
+
recursive: true,
|
|
572
|
+
mode: 448
|
|
573
|
+
});
|
|
574
|
+
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
575
|
+
const owner = await createLockOwner({ signal });
|
|
576
|
+
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
577
|
+
const payload = `${JSON.stringify({
|
|
578
|
+
...owner.payload,
|
|
579
|
+
created_at: new Date(lastCreatedAt).toISOString()
|
|
580
|
+
}, null, 2)}\n`;
|
|
581
|
+
for (;;) {
|
|
582
|
+
signal?.throwIfAborted();
|
|
583
|
+
const turn = await tryAcquireTurn(filePath, payload, owner, signal);
|
|
584
|
+
if (turn) return turn;
|
|
585
|
+
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
//#endregion
|
|
349
589
|
//#region src/session/execution/contracts.ts
|
|
350
590
|
const DEFAULT_QUEUE_OWNER_TTL_MS = 3e5;
|
|
351
591
|
function normalizeQueueOwnerTtlMs(ttlMs) {
|
|
@@ -1016,10 +1256,10 @@ function assignClaudeCodeSystemPrompt(target, systemPrompt) {
|
|
|
1016
1256
|
function isAppendSystemPrompt(value) {
|
|
1017
1257
|
return !!value && typeof value === "object" && typeof value.append === "string" && value.append.length > 0;
|
|
1018
1258
|
}
|
|
1019
|
-
function resolveClaudeCodeExecutable(platform = process.platform, env = process.env) {
|
|
1259
|
+
function resolveClaudeCodeExecutable(platform = process.platform, env = process.env, cwd) {
|
|
1020
1260
|
if (platform !== "win32") return;
|
|
1021
1261
|
if (readWindowsEnvValue(env, "CLAUDE_CODE_EXECUTABLE")) return;
|
|
1022
|
-
return resolveWindowsExecutablePath("claude", env);
|
|
1262
|
+
return resolveWindowsExecutablePath("claude", env, cwd);
|
|
1023
1263
|
}
|
|
1024
1264
|
//#endregion
|
|
1025
1265
|
//#region src/acp/auth-env.ts
|
|
@@ -1066,7 +1306,7 @@ function promotePrefixedAuthEnvironment(env) {
|
|
|
1066
1306
|
if (!normalized) continue;
|
|
1067
1307
|
protectEnvKey(protectedKeys, key);
|
|
1068
1308
|
protectEnvKey(protectedKeys, normalized);
|
|
1069
|
-
|
|
1309
|
+
assignIfMissing(env, normalized, value);
|
|
1070
1310
|
}
|
|
1071
1311
|
return protectedKeys;
|
|
1072
1312
|
}
|
|
@@ -1103,7 +1343,7 @@ function addAuthCredentialEnvKeys(protectedKeys, methodId, credential) {
|
|
|
1103
1343
|
}
|
|
1104
1344
|
function assignAuthCredentialEnv(env, methodId, credential) {
|
|
1105
1345
|
if (typeof credential !== "string" || credential.trim().length === 0) return;
|
|
1106
|
-
if (!methodId.includes("=") && !methodId.includes("\0")
|
|
1346
|
+
if (!methodId.includes("=") && !methodId.includes("\0")) assignIfMissing(env, methodId, credential);
|
|
1107
1347
|
const normalized = toEnvToken(methodId);
|
|
1108
1348
|
if (normalized) {
|
|
1109
1349
|
assignIfMissing(env, `${AUTH_ENV_PREFIX}${normalized}`, credential);
|
|
@@ -1111,7 +1351,10 @@ function assignAuthCredentialEnv(env, methodId, credential) {
|
|
|
1111
1351
|
}
|
|
1112
1352
|
}
|
|
1113
1353
|
function assignIfMissing(env, key, value) {
|
|
1114
|
-
if (env[key]
|
|
1354
|
+
if (env[key] != null) return;
|
|
1355
|
+
const normalizedKey = protectedEnvKey(key);
|
|
1356
|
+
if (process.platform === "win32" && Object.entries(env).some(([existingKey, existingValue]) => existingValue != null && protectedEnvKey(existingKey) === normalizedKey)) return;
|
|
1357
|
+
env[key] = value;
|
|
1115
1358
|
}
|
|
1116
1359
|
function resolveConfiguredAuthCredential(methodId, authCredentials) {
|
|
1117
1360
|
const configCredentials = authCredentials ?? {};
|
|
@@ -1622,7 +1865,7 @@ function buildTerminalSpawnOptions(command, cwd, env, platform = process.platfor
|
|
|
1622
1865
|
"pipe"
|
|
1623
1866
|
],
|
|
1624
1867
|
windowsHide: true
|
|
1625
|
-
}, platform, resolvedEnv ?? process.env);
|
|
1868
|
+
}, platform, resolvedEnv ?? process.env, cwd);
|
|
1626
1869
|
}
|
|
1627
1870
|
function readTerminalOutputCeiling() {
|
|
1628
1871
|
const raw = process.env.ACPX_TERMINAL_MAX_OUTPUT_BYTES?.trim();
|
|
@@ -1673,6 +1916,7 @@ var TerminalManager = class {
|
|
|
1673
1916
|
processHelperTimeoutMs;
|
|
1674
1917
|
outputCeilingBytes;
|
|
1675
1918
|
terminals = /* @__PURE__ */ new Map();
|
|
1919
|
+
creations = /* @__PURE__ */ new Map();
|
|
1676
1920
|
constructor(options) {
|
|
1677
1921
|
this.outputCeilingBytes = readTerminalOutputCeiling();
|
|
1678
1922
|
this.cwd = options.cwd;
|
|
@@ -1689,7 +1933,15 @@ var TerminalManager = class {
|
|
|
1689
1933
|
this.permissionMode = permissionMode;
|
|
1690
1934
|
this.nonInteractivePermissions = nonInteractivePermissions ?? "deny";
|
|
1691
1935
|
}
|
|
1692
|
-
|
|
1936
|
+
createTerminal(params, authority) {
|
|
1937
|
+
const creation = {};
|
|
1938
|
+
const pending = Promise.resolve().then(() => this.create(params, authority, creation));
|
|
1939
|
+
this.creations.set(pending, creation);
|
|
1940
|
+
const remove = () => this.creations.delete(pending);
|
|
1941
|
+
pending.then(remove, remove);
|
|
1942
|
+
return pending;
|
|
1943
|
+
}
|
|
1944
|
+
async create(params, authority, creation) {
|
|
1693
1945
|
assertControlAuthority(authority);
|
|
1694
1946
|
const commandLine = toCommandLine(params.command, params.args);
|
|
1695
1947
|
const summary = `terminal/create: ${commandLine}`;
|
|
@@ -1753,10 +2005,15 @@ var TerminalManager = class {
|
|
|
1753
2005
|
await terminal.descendants?.capture(terminal.processHelperTimeoutMs);
|
|
1754
2006
|
assertControlAuthority(authority);
|
|
1755
2007
|
} catch (error) {
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2008
|
+
try {
|
|
2009
|
+
await this.releaseTerminal({
|
|
2010
|
+
terminalId,
|
|
2011
|
+
sessionId: params.sessionId
|
|
2012
|
+
});
|
|
2013
|
+
} catch (cleanupError) {
|
|
2014
|
+
creation.cleanupFailure = { error: cleanupError };
|
|
2015
|
+
throw cleanupError;
|
|
2016
|
+
}
|
|
1760
2017
|
throw error;
|
|
1761
2018
|
}
|
|
1762
2019
|
this.emitOperation({
|
|
@@ -1889,11 +2146,13 @@ var TerminalManager = class {
|
|
|
1889
2146
|
}
|
|
1890
2147
|
}
|
|
1891
2148
|
async shutdown() {
|
|
2149
|
+
const creations = [...this.creations];
|
|
2150
|
+
await Promise.allSettled(creations.map(([pending]) => pending));
|
|
1892
2151
|
const results = await Promise.allSettled(Array.from(this.terminals.keys(), (terminalId) => this.releaseTerminal({
|
|
1893
2152
|
terminalId,
|
|
1894
2153
|
sessionId: "shutdown"
|
|
1895
2154
|
})));
|
|
1896
|
-
const failures = [];
|
|
2155
|
+
const failures = creations.flatMap(([, creation]) => creation.cleanupFailure ? [creation.cleanupFailure.error] : []);
|
|
1897
2156
|
for (const result of results) if (result.status === "rejected") failures.push(result.reason);
|
|
1898
2157
|
if (failures.length > 0) throw new AggregateError(failures, "Terminal shutdown failed", { cause: failures[0] });
|
|
1899
2158
|
}
|
|
@@ -2337,8 +2596,9 @@ var AcpClient = class {
|
|
|
2337
2596
|
}
|
|
2338
2597
|
fallback?.abort();
|
|
2339
2598
|
}
|
|
2340
|
-
async start(authority) {
|
|
2599
|
+
async start(authority, options) {
|
|
2341
2600
|
assertControlAuthority(authority);
|
|
2601
|
+
if (options) this.options.sessionOptions = structuredClone(options.sessionOptions);
|
|
2342
2602
|
if (this.hasLiveConnection()) return;
|
|
2343
2603
|
const epoch = await this.waitForPreviousClose();
|
|
2344
2604
|
const maxMessageBytes = readMaxAcpMessageBytes();
|
|
@@ -2457,14 +2717,14 @@ var AcpClient = class {
|
|
|
2457
2717
|
async ensureLaunchSupport(plan) {
|
|
2458
2718
|
if (plan.copilotAcp) await ensureCopilotAcpSupport(plan.spawnCommand, plan.commandContext);
|
|
2459
2719
|
if (!plan.claudeAcp) return;
|
|
2460
|
-
const claudeExe = resolveClaudeCodeExecutable(process.platform, plan.spawnOptions.env);
|
|
2720
|
+
const claudeExe = resolveClaudeCodeExecutable(process.platform, plan.spawnOptions.env, plan.spawnOptions.cwd);
|
|
2461
2721
|
if (claudeExe) {
|
|
2462
2722
|
plan.spawnOptions.env.CLAUDE_CODE_EXECUTABLE = claudeExe;
|
|
2463
2723
|
this.log(`resolved system Claude Code executable: ${claudeExe}`);
|
|
2464
2724
|
}
|
|
2465
2725
|
}
|
|
2466
2726
|
prepareProcessLaunch(command, args, options) {
|
|
2467
|
-
const resolved = buildAgentSpawnCommand(command, args, process.platform, options.env);
|
|
2727
|
+
const resolved = buildAgentSpawnCommand(command, args, process.platform, options.env, options.cwd);
|
|
2468
2728
|
return {
|
|
2469
2729
|
launch: Object.freeze({
|
|
2470
2730
|
launchId: randomUUID(),
|
|
@@ -2674,6 +2934,10 @@ var AcpClient = class {
|
|
|
2674
2934
|
onPromptRequestWritten: (active, owner) => this.onPromptRequestWritten(active, owner)
|
|
2675
2935
|
});
|
|
2676
2936
|
}
|
|
2937
|
+
sessionMeta() {
|
|
2938
|
+
const { command, args } = resolveAgentCommandParts(this.options.agentCommand, this.options.agentArgv);
|
|
2939
|
+
return buildClaudeCodeOptionsMeta(this.options.sessionOptions, isClaudeAcpCommand(command, args));
|
|
2940
|
+
}
|
|
2677
2941
|
async createSession(cwd = this.options.cwd, authority) {
|
|
2678
2942
|
assertControlAuthority(authority);
|
|
2679
2943
|
const connection = this.getConnection();
|
|
@@ -2685,7 +2949,7 @@ var AcpClient = class {
|
|
|
2685
2949
|
const createPromise = this.runConnectionRequest(() => connection.agent.request(methods.agent.session.new, {
|
|
2686
2950
|
cwd: sessionCwd,
|
|
2687
2951
|
mcpServers: this.options.mcpServers ?? [],
|
|
2688
|
-
_meta:
|
|
2952
|
+
_meta: this.sessionMeta()
|
|
2689
2953
|
}), authority);
|
|
2690
2954
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
2691
2955
|
} catch (error) {
|
|
@@ -2725,7 +2989,8 @@ var AcpClient = class {
|
|
|
2725
2989
|
response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.load, {
|
|
2726
2990
|
sessionId,
|
|
2727
2991
|
cwd: sessionCwd,
|
|
2728
|
-
mcpServers: this.options.mcpServers ?? []
|
|
2992
|
+
mcpServers: this.options.mcpServers ?? [],
|
|
2993
|
+
_meta: this.sessionMeta()
|
|
2729
2994
|
}), options.authority);
|
|
2730
2995
|
await this.waitForSessionUpdateDrain(options.replayIdleMs ?? REPLAY_IDLE_MS, options.replayDrainTimeoutMs ?? REPLAY_DRAIN_TIMEOUT_MS);
|
|
2731
2996
|
} finally {
|
|
@@ -2743,7 +3008,8 @@ var AcpClient = class {
|
|
|
2743
3008
|
const response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.resume, {
|
|
2744
3009
|
sessionId,
|
|
2745
3010
|
cwd: sessionCwd,
|
|
2746
|
-
mcpServers: this.options.mcpServers ?? []
|
|
3011
|
+
mcpServers: this.options.mcpServers ?? [],
|
|
3012
|
+
_meta: this.sessionMeta()
|
|
2747
3013
|
}), authority);
|
|
2748
3014
|
this.loadedSessionId = sessionId;
|
|
2749
3015
|
const result = toReconnectedSessionResult(response);
|
|
@@ -3731,6 +3997,7 @@ function contentToUserContent(content) {
|
|
|
3731
3997
|
if (content.type === "resource") return resourceToUserContent(content);
|
|
3732
3998
|
if (content.type === "image") return { Image: {
|
|
3733
3999
|
source: content.data,
|
|
4000
|
+
mime_type: content.mimeType,
|
|
3734
4001
|
size: null
|
|
3735
4002
|
} };
|
|
3736
4003
|
if (content.type === "audio") return { Audio: {
|
|
@@ -3799,7 +4066,10 @@ function appendAgentThinking(agent, text) {
|
|
|
3799
4066
|
}
|
|
3800
4067
|
function trimRuntimeText(value, maxChars) {
|
|
3801
4068
|
if (value.length <= maxChars) return value;
|
|
3802
|
-
|
|
4069
|
+
let end = Math.max(0, maxChars - 3);
|
|
4070
|
+
const last = value.charCodeAt(end - 1);
|
|
4071
|
+
if (last >= 55296 && last <= 56319) end -= 1;
|
|
4072
|
+
return `${value.slice(0, end)}...`;
|
|
3803
4073
|
}
|
|
3804
4074
|
function statusIndicatesComplete(status) {
|
|
3805
4075
|
if (typeof status !== "string") return false;
|
|
@@ -4346,13 +4616,6 @@ function clearDesiredConfigOption(state, configId) {
|
|
|
4346
4616
|
function getDesiredModelId(state) {
|
|
4347
4617
|
return normalizeModelId(state?.session_options?.model);
|
|
4348
4618
|
}
|
|
4349
|
-
function setCurrentModelId(record, modelId) {
|
|
4350
|
-
const acpx = ensureAcpxState(record.acpx);
|
|
4351
|
-
const normalized = normalizeModelId(modelId);
|
|
4352
|
-
if (normalized) acpx.current_model_id = normalized;
|
|
4353
|
-
else delete acpx.current_model_id;
|
|
4354
|
-
record.acpx = acpx;
|
|
4355
|
-
}
|
|
4356
4619
|
function syncAdvertisedModelState(record, models) {
|
|
4357
4620
|
if (!models) return;
|
|
4358
4621
|
const acpx = ensureAcpxState(record.acpx);
|
|
@@ -4375,9 +4638,13 @@ async function applyRequestedModelIfAdvertised(params) {
|
|
|
4375
4638
|
});
|
|
4376
4639
|
if (warning) params.onWarning?.(warning);
|
|
4377
4640
|
if (!params.models) return { applied: false };
|
|
4378
|
-
if (params.models.currentModelId === requestedModel) return {
|
|
4641
|
+
if (params.models.currentModelId === requestedModel) return {
|
|
4642
|
+
applied: true,
|
|
4643
|
+
modelId: requestedModel
|
|
4644
|
+
};
|
|
4379
4645
|
return {
|
|
4380
4646
|
applied: true,
|
|
4647
|
+
modelId: requestedModel,
|
|
4381
4648
|
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models, params.authority), params.timeoutMs)
|
|
4382
4649
|
};
|
|
4383
4650
|
}
|
|
@@ -4393,25 +4660,35 @@ function applyConfigOptionsToRecord(record, result) {
|
|
|
4393
4660
|
if (!configOptions) return;
|
|
4394
4661
|
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
4395
4662
|
}
|
|
4396
|
-
function applyInitialModelSelection(record, originalModels,
|
|
4663
|
+
function applyInitialModelSelection(record, originalModels, modelApplication) {
|
|
4397
4664
|
applyConfigOptionsToRecord(record, modelApplication.response);
|
|
4398
|
-
syncAdvertisedModelState(record, modelApplication.response ? modelStateFromConfigOptions(modelApplication.response.configOptions) : originalModels);
|
|
4399
|
-
if (modelApplication.applied)
|
|
4665
|
+
syncAdvertisedModelState(record, modelApplication.response?.configOptions !== void 0 ? modelStateFromConfigOptions(modelApplication.response.configOptions) : originalModels);
|
|
4666
|
+
if (modelApplication.applied) record.acpx = applyModelSelection(record.acpx, modelApplication.modelId, modelApplication.response);
|
|
4400
4667
|
}
|
|
4401
|
-
function applyAcceptedConfigOptions(state, response) {
|
|
4668
|
+
function applyAcceptedConfigOptions(state, response, selection) {
|
|
4402
4669
|
const next = cloneSessionAcpxState(state) ?? {};
|
|
4403
|
-
if (
|
|
4670
|
+
if (response?.configOptions === void 0) {
|
|
4671
|
+
const option = next.config_options?.find((entry) => entry.id === selection.configId);
|
|
4672
|
+
if (option) option.currentValue = selection.value;
|
|
4673
|
+
return next;
|
|
4674
|
+
}
|
|
4404
4675
|
applyConfigOptionsModelState(next, response.configOptions);
|
|
4405
|
-
|
|
4406
|
-
const desiredEntries = [];
|
|
4407
|
-
for (const option of response.configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(next.desired_config_options, option.id)) desiredEntries.push([option.id, option.currentValue]);
|
|
4408
|
-
if (desiredEntries.length > 0) next.desired_config_options = Object.fromEntries(desiredEntries);
|
|
4409
|
-
else delete next.desired_config_options;
|
|
4676
|
+
reconcileDesiredConfigOptions(next, response.configOptions);
|
|
4410
4677
|
return next;
|
|
4411
4678
|
}
|
|
4679
|
+
function reconcileDesiredConfigOptions(state, configOptions) {
|
|
4680
|
+
if (!state.desired_config_options) return;
|
|
4681
|
+
const desiredEntries = [];
|
|
4682
|
+
for (const option of configOptions) if (typeof option.currentValue === "string" && Object.hasOwn(state.desired_config_options, option.id)) desiredEntries.push([option.id, option.currentValue]);
|
|
4683
|
+
if (desiredEntries.length > 0) state.desired_config_options = Object.fromEntries(desiredEntries);
|
|
4684
|
+
else delete state.desired_config_options;
|
|
4685
|
+
}
|
|
4412
4686
|
function applyModelSelection(state, modelId, response) {
|
|
4413
4687
|
const modelConfigId = advertisedModelState(state)?.configId;
|
|
4414
|
-
const next = applyAcceptedConfigOptions(state, response
|
|
4688
|
+
const next = applyAcceptedConfigOptions(state, response, {
|
|
4689
|
+
configId: modelConfigId,
|
|
4690
|
+
value: modelId
|
|
4691
|
+
});
|
|
4415
4692
|
next.session_options = {
|
|
4416
4693
|
...next.session_options,
|
|
4417
4694
|
model: modelId
|
|
@@ -4427,235 +4704,10 @@ function applyConfigOptionSelection(state, configId, value, response, modelConfi
|
|
|
4427
4704
|
...next.desired_config_options,
|
|
4428
4705
|
[configId]: value
|
|
4429
4706
|
};
|
|
4430
|
-
return applyAcceptedConfigOptions(next, response
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
//#region src/session/turn-ownership.ts
|
|
4434
|
-
const LOCK_RETRY_MS = 15;
|
|
4435
|
-
const INCOMPLETE_RESERVATION_GRACE_MS = 15e3;
|
|
4436
|
-
let lastCreatedAt = 0;
|
|
4437
|
-
const localTurns = /* @__PURE__ */ new Map();
|
|
4438
|
-
async function readLock(filePath) {
|
|
4439
|
-
try {
|
|
4440
|
-
const stat = await fs$1.lstat(filePath, { bigint: true });
|
|
4441
|
-
if (!stat.isFile()) throw new Error(`Session turn lock is not a regular file: ${filePath}`);
|
|
4442
|
-
return {
|
|
4443
|
-
stat,
|
|
4444
|
-
payload: await fs$1.readFile(filePath, "utf8")
|
|
4445
|
-
};
|
|
4446
|
-
} catch (error) {
|
|
4447
|
-
if (error.code === "ENOENT") return;
|
|
4448
|
-
throw error;
|
|
4449
|
-
}
|
|
4450
|
-
}
|
|
4451
|
-
function parseLock(payload) {
|
|
4452
|
-
try {
|
|
4453
|
-
return JSON.parse(payload);
|
|
4454
|
-
} catch {
|
|
4455
|
-
return;
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
async function tryAcquireGuard(filePath, owner, signal) {
|
|
4459
|
-
try {
|
|
4460
|
-
return await acquireFileLock(filePath, {
|
|
4461
|
-
managerKey: "acpx.session-turn",
|
|
4462
|
-
lockPath: `${filePath}.guard`,
|
|
4463
|
-
staleMs: Infinity,
|
|
4464
|
-
timeoutMs: LOCK_RETRY_MS,
|
|
4465
|
-
retry: {
|
|
4466
|
-
retries: 8,
|
|
4467
|
-
minTimeout: 1,
|
|
4468
|
-
maxTimeout: 2,
|
|
4469
|
-
factor: 1,
|
|
4470
|
-
randomize: false
|
|
4471
|
-
},
|
|
4472
|
-
staleRecovery: "remove-if-unchanged",
|
|
4473
|
-
payload: () => owner.payload,
|
|
4474
|
-
shouldReclaim: ({ payload }) => owner.hasExited(payload, false, signal),
|
|
4475
|
-
shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true, signal)
|
|
4476
|
-
});
|
|
4477
|
-
} catch (error) {
|
|
4478
|
-
if (error.code === "file_lock_timeout") return;
|
|
4479
|
-
throw error;
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4482
|
-
async function removeObservedLock(filePath, observed) {
|
|
4483
|
-
const current = await readLock(filePath);
|
|
4484
|
-
if (!current) return true;
|
|
4485
|
-
if (current.payload !== observed.payload || current.stat.mtimeNs !== observed.stat.mtimeNs || current.stat.size !== observed.stat.size) return false;
|
|
4486
|
-
await fs$1.unlink(filePath).catch((error) => {
|
|
4487
|
-
if (error.code !== "ENOENT") throw error;
|
|
4488
|
-
});
|
|
4489
|
-
return true;
|
|
4490
|
-
}
|
|
4491
|
-
async function recoverAbandonedLock(filePath, owner, signal) {
|
|
4492
|
-
const observed = await readLock(filePath);
|
|
4493
|
-
if (!observed) return true;
|
|
4494
|
-
const payload = parseLock(observed.payload);
|
|
4495
|
-
const pid = lockOwnerPid(payload);
|
|
4496
|
-
if (pid && (!await owner.hasExited(payload, false, signal) || !await owner.hasExited(payload, true, signal))) return false;
|
|
4497
|
-
if (!pid && Date.now() - Number(observed.stat.mtimeMs) <= INCOMPLETE_RESERVATION_GRACE_MS) return false;
|
|
4498
|
-
if (await removeObservedLock(filePath, observed)) {
|
|
4499
|
-
incrementPerfCounter("session.events.stale_lock_recovered");
|
|
4500
|
-
return true;
|
|
4501
|
-
}
|
|
4502
|
-
return false;
|
|
4503
|
-
}
|
|
4504
|
-
async function publishLock(filePath, payload, signal) {
|
|
4505
|
-
await withTempFile({
|
|
4506
|
-
rootDir: path.dirname(filePath),
|
|
4507
|
-
prefix: "session-turn",
|
|
4508
|
-
fileName: "lock"
|
|
4509
|
-
}, async (temporaryPath) => {
|
|
4510
|
-
await fs$1.writeFile(temporaryPath, payload, {
|
|
4511
|
-
encoding: "utf8",
|
|
4512
|
-
flag: "wx",
|
|
4513
|
-
mode: 384
|
|
4514
|
-
});
|
|
4515
|
-
signal?.throwIfAborted();
|
|
4516
|
-
try {
|
|
4517
|
-
await fs$1.link(temporaryPath, filePath);
|
|
4518
|
-
} catch (error) {
|
|
4519
|
-
if (!isHardlinkFallbackError(error)) throw error;
|
|
4520
|
-
await fs$1.writeFile(filePath, payload, {
|
|
4521
|
-
encoding: "utf8",
|
|
4522
|
-
flag: "wx",
|
|
4523
|
-
mode: 384
|
|
4524
|
-
});
|
|
4525
|
-
}
|
|
4526
|
-
});
|
|
4527
|
-
}
|
|
4528
|
-
async function removePublishedLock(filePath, payload) {
|
|
4529
|
-
const current = await readLock(filePath);
|
|
4530
|
-
if (current?.payload === payload) await removeObservedLock(filePath, current);
|
|
4531
|
-
}
|
|
4532
|
-
async function rejectAcquisition(filePath, receipt, error) {
|
|
4533
|
-
const failures = [error];
|
|
4534
|
-
await disposeTurn(filePath, receipt).catch((cleanupError) => {
|
|
4535
|
-
failures.push(...cleanupError instanceof AggregateError ? cleanupError.errors : [cleanupError]);
|
|
4536
|
-
});
|
|
4537
|
-
if (failures.length > 1) throw new AggregateError(failures, "Session turn acquisition and cleanup failed");
|
|
4538
|
-
throw error;
|
|
4539
|
-
}
|
|
4540
|
-
async function releaseTurnGuard(receipt) {
|
|
4541
|
-
await receipt.guard?.release();
|
|
4542
|
-
receipt.guard = void 0;
|
|
4543
|
-
}
|
|
4544
|
-
async function removeTurnMarker(filePath, receipt) {
|
|
4545
|
-
if (!receipt.removeMarker) return true;
|
|
4546
|
-
if (receipt.guard && !await receipt.guard.verifyStillHeld()) await releaseTurnGuard(receipt);
|
|
4547
|
-
receipt.guard ??= await tryAcquireGuard(filePath, receipt.owner);
|
|
4548
|
-
if (!receipt.guard) return false;
|
|
4549
|
-
await receipt.removeMarker();
|
|
4550
|
-
receipt.removeMarker = void 0;
|
|
4551
|
-
return true;
|
|
4552
|
-
}
|
|
4553
|
-
async function settleTurn(filePath, receipt) {
|
|
4554
|
-
if (receipt.settled) return true;
|
|
4555
|
-
const pending = receipt.pending ??= (async () => {
|
|
4556
|
-
receipt.failed = false;
|
|
4557
|
-
const failures = [];
|
|
4558
|
-
try {
|
|
4559
|
-
if (!await removeTurnMarker(filePath, receipt)) return false;
|
|
4560
|
-
} catch (error) {
|
|
4561
|
-
failures.push(error);
|
|
4562
|
-
}
|
|
4563
|
-
if (failures.length === 0 || !receipt.admitted) try {
|
|
4564
|
-
await releaseTurnGuard(receipt);
|
|
4565
|
-
} catch (error) {
|
|
4566
|
-
failures.push(error);
|
|
4567
|
-
}
|
|
4568
|
-
if (failures.length > 1) throw new AggregateError(failures, "Session turn cleanup failed");
|
|
4569
|
-
if (failures.length === 1) throw failures[0];
|
|
4570
|
-
receipt.settled = true;
|
|
4571
|
-
localTurns.delete(filePath);
|
|
4572
|
-
return true;
|
|
4573
|
-
})();
|
|
4574
|
-
try {
|
|
4575
|
-
return await pending;
|
|
4576
|
-
} finally {
|
|
4577
|
-
if (receipt.pending === pending) {
|
|
4578
|
-
receipt.pending = void 0;
|
|
4579
|
-
receipt.failed = !receipt.settled;
|
|
4580
|
-
}
|
|
4581
|
-
}
|
|
4582
|
-
}
|
|
4583
|
-
async function disposeTurn(filePath, receipt) {
|
|
4584
|
-
if (!await settleTurn(filePath, receipt)) throw new Error(`Session turn cleanup is waiting for its guard: ${filePath}`);
|
|
4585
|
-
}
|
|
4586
|
-
async function tryPublishLock(filePath, payload, owner, signal) {
|
|
4587
|
-
for (;;) try {
|
|
4588
|
-
await publishLock(filePath, payload, signal);
|
|
4589
|
-
return true;
|
|
4590
|
-
} catch (error) {
|
|
4591
|
-
if (error.code !== "EEXIST") throw error;
|
|
4592
|
-
if (!await recoverAbandonedLock(filePath, owner, signal)) return false;
|
|
4593
|
-
}
|
|
4594
|
-
}
|
|
4595
|
-
async function tryAcquireTurnCleanup(filePath, payload, owner, signal) {
|
|
4596
|
-
const previous = localTurns.get(filePath);
|
|
4597
|
-
if (previous) {
|
|
4598
|
-
if (previous.failed) await settleTurn(filePath, previous);
|
|
4599
|
-
return;
|
|
4600
|
-
}
|
|
4601
|
-
const receipt = {
|
|
4602
|
-
owner,
|
|
4603
|
-
removeMarker: async () => await removePublishedLock(filePath, payload),
|
|
4604
|
-
admitted: false,
|
|
4605
|
-
failed: false,
|
|
4606
|
-
settled: false
|
|
4607
|
-
};
|
|
4608
|
-
localTurns.set(filePath, receipt);
|
|
4609
|
-
try {
|
|
4610
|
-
receipt.guard = await tryAcquireGuard(filePath, owner, signal);
|
|
4611
|
-
} catch (error) {
|
|
4612
|
-
localTurns.delete(filePath);
|
|
4613
|
-
throw error;
|
|
4614
|
-
}
|
|
4615
|
-
if (!receipt.guard) {
|
|
4616
|
-
localTurns.delete(filePath);
|
|
4617
|
-
return;
|
|
4618
|
-
}
|
|
4619
|
-
return receipt;
|
|
4620
|
-
}
|
|
4621
|
-
async function tryAcquireTurn(filePath, payload, owner, signal) {
|
|
4622
|
-
const receipt = await tryAcquireTurnCleanup(filePath, payload, owner, signal);
|
|
4623
|
-
if (!receipt) return;
|
|
4624
|
-
try {
|
|
4625
|
-
signal?.throwIfAborted();
|
|
4626
|
-
if (!await tryPublishLock(filePath, payload, owner, signal)) {
|
|
4627
|
-
await disposeTurn(filePath, receipt);
|
|
4628
|
-
return;
|
|
4629
|
-
}
|
|
4630
|
-
const observed = await readLock(filePath);
|
|
4631
|
-
if (observed?.payload !== payload) throw new Error(`Session turn ownership changed before admission: ${filePath}`);
|
|
4632
|
-
signal?.throwIfAborted();
|
|
4633
|
-
receipt.admitted = true;
|
|
4634
|
-
receipt.removeMarker = async () => await removeObservedLock(filePath, observed);
|
|
4635
|
-
return { [Symbol.asyncDispose]: async () => await disposeTurn(filePath, receipt) };
|
|
4636
|
-
} catch (error) {
|
|
4637
|
-
return await rejectAcquisition(filePath, receipt, error);
|
|
4638
|
-
}
|
|
4639
|
-
}
|
|
4640
|
-
async function acquireSessionTurn(recordId, signal) {
|
|
4641
|
-
const requestedPath = sessionEventLockPath(recordId);
|
|
4642
|
-
await fs$1.mkdir(path.dirname(requestedPath), {
|
|
4643
|
-
recursive: true,
|
|
4644
|
-
mode: 448
|
|
4707
|
+
return applyAcceptedConfigOptions(next, response, {
|
|
4708
|
+
configId,
|
|
4709
|
+
value
|
|
4645
4710
|
});
|
|
4646
|
-
const filePath = path.join(await fs$1.realpath(path.dirname(requestedPath)), path.basename(requestedPath));
|
|
4647
|
-
const owner = await createLockOwner({ signal });
|
|
4648
|
-
lastCreatedAt = Math.max(Date.now(), lastCreatedAt + 1);
|
|
4649
|
-
const payload = `${JSON.stringify({
|
|
4650
|
-
...owner.payload,
|
|
4651
|
-
created_at: new Date(lastCreatedAt).toISOString()
|
|
4652
|
-
}, null, 2)}\n`;
|
|
4653
|
-
for (;;) {
|
|
4654
|
-
signal?.throwIfAborted();
|
|
4655
|
-
const turn = await tryAcquireTurn(filePath, payload, owner, signal);
|
|
4656
|
-
if (turn) return turn;
|
|
4657
|
-
await setTimeout$1(LOCK_RETRY_MS, void 0, { signal });
|
|
4658
|
-
}
|
|
4659
4711
|
}
|
|
4660
4712
|
//#endregion
|
|
4661
4713
|
//#region src/session/execution/direct-lifetime.ts
|
|
@@ -4757,10 +4809,7 @@ async function replayDesiredModel(params) {
|
|
|
4757
4809
|
const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models, params.replay.authority), params.timeoutMs);
|
|
4758
4810
|
params.replay.acknowledged = true;
|
|
4759
4811
|
params.record.acpx = applyModelSelection(params.record.acpx, params.desiredModelId, response);
|
|
4760
|
-
const models =
|
|
4761
|
-
...params.models,
|
|
4762
|
-
currentModelId: params.desiredModelId
|
|
4763
|
-
};
|
|
4812
|
+
const models = advertisedModelState(params.record.acpx);
|
|
4764
4813
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
4765
4814
|
return {
|
|
4766
4815
|
replayed: true,
|
|
@@ -4788,10 +4837,10 @@ async function replayDesiredConfigOptions(params) {
|
|
|
4788
4837
|
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value, advertisedModelState(params.record.acpx), params.replay.authority), params.timeoutMs);
|
|
4789
4838
|
params.replay.acknowledged = true;
|
|
4790
4839
|
params.record.acpx = applyConfigOptionSelection(params.record.acpx, configId, value, response);
|
|
4791
|
-
acceptedConfigOptions =
|
|
4840
|
+
acceptedConfigOptions = params.record.acpx.config_options;
|
|
4792
4841
|
result = {
|
|
4793
4842
|
replayed: true,
|
|
4794
|
-
models:
|
|
4843
|
+
models: advertisedModelState(params.record.acpx)
|
|
4795
4844
|
};
|
|
4796
4845
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
4797
4846
|
} catch (error) {
|
|
@@ -4823,7 +4872,7 @@ async function connectAndLoadSession(options) {
|
|
|
4823
4872
|
logReconnectAttempt(record, storedProcessAlive, Boolean(record.pid) && !storedProcessAlive, options.verbose);
|
|
4824
4873
|
const reusingLoadedSession = client.hasReusableSession(record.acpSessionId);
|
|
4825
4874
|
if (reusingLoadedSession) incrementPerfCounter("runtime.connect_and_load.reused_session");
|
|
4826
|
-
else await withTimeout(client.start(options.authority), options.timeoutMs);
|
|
4875
|
+
else await withTimeout(client.start(options.authority, { sessionOptions: mergeSessionOptions(options.sessionOptions, sessionOptionsFromRecord(record)) }), options.timeoutMs);
|
|
4827
4876
|
assertControlAuthority(options.authority);
|
|
4828
4877
|
options.onClientAvailable?.(options.activeController);
|
|
4829
4878
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
@@ -5577,7 +5626,7 @@ async function createSessionRecordWithClient(client, options) {
|
|
|
5577
5626
|
};
|
|
5578
5627
|
persistSessionOptions(record, options.sessionOptions);
|
|
5579
5628
|
applyConfigOptionsToRecord(record, createdState.sessionResult);
|
|
5580
|
-
applyInitialModelSelection(record, createdState.sessionResult.models,
|
|
5629
|
+
applyInitialModelSelection(record, createdState.sessionResult.models, createdState.modelApplication);
|
|
5581
5630
|
await writeSessionRecord(record);
|
|
5582
5631
|
assertControlAuthority({ signal: options.signal });
|
|
5583
5632
|
return record;
|
|
@@ -5632,6 +5681,14 @@ async function resumeSessionRecordWithClient(client, options, cwd) {
|
|
|
5632
5681
|
}
|
|
5633
5682
|
async function createSessionWithClient(options) {
|
|
5634
5683
|
assertControlAuthority({ signal: options.signal });
|
|
5684
|
+
if (options.resumeSessionId) {
|
|
5685
|
+
const resumedRecord = await readSessionRecord(options.resumeSessionId);
|
|
5686
|
+
assertControlAuthority({ signal: options.signal });
|
|
5687
|
+
if (resumedRecord && !resumedRecord.closed && resumedRecord.agentCommand === options.agentCommand) {
|
|
5688
|
+
await closeSession(resumedRecord.acpxRecordId);
|
|
5689
|
+
assertControlAuthority({ signal: options.signal });
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5635
5692
|
const client = new AcpClient({
|
|
5636
5693
|
agentCommand: options.agentCommand,
|
|
5637
5694
|
agentArgv: options.agentArgv,
|
|
@@ -5719,11 +5776,11 @@ async function listAgentSessions(options) {
|
|
|
5719
5776
|
}
|
|
5720
5777
|
async function ensureSession(options) {
|
|
5721
5778
|
const cwd = absolutePath(options.cwd);
|
|
5722
|
-
const ownership = await
|
|
5723
|
-
options.agentCommand,
|
|
5779
|
+
const ownership = await acquireSessionScope({
|
|
5780
|
+
agentCommand: options.agentCommand,
|
|
5724
5781
|
cwd,
|
|
5725
|
-
normalizeName(options.name)
|
|
5726
|
-
|
|
5782
|
+
name: normalizeName(options.name)
|
|
5783
|
+
}, options.signal);
|
|
5727
5784
|
try {
|
|
5728
5785
|
return await ensureSessionWithOwnership(options, cwd);
|
|
5729
5786
|
} finally {
|
|
@@ -6503,7 +6560,12 @@ var LiveSessionCheckpoint = class {
|
|
|
6503
6560
|
async flushDirty() {
|
|
6504
6561
|
while (this.dirty) {
|
|
6505
6562
|
this.dirty = false;
|
|
6506
|
-
|
|
6563
|
+
try {
|
|
6564
|
+
await this.save();
|
|
6565
|
+
} catch (error) {
|
|
6566
|
+
this.dirty = true;
|
|
6567
|
+
throw error;
|
|
6568
|
+
}
|
|
6507
6569
|
}
|
|
6508
6570
|
}
|
|
6509
6571
|
};
|
|
@@ -6557,8 +6619,8 @@ var QueueTaskOutputFormatter = class {
|
|
|
6557
6619
|
flush() {}
|
|
6558
6620
|
};
|
|
6559
6621
|
function markOutputAlreadyEmitted(error, outputAlreadyEmitted) {
|
|
6560
|
-
if (!
|
|
6561
|
-
error.outputAlreadyEmitted =
|
|
6622
|
+
if (!error || typeof error !== "object") return;
|
|
6623
|
+
error.outputAlreadyEmitted = outputAlreadyEmitted;
|
|
6562
6624
|
}
|
|
6563
6625
|
async function runWithOptionalInterrupt(options) {
|
|
6564
6626
|
if (options.handleProcessInterrupts === false) return await options.run();
|
|
@@ -6601,11 +6663,17 @@ var AcpErrorTracker = class {
|
|
|
6601
6663
|
return this.latestInbound ?? this.outbound.findLast((acp) => outboundAcpErrorMatches(error, acp));
|
|
6602
6664
|
}
|
|
6603
6665
|
};
|
|
6604
|
-
function toPromptResult(stopReason, sessionId, client, meta) {
|
|
6666
|
+
function toPromptResult(stopReason, sessionId, client, before, meta) {
|
|
6667
|
+
const after = client.getPermissionStats();
|
|
6605
6668
|
return {
|
|
6606
6669
|
stopReason,
|
|
6607
6670
|
sessionId,
|
|
6608
|
-
permissionStats:
|
|
6671
|
+
permissionStats: {
|
|
6672
|
+
requested: after.requested - before.requested,
|
|
6673
|
+
approved: after.approved - before.approved,
|
|
6674
|
+
denied: after.denied - before.denied,
|
|
6675
|
+
cancelled: after.cancelled - before.cancelled
|
|
6676
|
+
},
|
|
6609
6677
|
...meta === void 0 ? {} : { _meta: meta }
|
|
6610
6678
|
};
|
|
6611
6679
|
}
|
|
@@ -6627,60 +6695,37 @@ async function applyPromptModelIfAdvertised(params) {
|
|
|
6627
6695
|
});
|
|
6628
6696
|
if (result.applied) params.record.acpx = applyModelSelection(params.record.acpx, requestedModel, result.response);
|
|
6629
6697
|
}
|
|
6630
|
-
function jsonRpcIdKey(
|
|
6698
|
+
function jsonRpcIdKey(message) {
|
|
6699
|
+
if (!("id" in message)) return;
|
|
6700
|
+
const value = message.id;
|
|
6631
6701
|
if (typeof value === "string") return `s:${value}`;
|
|
6632
6702
|
if (typeof value === "number" && Number.isFinite(value)) return `n:${value}`;
|
|
6633
6703
|
}
|
|
6634
|
-
function extractJsonRpcRequestInfo(message) {
|
|
6635
|
-
const candidate = message;
|
|
6636
|
-
if (typeof candidate.method !== "string") return;
|
|
6637
|
-
const idKey = jsonRpcIdKey(candidate.id);
|
|
6638
|
-
if (!idKey) return;
|
|
6639
|
-
return {
|
|
6640
|
-
idKey,
|
|
6641
|
-
method: candidate.method
|
|
6642
|
-
};
|
|
6643
|
-
}
|
|
6644
|
-
function extractJsonRpcResponseInfo(message) {
|
|
6645
|
-
const candidate = message;
|
|
6646
|
-
const idKey = jsonRpcIdKey(candidate.id);
|
|
6647
|
-
if (!idKey) return;
|
|
6648
|
-
const hasError = Object.hasOwn(candidate, "error");
|
|
6649
|
-
const hasResult = Object.hasOwn(candidate, "result");
|
|
6650
|
-
if (!hasError && !hasResult) return;
|
|
6651
|
-
return {
|
|
6652
|
-
idKey,
|
|
6653
|
-
hasError
|
|
6654
|
-
};
|
|
6655
|
-
}
|
|
6656
6704
|
const SESSION_RECONNECT_METHODS = /* @__PURE__ */ new Set(["session/load", "session/resume"]);
|
|
6657
|
-
function
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
if (request) {
|
|
6663
|
-
requestMethodById.set(request.idKey, request.method);
|
|
6664
|
-
continue;
|
|
6665
|
-
}
|
|
6666
|
-
const response = extractJsonRpcResponseInfo(message);
|
|
6667
|
-
if (!response || !response.hasError) continue;
|
|
6668
|
-
const requestMethod = requestMethodById.get(response.idKey);
|
|
6669
|
-
if (requestMethod && SESSION_RECONNECT_METHODS.has(requestMethod)) failedLoadRequestIds.add(response.idKey);
|
|
6670
|
-
}
|
|
6671
|
-
if (failedLoadRequestIds.size === 0) return messages;
|
|
6672
|
-
return messages.filter((message) => {
|
|
6673
|
-
const request = extractJsonRpcRequestInfo(message);
|
|
6674
|
-
if (request && SESSION_RECONNECT_METHODS.has(request.method) && failedLoadRequestIds.has(request.idKey)) return false;
|
|
6675
|
-
const response = extractJsonRpcResponseInfo(message);
|
|
6676
|
-
if (response && failedLoadRequestIds.has(response.idKey)) return false;
|
|
6677
|
-
return true;
|
|
6678
|
-
});
|
|
6705
|
+
function isReconnectRequest(direction, message) {
|
|
6706
|
+
return direction === "outbound" && "method" in message && typeof message.method === "string" && SESSION_RECONNECT_METHODS.has(message.method);
|
|
6707
|
+
}
|
|
6708
|
+
function isInboundResponse(direction, message) {
|
|
6709
|
+
return direction === "inbound" && ("result" in message || "error" in message);
|
|
6679
6710
|
}
|
|
6680
6711
|
function filterBufferedConnectOutput(messages, loadError) {
|
|
6681
6712
|
if (loadError == null) return messages;
|
|
6682
|
-
const
|
|
6683
|
-
|
|
6713
|
+
const pending = /* @__PURE__ */ new Map();
|
|
6714
|
+
const suppressed = /* @__PURE__ */ new Set();
|
|
6715
|
+
for (const [index, { direction, message }] of messages.entries()) {
|
|
6716
|
+
const id = jsonRpcIdKey(message);
|
|
6717
|
+
if (!id) continue;
|
|
6718
|
+
if (isReconnectRequest(direction, message)) pending.set(id, index);
|
|
6719
|
+
else if (isInboundResponse(direction, message)) {
|
|
6720
|
+
const requestIndex = pending.get(id);
|
|
6721
|
+
if (requestIndex !== void 0 && "error" in message) {
|
|
6722
|
+
suppressed.add(requestIndex);
|
|
6723
|
+
suppressed.add(index);
|
|
6724
|
+
}
|
|
6725
|
+
pending.delete(id);
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
return messages.filter((_message, index) => !suppressed.has(index));
|
|
6684
6729
|
}
|
|
6685
6730
|
function emitPromptRetryNotice(params) {
|
|
6686
6731
|
if (params.suppressSdkConsoleErrors) return;
|
|
@@ -6744,7 +6789,7 @@ function buildQueuedTaskRunOptions(sessionRecordId, task, options, outputFormatt
|
|
|
6744
6789
|
suppressSdkConsoleErrors: task.suppressSdkConsoleErrors ?? options.suppressSdkConsoleErrors,
|
|
6745
6790
|
verbose: options.verbose,
|
|
6746
6791
|
promptRetries: task.promptRetries ?? options.promptRetries ?? 0,
|
|
6747
|
-
sessionOptions:
|
|
6792
|
+
sessionOptions: task.sessionOptions,
|
|
6748
6793
|
onClientAvailable: options.onClientAvailable,
|
|
6749
6794
|
onClientClosed: options.onClientClosed,
|
|
6750
6795
|
onClientCloseFailure: options.onClientCloseFailure,
|
|
@@ -7016,6 +7061,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7016
7061
|
verbose: options.verbose,
|
|
7017
7062
|
sessionOptions
|
|
7018
7063
|
});
|
|
7064
|
+
const permissionStatsBefore = client.getPermissionStats();
|
|
7019
7065
|
client.updateRuntimeOptions({
|
|
7020
7066
|
permissionMode: options.permissionMode,
|
|
7021
7067
|
nonInteractivePermissions: options.nonInteractivePermissions,
|
|
@@ -7094,6 +7140,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7094
7140
|
client,
|
|
7095
7141
|
record,
|
|
7096
7142
|
resumePolicy: options.resumePolicy,
|
|
7143
|
+
sessionOptions: options.sessionOptions,
|
|
7097
7144
|
replacingConfigOption: requestedModelId(options.sessionOptions?.model) ? { key: "model" } : void 0,
|
|
7098
7145
|
timeoutMs: options.timeoutMs,
|
|
7099
7146
|
verbose: options.verbose,
|
|
@@ -7171,7 +7218,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7171
7218
|
applyConversation(record, conversation);
|
|
7172
7219
|
const propagated = error instanceof Error ? error : new Error(formatErrorMessage(error));
|
|
7173
7220
|
attachAcpErrorPayload(propagated, normalizedError.acp);
|
|
7174
|
-
propagated
|
|
7221
|
+
markOutputAlreadyEmitted(propagated, matchedAcpError !== void 0 && shouldMarkAcpErrorsEmitted);
|
|
7175
7222
|
propagated.normalizedOutputError = normalizedError;
|
|
7176
7223
|
throw propagated;
|
|
7177
7224
|
};
|
|
@@ -7224,7 +7271,7 @@ async function runOwnedSessionPrompt(options) {
|
|
|
7224
7271
|
const response = await savePromptSuccess(await runPromptWithRetries(activeSessionId));
|
|
7225
7272
|
promptTurnActive = false;
|
|
7226
7273
|
return {
|
|
7227
|
-
...toPromptResult(response.stopReason, record.acpxRecordId, client, response._meta),
|
|
7274
|
+
...toPromptResult(response.stopReason, record.acpxRecordId, client, permissionStatsBefore, response._meta),
|
|
7228
7275
|
record,
|
|
7229
7276
|
resumed,
|
|
7230
7277
|
loadError
|
|
@@ -7344,6 +7391,7 @@ async function runOnce(options, control) {
|
|
|
7344
7391
|
},
|
|
7345
7392
|
sessionOptions: options.sessionOptions
|
|
7346
7393
|
});
|
|
7394
|
+
const permissionStatsBefore = client.getPermissionStats();
|
|
7347
7395
|
const closeOwnedClient = ownDirectClient(client, control?.signal);
|
|
7348
7396
|
const runExecPromptAttempt = async (sessionId) => {
|
|
7349
7397
|
assertControlAuthority(authority);
|
|
@@ -7389,17 +7437,17 @@ async function runOnce(options, control) {
|
|
|
7389
7437
|
authority,
|
|
7390
7438
|
onWarning: options.suppressSdkConsoleErrors ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
|
|
7391
7439
|
});
|
|
7392
|
-
if (modelApplication.
|
|
7440
|
+
if (modelApplication.applied) controlState = applyModelSelection(controlState, modelApplication.modelId, modelApplication.response);
|
|
7393
7441
|
for (const configOption of options.configOptions ?? []) {
|
|
7394
7442
|
assertControlAuthority(authority);
|
|
7395
7443
|
const response = await withTimeout(client.setSessionConfigOption(sessionId, configOption.configId, configOption.value, advertisedModelState(controlState), authority), options.timeoutMs);
|
|
7396
|
-
controlState =
|
|
7444
|
+
controlState = applyConfigOptionSelection(controlState, configOption.configId, configOption.value, response);
|
|
7397
7445
|
}
|
|
7398
7446
|
assertControlAuthority(authority);
|
|
7399
7447
|
output.setContext({ sessionId });
|
|
7400
7448
|
const response = await runExecPromptWithRetries(sessionId);
|
|
7401
7449
|
promptTurnActive = false;
|
|
7402
|
-
return toPromptResult(response.stopReason, sessionId, client, response._meta);
|
|
7450
|
+
return toPromptResult(response.stopReason, sessionId, client, permissionStatsBefore, response._meta);
|
|
7403
7451
|
},
|
|
7404
7452
|
handleInterrupt: async () => {
|
|
7405
7453
|
await client.cancelActivePrompt(INTERRUPT_CANCEL_WAIT_MS);
|
|
@@ -7626,31 +7674,50 @@ function createLeaseHeartbeat(lease, shutdown) {
|
|
|
7626
7674
|
};
|
|
7627
7675
|
}
|
|
7628
7676
|
async function runSessionQueueOwner(options) {
|
|
7629
|
-
const
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
let
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
}
|
|
7677
|
+
const signals = [
|
|
7678
|
+
"SIGINT",
|
|
7679
|
+
"SIGTERM",
|
|
7680
|
+
"SIGHUP"
|
|
7681
|
+
];
|
|
7682
|
+
let stopRequested = false;
|
|
7683
|
+
let requestRuntimeStop;
|
|
7684
|
+
const onSignal = () => {
|
|
7685
|
+
stopRequested = true;
|
|
7686
|
+
requestRuntimeStop?.();
|
|
7687
|
+
};
|
|
7640
7688
|
try {
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7689
|
+
for (const signal of signals) process.on(signal, onSignal);
|
|
7690
|
+
const lease = await tryAcquireQueueOwnerLease(options.sessionId, {
|
|
7691
|
+
path: options.mcpConfigPath,
|
|
7692
|
+
fingerprint: options.mcpConfigFingerprint
|
|
7693
|
+
});
|
|
7694
|
+
if (!lease) return;
|
|
7695
|
+
let runtimeFailure;
|
|
7696
|
+
try {
|
|
7697
|
+
await runQueueOwnerRuntime(options, lease, (requestStop) => {
|
|
7698
|
+
requestRuntimeStop = requestStop;
|
|
7699
|
+
if (stopRequested) requestStop();
|
|
7700
|
+
});
|
|
7701
|
+
} catch (error) {
|
|
7702
|
+
runtimeFailure = { error };
|
|
7703
|
+
}
|
|
7704
|
+
try {
|
|
7705
|
+
await releaseQueueOwnerLease(lease);
|
|
7706
|
+
} catch (error) {
|
|
7707
|
+
if (runtimeFailure) throw new AggregateError([runtimeFailure.error, error], "Queue owner shutdown failed", { cause: error });
|
|
7708
|
+
throw error;
|
|
7709
|
+
}
|
|
7710
|
+
if (runtimeFailure) throw runtimeFailure.error;
|
|
7711
|
+
} finally {
|
|
7712
|
+
for (const signal of signals) process.off(signal, onSignal);
|
|
7645
7713
|
}
|
|
7646
|
-
if (runtimeFailure) throw runtimeFailure.error;
|
|
7647
7714
|
}
|
|
7648
7715
|
async function keepOwnerForPendingControls(controls, shutdown) {
|
|
7649
7716
|
if (shutdown.requested || !controls.hasPending) return false;
|
|
7650
7717
|
await controls.drainAdmitted();
|
|
7651
7718
|
return !shutdown.requested;
|
|
7652
7719
|
}
|
|
7653
|
-
async function runQueueOwnerRuntime(options, lease) {
|
|
7720
|
+
async function runQueueOwnerRuntime(options, lease, bindStopRequest) {
|
|
7654
7721
|
const sessionRecord = await resolveSessionRecord(options.sessionId);
|
|
7655
7722
|
let owner;
|
|
7656
7723
|
let heartbeatTimer;
|
|
@@ -7724,13 +7791,8 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7724
7791
|
sessionId: options.sessionId,
|
|
7725
7792
|
verbose: options.verbose
|
|
7726
7793
|
});
|
|
7727
|
-
|
|
7728
|
-
shutdown.request();
|
|
7729
|
-
};
|
|
7794
|
+
bindStopRequest(shutdown.request);
|
|
7730
7795
|
const heartbeat = createLeaseHeartbeat(lease, shutdown);
|
|
7731
|
-
process.once("SIGINT", onSignal);
|
|
7732
|
-
process.once("SIGTERM", onSignal);
|
|
7733
|
-
process.once("SIGHUP", onSignal);
|
|
7734
7796
|
try {
|
|
7735
7797
|
owner = await SessionQueueOwner.start(lease, {
|
|
7736
7798
|
cancelPrompt: async () => {
|
|
@@ -7812,9 +7874,6 @@ async function runQueueOwnerRuntime(options, lease) {
|
|
|
7812
7874
|
}
|
|
7813
7875
|
}
|
|
7814
7876
|
} finally {
|
|
7815
|
-
process.off("SIGINT", onSignal);
|
|
7816
|
-
process.off("SIGTERM", onSignal);
|
|
7817
|
-
process.off("SIGHUP", onSignal);
|
|
7818
7877
|
await shutdown.shutdown();
|
|
7819
7878
|
await lease.updates;
|
|
7820
7879
|
}
|
|
@@ -7858,6 +7917,6 @@ async function sendSession(options) {
|
|
|
7858
7917
|
}
|
|
7859
7918
|
const queueOwnerRuntimeTestInternals = { queueOwnerExitIsFatal };
|
|
7860
7919
|
//#endregion
|
|
7861
|
-
export { isRequestedModelUnsupportedError as $, applyInitialModelSelection as A, reconcileAgentSessionId as B, setSessionModeOnOwner as C, connectAndLoadSession as D, withConnectedSession as E, persistSessionOptions as F, recordPromptSubmission as G, cloneSessionConversation as H, sessionOptionsFromRecord as I, advertisedModelState as J, recordSessionUpdate as K, applyConversation as L, applyRequestedModelIfAdvertised as M, setDesiredModeId as N, applyConfigOptionSelection as O, normalizeSessionOptionShape as P, RequestedModelUnsupportedError as Q, applyLifecycleSnapshotToRecord as R, setSessionMode as S, setSessionModelOnOwner as T, createSessionConversation as U, cloneSessionAcpxState as V, recordClientOperation as W, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as X, AcpClient as Y, REQUESTED_MODEL_UNSUPPORTED_REASONS as Z, cancelSessionPrompt as _, runQueuedTask as a,
|
|
7920
|
+
export { isRequestedModelUnsupportedError as $, applyInitialModelSelection as A, reconcileAgentSessionId as B, setSessionModeOnOwner as C, connectAndLoadSession as D, withConnectedSession as E, persistSessionOptions as F, recordPromptSubmission as G, cloneSessionConversation as H, sessionOptionsFromRecord as I, advertisedModelState as J, recordSessionUpdate as K, applyConversation as L, applyRequestedModelIfAdvertised as M, setDesiredModeId as N, applyConfigOptionSelection as O, normalizeSessionOptionShape as P, RequestedModelUnsupportedError as Q, applyLifecycleSnapshotToRecord as R, setSessionMode as S, setSessionModelOnOwner as T, createSessionConversation as U, cloneSessionAcpxState as V, recordClientOperation as W, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as X, AcpClient as Y, REQUESTED_MODEL_UNSUPPORTED_REASONS as Z, cancelSessionPrompt as _, runQueuedTask as a, acquireSessionScope as at, setSessionConfigOption as b, LiveSessionCheckpoint as c, InterruptedError as ct, flushPerfMetricsCapture as d, withInterrupt as dt, getAcpxVersion as et, installPerfMetricsCapture as f, withTimeout as ft, listAgentSessions as g, ensureSession as h, runOnce as i, acquireSessionImport as it, applyModelSelection as j, applyConfigOptionsToRecord as k, runPromptTurn as l, TimeoutError as lt, createSessionWithClient as m, runSessionQueueOwner as n, DEFAULT_QUEUE_OWNER_TTL_MS as nt, sendSessionDirect as o, listSessionEvents as ot, createSession as p, trimConversationForRuntime as q, sendSession as r, normalizeQueueOwnerTtlMs as rt, DISCARD_OUTPUT_FORMATTER as s, isLegacyZedCodexAcpInvocation as st, queueOwnerRuntimeTestInternals as t, permissionModeSatisfies as tt, buildQueueOwnerArgOverride as u, assertControlAuthority as ut, closeSession as v, setSessionModel as w, setSessionConfigOptionOnOwner as x, sessionControlTestInternals as y, createInitialSessionRecord as z };
|
|
7862
7921
|
|
|
7863
|
-
//# sourceMappingURL=queue-owner-runtime-
|
|
7922
|
+
//# sourceMappingURL=queue-owner-runtime-Dhr4gxNE.js.map
|