@codexhost/cli-darwin-x64 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.txt +1 -1
- package/app/codexhost-distribution.json +1 -1
- package/app/console-server.mjs +88 -44
- package/app/console-web.js +3681 -3948
- package/app/desktop-controller.mjs +8 -1
- package/app/host-runtime.mjs +927 -236
- package/app/plugins/antigravity/plugin.mjs +8 -1
- package/app/plugins/claude-code/plugin.mjs +33031 -22328
- package/app/plugins/codebuddy/plugin.mjs +47 -4
- package/app/plugins/codex-usage/plugin.mjs +657 -27
- package/app/plugins/cursor-cli/plugin.mjs +8 -1
- package/app/plugins/deepseek-harness/plugin.mjs +28 -3
- package/app/plugins/grok/plugin.mjs +43 -8
- package/app/plugins/hermes/plugin.mjs +192 -41
- package/app/plugins/kimi-code/plugin.mjs +8 -1
- package/app/plugins/kiro-cli/plugin.mjs +8 -1
- package/app/plugins/omp/plugin.mjs +31 -7
- package/app/plugins/opencode/plugin.mjs +34 -4
- package/app/plugins/pi/plugin.mjs +84 -43
- package/app/plugins/qoder/plugin.mjs +54 -4
- package/app/plugins/qoder-cn/plugin.mjs +54 -4
- package/app/plugins/workbuddy/plugin.mjs +47 -4
- package/app/plugins/zcode/plugin.mjs +37 -4
- package/app/renderer-extension.js +11574 -7264
- package/bin/codexhost +0 -0
- package/libexec/codexhost-shim +0 -0
- package/libexec/codexhost-updater +0 -0
- package/package.json +1 -1
package/app/host-runtime.mjs
CHANGED
|
@@ -4295,9 +4295,11 @@ async function runDelegationCli(input) {
|
|
|
4295
4295
|
"INVALID_ARGUMENT",
|
|
4296
4296
|
"--host conflicts with the Thread reference"
|
|
4297
4297
|
);
|
|
4298
|
+
const caller = command === "read" ? callerThreadId(environment) : void 0;
|
|
4298
4299
|
const body = {
|
|
4299
4300
|
...reference,
|
|
4300
4301
|
...hostId2 ? { hostId: hostId2 } : {},
|
|
4302
|
+
...caller ? { callerThreadId: normalizeThreadId(caller) } : {},
|
|
4301
4303
|
view,
|
|
4302
4304
|
...value(parsed2, "--cursor") ? { cursor: value(parsed2, "--cursor") } : {},
|
|
4303
4305
|
...value(parsed2, "--limit") ? { limit: positiveInteger(value(parsed2, "--limit"), "--limit", MAX_LIMIT) } : {},
|
|
@@ -4434,6 +4436,7 @@ import {
|
|
|
4434
4436
|
realpath,
|
|
4435
4437
|
stat,
|
|
4436
4438
|
readdir as readdir2,
|
|
4439
|
+
unlink,
|
|
4437
4440
|
writeFile as writeFile3
|
|
4438
4441
|
} from "node:fs/promises";
|
|
4439
4442
|
import { homedir } from "node:os";
|
|
@@ -4894,39 +4897,25 @@ async function regularFile(filePath) {
|
|
|
4894
4897
|
throw error51;
|
|
4895
4898
|
}
|
|
4896
4899
|
}
|
|
4897
|
-
async function
|
|
4900
|
+
async function discoverActiveUpdateStatus(stateDirectory) {
|
|
4898
4901
|
if (!path4.isAbsolute(stateDirectory))
|
|
4899
4902
|
throw new Error("update state directory must be absolute");
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
if (!path4.isAbsolute(stateDirectory))
|
|
4904
|
-
throw new Error("update state directory must be absolute");
|
|
4905
|
-
let entries;
|
|
4903
|
+
const lockPath = path4.join(stateDirectory, LOCK_FILE);
|
|
4904
|
+
if (!await regularFile(lockPath))
|
|
4905
|
+
return null;
|
|
4906
4906
|
try {
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
if (error51.code === "ENOENT")
|
|
4907
|
+
const lock = JSON.parse(await readFile3(lockPath, "utf8"));
|
|
4908
|
+
if (typeof lock.statusPath !== "string" || !path4.isAbsolute(lock.statusPath))
|
|
4910
4909
|
return null;
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
continue;
|
|
4920
|
-
try {
|
|
4921
|
-
candidates.push({
|
|
4922
|
-
statusPath,
|
|
4923
|
-
status: parseUpdateStatus(JSON.parse(await readFile3(statusPath, "utf8")))
|
|
4924
|
-
});
|
|
4925
|
-
} catch {
|
|
4926
|
-
}
|
|
4910
|
+
if (!await regularFile(lock.statusPath))
|
|
4911
|
+
return null;
|
|
4912
|
+
const status2 = parseUpdateStatus(JSON.parse(await readFile3(lock.statusPath, "utf8")));
|
|
4913
|
+
if (TERMINAL_PHASES.has(status2.phase))
|
|
4914
|
+
return null;
|
|
4915
|
+
return { statusPath: lock.statusPath, status: status2 };
|
|
4916
|
+
} catch {
|
|
4917
|
+
return null;
|
|
4927
4918
|
}
|
|
4928
|
-
candidates.sort((left, right) => right.status.updatedAt - left.status.updatedAt);
|
|
4929
|
-
return candidates[0] ?? null;
|
|
4930
4919
|
}
|
|
4931
4920
|
async function cleanupTerminalUpdateState(stateDirectory, options2 = {}) {
|
|
4932
4921
|
const now = Math.floor((options2.now ?? Date.now()) / 1e3);
|
|
@@ -5124,6 +5113,48 @@ async function verifyDownloadedArtifact(source, filePath, result) {
|
|
|
5124
5113
|
}
|
|
5125
5114
|
}
|
|
5126
5115
|
|
|
5116
|
+
// packages/update-manager/dist/updater-ready.js
|
|
5117
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
5118
|
+
async function waitForUpdaterReady(child, readStatus, timeoutMs = 1e4) {
|
|
5119
|
+
let failure;
|
|
5120
|
+
const onError = (error51) => {
|
|
5121
|
+
failure = error51;
|
|
5122
|
+
};
|
|
5123
|
+
const onExit = () => {
|
|
5124
|
+
failure = new Error("Background Updater exited before completing handoff");
|
|
5125
|
+
};
|
|
5126
|
+
child.on("error", onError);
|
|
5127
|
+
child.on("exit", onExit);
|
|
5128
|
+
const deadline = performance.now() + timeoutMs;
|
|
5129
|
+
try {
|
|
5130
|
+
while (true) {
|
|
5131
|
+
const status2 = await readStatus();
|
|
5132
|
+
if (status2?.phase === "failed")
|
|
5133
|
+
throw new Error(status2.error ?? "Background Updater failed");
|
|
5134
|
+
if (failure)
|
|
5135
|
+
throw failure;
|
|
5136
|
+
if (child.exitCode != null || child.signalCode != null) {
|
|
5137
|
+
throw new Error("Background Updater exited before completing handoff");
|
|
5138
|
+
}
|
|
5139
|
+
if (status2?.phase === "waiting-for-exit")
|
|
5140
|
+
return;
|
|
5141
|
+
if (status2 && status2.phase !== "prepared") {
|
|
5142
|
+
throw new Error(`Unexpected Updater phase during handoff: ${status2.phase}`);
|
|
5143
|
+
}
|
|
5144
|
+
if (performance.now() >= deadline) {
|
|
5145
|
+
throw new Error("Background Updater did not confirm handoff before the startup timeout");
|
|
5146
|
+
}
|
|
5147
|
+
await delay(20);
|
|
5148
|
+
}
|
|
5149
|
+
} catch (error51) {
|
|
5150
|
+
child.kill();
|
|
5151
|
+
throw error51;
|
|
5152
|
+
} finally {
|
|
5153
|
+
child.removeListener("error", onError);
|
|
5154
|
+
child.removeListener("exit", onExit);
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
5157
|
+
|
|
5127
5158
|
// packages/update-manager/dist/update-manager.js
|
|
5128
5159
|
var REQUEST_SCHEMA_VERSION = 1;
|
|
5129
5160
|
var WINDOWS_RENAME_RETRY_DELAYS_MS = [10, 30, 70, 150, 300];
|
|
@@ -5133,7 +5164,7 @@ function errorMessage(error51) {
|
|
|
5133
5164
|
function systemErrorCode(error51) {
|
|
5134
5165
|
return typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : null;
|
|
5135
5166
|
}
|
|
5136
|
-
function
|
|
5167
|
+
function delay2(milliseconds) {
|
|
5137
5168
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
5138
5169
|
}
|
|
5139
5170
|
async function replaceStatusFile(temporaryPath, statusPath) {
|
|
@@ -5149,7 +5180,7 @@ async function replaceStatusFile(temporaryPath, statusPath) {
|
|
|
5149
5180
|
throw error51;
|
|
5150
5181
|
}
|
|
5151
5182
|
retryIndex += 1;
|
|
5152
|
-
await
|
|
5183
|
+
await delay2(retryDelay);
|
|
5153
5184
|
}
|
|
5154
5185
|
}
|
|
5155
5186
|
}
|
|
@@ -5369,14 +5400,20 @@ function createBackgroundUpdateManager(dependencies2 = {}) {
|
|
|
5369
5400
|
app_path: appPath
|
|
5370
5401
|
}, artifact.artifactPath);
|
|
5371
5402
|
},
|
|
5372
|
-
start(prepared) {
|
|
5403
|
+
async start(prepared) {
|
|
5373
5404
|
if (!preparedRequests.delete(prepared.requestPath)) {
|
|
5374
5405
|
throw new Error("background update was not prepared by this manager or was already started");
|
|
5375
5406
|
}
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5407
|
+
try {
|
|
5408
|
+
const child = spawnUpdater(prepared.helperPath, prepared.requestPath);
|
|
5409
|
+
await waitForUpdaterReady(child, () => this.readStatus(prepared.statusPath));
|
|
5410
|
+
if (child.pid === void 0)
|
|
5411
|
+
throw new Error("background Updater did not report a process ID");
|
|
5412
|
+
return Object.freeze({ ...prepared, updaterPid: child.pid });
|
|
5413
|
+
} catch (error51) {
|
|
5414
|
+
await writeFailedStatus(prepared.statusPath, prepared.version, prepared.installation, error51);
|
|
5415
|
+
throw error51;
|
|
5416
|
+
}
|
|
5380
5417
|
},
|
|
5381
5418
|
async readStatus(statusPathValue) {
|
|
5382
5419
|
const statusPath = requireAbsolutePath(statusPathValue, "update status path");
|
|
@@ -19909,6 +19946,7 @@ config(en_default());
|
|
|
19909
19946
|
var REMOTE_THREAD_REPLY_MAX_BYTES = 16 * 1024 * 1024;
|
|
19910
19947
|
var REMOTE_THREAD_CONTROL_TIMEOUT_MS = 4e4;
|
|
19911
19948
|
var DELEGATION_READ_METHOD = "codexhost/thread/delegation-read";
|
|
19949
|
+
var THREAD_REFERENCE_SOURCE_HOST_PARAM = "codexhostSourceHostId";
|
|
19912
19950
|
var delegationReadParamsSchema = external_exports.strictObject({
|
|
19913
19951
|
threadId: external_exports.string().min(1).max(1024),
|
|
19914
19952
|
view: external_exports.enum(["result", "messages"]),
|
|
@@ -20071,6 +20109,8 @@ var usageStatisticsSessionSchema = external_exports.strictObject({
|
|
|
20071
20109
|
harness,
|
|
20072
20110
|
sessionId: external_exports.string().min(1).max(512),
|
|
20073
20111
|
project: project.nullable(),
|
|
20112
|
+
/** Persisted Desktop title for a mapped Session, otherwise its native name; may be absent. */
|
|
20113
|
+
title: external_exports.string().min(1).max(200).optional(),
|
|
20074
20114
|
/** Models the session used, most requests first. */
|
|
20075
20115
|
models: external_exports.array(model).max(16),
|
|
20076
20116
|
firstAtMs: count,
|
|
@@ -20137,7 +20177,12 @@ var usageStatisticsResultSchema = external_exports.strictObject({
|
|
|
20137
20177
|
* The sessions that spent the most, by cost and by tokens without cache, all filters applied;
|
|
20138
20178
|
* requests without a native session are not listed here but count everywhere else.
|
|
20139
20179
|
*/
|
|
20140
|
-
sessions: external_exports.array(usageStatisticsSessionSchema).max(100)
|
|
20180
|
+
sessions: external_exports.array(usageStatisticsSessionSchema).max(100),
|
|
20181
|
+
/**
|
|
20182
|
+
* The sessions last active in the range, newest first, all filters applied. Optional for older
|
|
20183
|
+
* Hosts; missing is treated as no list rather than no sessions.
|
|
20184
|
+
*/
|
|
20185
|
+
recentSessions: external_exports.array(usageStatisticsSessionSchema).max(100).optional()
|
|
20141
20186
|
});
|
|
20142
20187
|
|
|
20143
20188
|
// packages/shared-contracts/dist/ids.js
|
|
@@ -21426,14 +21471,37 @@ var RuntimeMaintenance = class {
|
|
|
21426
21471
|
options2.environment.CODEXHOST_DATA_DIR ?? path7.join(home, ".codexhost", "remote", "data"),
|
|
21427
21472
|
"remote-update.json"
|
|
21428
21473
|
);
|
|
21474
|
+
this.#cancelPath = path7.join(path7.dirname(this.#statusPath), "remote-update.cancel");
|
|
21429
21475
|
}
|
|
21430
21476
|
options;
|
|
21431
21477
|
#running;
|
|
21432
21478
|
#statusPath;
|
|
21479
|
+
#cancelPath;
|
|
21433
21480
|
#update = { phase: "idle", targetVersion: null, error: null };
|
|
21434
21481
|
#blocked = false;
|
|
21482
|
+
#watch = null;
|
|
21435
21483
|
#starting = null;
|
|
21436
21484
|
#startingVersion = null;
|
|
21485
|
+
#ensureWatching() {
|
|
21486
|
+
if (this.#watch) return;
|
|
21487
|
+
this.#watch = setInterval(() => {
|
|
21488
|
+
void this.status().catch(() => void 0).then(() => {
|
|
21489
|
+
if (!this.#blocked && this.#watch) {
|
|
21490
|
+
clearInterval(this.#watch);
|
|
21491
|
+
this.#watch = null;
|
|
21492
|
+
}
|
|
21493
|
+
});
|
|
21494
|
+
}, UPDATER_WATCH_INTERVAL_MS);
|
|
21495
|
+
this.#watch.unref();
|
|
21496
|
+
}
|
|
21497
|
+
async #recordStartFailure(version2, failure) {
|
|
21498
|
+
await writeFile3(this.#cancelPath, JSON.stringify({ targetVersion: version2 }), { mode: 384 });
|
|
21499
|
+
await writeFile3(
|
|
21500
|
+
this.#statusPath,
|
|
21501
|
+
JSON.stringify({ phase: "failed", targetVersion: version2, error: failure }),
|
|
21502
|
+
{ mode: 384 }
|
|
21503
|
+
);
|
|
21504
|
+
}
|
|
21437
21505
|
#sshPending = /* @__PURE__ */ new Set();
|
|
21438
21506
|
async setupSsh(input) {
|
|
21439
21507
|
if (this.options.remote) throw new Error("SSH installation must run from the local computer");
|
|
@@ -21494,7 +21562,7 @@ var RuntimeMaintenance = class {
|
|
|
21494
21562
|
installation(this.options.runtimePath, this.options.environment),
|
|
21495
21563
|
this.#resources()
|
|
21496
21564
|
]);
|
|
21497
|
-
if (this.#update.phase === "idle" || this.#blocked) {
|
|
21565
|
+
if (this.#update.phase === "idle" || this.#blocked || this.#update.phase === "failed") {
|
|
21498
21566
|
try {
|
|
21499
21567
|
const value2 = JSON.parse(await readFile5(this.#statusPath, "utf8"));
|
|
21500
21568
|
const parsed2 = runtimeStatusSchema.shape.update.parse(value2);
|
|
@@ -21517,8 +21585,15 @@ var RuntimeMaintenance = class {
|
|
|
21517
21585
|
parsed2.error = "The previous remote update was interrupted; retry the update";
|
|
21518
21586
|
}
|
|
21519
21587
|
}
|
|
21520
|
-
if (this.#update === observedUpdate)
|
|
21521
|
-
|
|
21588
|
+
if (this.#update === observedUpdate) {
|
|
21589
|
+
this.#update = parsed2;
|
|
21590
|
+
if (["installing", "restarting"].includes(this.#update.phase)) {
|
|
21591
|
+
this.#blocked = true;
|
|
21592
|
+
this.#ensureWatching();
|
|
21593
|
+
} else if (["failed", "succeeded"].includes(this.#update.phase)) {
|
|
21594
|
+
this.#blocked = false;
|
|
21595
|
+
}
|
|
21596
|
+
}
|
|
21522
21597
|
} catch {
|
|
21523
21598
|
}
|
|
21524
21599
|
}
|
|
@@ -21590,6 +21665,7 @@ var RuntimeMaintenance = class {
|
|
|
21590
21665
|
await chmod2(helper, 448);
|
|
21591
21666
|
const request = path7.join(work, "request.json");
|
|
21592
21667
|
this.#update = { phase: "installing", targetVersion: version2, error: null };
|
|
21668
|
+
await unlink(this.#cancelPath).catch(() => void 0);
|
|
21593
21669
|
await writeFile3(this.#statusPath, JSON.stringify(this.#update), { mode: 384 });
|
|
21594
21670
|
await writeFile3(
|
|
21595
21671
|
request,
|
|
@@ -21608,35 +21684,38 @@ var RuntimeMaintenance = class {
|
|
|
21608
21684
|
);
|
|
21609
21685
|
const starter = spawn3(
|
|
21610
21686
|
"/bin/sh",
|
|
21611
|
-
["-c", '"$0" remote --request "$1" </dev/null >/dev/null 2>&1 &', helper, request],
|
|
21612
|
-
{ detached: true, stdio: "ignore", env: this.options.environment }
|
|
21687
|
+
["-c", '"$0" remote --request "$1" </dev/null >/dev/null 2>&1 & echo $!', helper, request],
|
|
21688
|
+
{ detached: true, stdio: ["ignore", "pipe", "ignore"], env: this.options.environment }
|
|
21613
21689
|
);
|
|
21690
|
+
let pidOutput = "";
|
|
21691
|
+
starter.stdout?.on("data", (chunk) => {
|
|
21692
|
+
pidOutput += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
21693
|
+
});
|
|
21614
21694
|
await new Promise((resolve, reject) => {
|
|
21615
21695
|
starter.once("error", reject);
|
|
21616
21696
|
starter.once(
|
|
21617
|
-
"
|
|
21697
|
+
"close",
|
|
21618
21698
|
(code) => code === 0 ? resolve() : reject(new Error("Remote updater could not be started"))
|
|
21619
21699
|
);
|
|
21620
21700
|
});
|
|
21701
|
+
const helperPid = Number.parseInt(pidOutput.trim(), 10);
|
|
21702
|
+
if (!Number.isSafeInteger(helperPid) || helperPid <= 0) {
|
|
21703
|
+
throw new Error("Remote updater could not be started");
|
|
21704
|
+
}
|
|
21621
21705
|
const deadline = Date.now() + UPDATER_START_TIMEOUT_MS;
|
|
21622
21706
|
while (!await this.#helperReported()) {
|
|
21623
21707
|
if (Date.now() >= deadline) {
|
|
21708
|
+
try {
|
|
21709
|
+
process.kill(helperPid);
|
|
21710
|
+
} catch {
|
|
21711
|
+
}
|
|
21624
21712
|
const failure = "Remote updater did not start";
|
|
21625
|
-
await
|
|
21626
|
-
this.#statusPath,
|
|
21627
|
-
JSON.stringify({ phase: "failed", targetVersion: version2, error: failure }),
|
|
21628
|
-
{ mode: 384 }
|
|
21629
|
-
);
|
|
21713
|
+
await this.#recordStartFailure(version2, failure);
|
|
21630
21714
|
throw new Error(failure);
|
|
21631
21715
|
}
|
|
21632
21716
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
21633
21717
|
}
|
|
21634
|
-
|
|
21635
|
-
void this.status().catch(() => void 0).then(() => {
|
|
21636
|
-
if (!this.#blocked) clearInterval(watch);
|
|
21637
|
-
});
|
|
21638
|
-
}, UPDATER_WATCH_INTERVAL_MS);
|
|
21639
|
-
watch.unref();
|
|
21718
|
+
this.#ensureWatching();
|
|
21640
21719
|
}
|
|
21641
21720
|
async #helperReported() {
|
|
21642
21721
|
try {
|
|
@@ -21650,6 +21729,7 @@ var RuntimeMaintenance = class {
|
|
|
21650
21729
|
|
|
21651
21730
|
// packages/host-runtime/src/run-host-runtime.ts
|
|
21652
21731
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
21732
|
+
import { statSync } from "node:fs";
|
|
21653
21733
|
import path37 from "node:path";
|
|
21654
21734
|
import { homedir as homedir4 } from "node:os";
|
|
21655
21735
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -26894,61 +26974,6 @@ function finitePercent(value2) {
|
|
|
26894
26974
|
function optionalReset(value2) {
|
|
26895
26975
|
return nonNegativeSafeInteger(value2);
|
|
26896
26976
|
}
|
|
26897
|
-
function addBreakdown(target, source) {
|
|
26898
|
-
if (!source)
|
|
26899
|
-
return;
|
|
26900
|
-
const fields = [
|
|
26901
|
-
["totalTokens", "totalTokens"],
|
|
26902
|
-
["inputTokens", "inputTokens"],
|
|
26903
|
-
["cachedInputTokens", "cachedInputTokens"],
|
|
26904
|
-
["cacheWriteInputTokens", "cacheWriteInputTokens"],
|
|
26905
|
-
["outputTokens", "outputTokens"],
|
|
26906
|
-
["reasoningOutputTokens", "reasoningOutputTokens"]
|
|
26907
|
-
];
|
|
26908
|
-
for (const [sourceField, targetField] of fields) {
|
|
26909
|
-
const value2 = nonNegativeSafeInteger(source[sourceField]);
|
|
26910
|
-
if (value2 !== void 0)
|
|
26911
|
-
target[targetField] = value2;
|
|
26912
|
-
}
|
|
26913
|
-
}
|
|
26914
|
-
function observeCodexTokenUsage(value2) {
|
|
26915
|
-
if (!isRecord9(value2) || value2.method !== "thread/tokenUsage/updated")
|
|
26916
|
-
return null;
|
|
26917
|
-
const params = value2.params;
|
|
26918
|
-
if (!isRecord9(params))
|
|
26919
|
-
return null;
|
|
26920
|
-
const threadId2 = hostThreadIdSchema.safeParse(params.threadId);
|
|
26921
|
-
const turnId = hostTurnIdSchema.safeParse(params.turnId);
|
|
26922
|
-
if (!threadId2.success || !turnId.success)
|
|
26923
|
-
return null;
|
|
26924
|
-
const tokenUsage = params.tokenUsage;
|
|
26925
|
-
if (!isRecord9(tokenUsage))
|
|
26926
|
-
return null;
|
|
26927
|
-
const total = isRecord9(tokenUsage.total) ? tokenUsage.total : void 0;
|
|
26928
|
-
const last = isRecord9(tokenUsage.last) ? tokenUsage.last : void 0;
|
|
26929
|
-
const usage = {};
|
|
26930
|
-
addBreakdown(usage, total);
|
|
26931
|
-
const contextUsedTokens = nonNegativeSafeInteger(last?.totalTokens);
|
|
26932
|
-
const contextWindowTokens = nonNegativeSafeInteger(tokenUsage.modelContextWindow);
|
|
26933
|
-
if (contextUsedTokens !== void 0 && contextWindowTokens !== void 0 && contextWindowTokens > 0) {
|
|
26934
|
-
usage.contextUsedTokens = contextUsedTokens;
|
|
26935
|
-
usage.contextWindowTokens = contextWindowTokens;
|
|
26936
|
-
}
|
|
26937
|
-
const inputTokens = nonNegativeSafeInteger(last?.inputTokens);
|
|
26938
|
-
const cachedInputTokens = nonNegativeSafeInteger(last?.cachedInputTokens);
|
|
26939
|
-
if (inputTokens !== void 0 && cachedInputTokens !== void 0 && inputTokens > 0) {
|
|
26940
|
-
usage.cacheHitRatePercent = Math.min(100, cachedInputTokens / inputTokens * 100);
|
|
26941
|
-
}
|
|
26942
|
-
try {
|
|
26943
|
-
return {
|
|
26944
|
-
threadId: threadId2.data,
|
|
26945
|
-
turnId: turnId.data,
|
|
26946
|
-
usage: parseHostUsage(usage)
|
|
26947
|
-
};
|
|
26948
|
-
} catch {
|
|
26949
|
-
return null;
|
|
26950
|
-
}
|
|
26951
|
-
}
|
|
26952
26977
|
function parseRateLimitWindow(value2) {
|
|
26953
26978
|
if (!isRecord9(value2))
|
|
26954
26979
|
return null;
|
|
@@ -31759,6 +31784,16 @@ var ExternalThreadIdleRelease = class {
|
|
|
31759
31784
|
const state = this.#activity.get(thread);
|
|
31760
31785
|
if (state) state.outputFailed = true;
|
|
31761
31786
|
}
|
|
31787
|
+
/** After a successful Session revival, allow idle release again. */
|
|
31788
|
+
clearOutputFailed(thread) {
|
|
31789
|
+
const state = this.#activity.get(thread);
|
|
31790
|
+
if (state) {
|
|
31791
|
+
state.outputFailed = false;
|
|
31792
|
+
state.lastActivity = Date.now();
|
|
31793
|
+
} else {
|
|
31794
|
+
this.touch(thread);
|
|
31795
|
+
}
|
|
31796
|
+
}
|
|
31762
31797
|
failure(thread) {
|
|
31763
31798
|
return this.#activity.get(thread)?.closeFailed ? CLOSE_FAILURE : void 0;
|
|
31764
31799
|
}
|
|
@@ -31907,6 +31942,80 @@ var ExternalThreadIdleRelease = class {
|
|
|
31907
31942
|
}
|
|
31908
31943
|
};
|
|
31909
31944
|
|
|
31945
|
+
// packages/host-runtime/src/external-thread-lifecycle.ts
|
|
31946
|
+
var RELEASE_TIMEOUT_MS = 6e4;
|
|
31947
|
+
var ExternalSessionReleaseError = class extends Error {
|
|
31948
|
+
constructor(cause) {
|
|
31949
|
+
super(
|
|
31950
|
+
"External Session shutdown could not be confirmed. Retry to confirm release; recovery is blocked until the old Session closes and its output drains. If the Harness cannot retry shutdown, stop residual processes and restart Codex Desktop.",
|
|
31951
|
+
{ cause }
|
|
31952
|
+
);
|
|
31953
|
+
this.name = "ExternalSessionReleaseError";
|
|
31954
|
+
}
|
|
31955
|
+
};
|
|
31956
|
+
var ExternalThreadLifecycle = class {
|
|
31957
|
+
#pending = /* @__PURE__ */ new Map();
|
|
31958
|
+
#closing = /* @__PURE__ */ new WeakMap();
|
|
31959
|
+
#releasing = /* @__PURE__ */ new Map();
|
|
31960
|
+
busy(id2) {
|
|
31961
|
+
return this.#pending.has(id2) || this.#releasing.has(id2);
|
|
31962
|
+
}
|
|
31963
|
+
pending() {
|
|
31964
|
+
return [...this.#pending];
|
|
31965
|
+
}
|
|
31966
|
+
run(id2, operation) {
|
|
31967
|
+
const previous = this.#pending.get(id2) ?? Promise.resolve();
|
|
31968
|
+
const task = previous.then(async () => {
|
|
31969
|
+
const releasing = this.#releasing.get(id2);
|
|
31970
|
+
if (releasing) await this.release(id2, releasing.session, releasing.output);
|
|
31971
|
+
return operation();
|
|
31972
|
+
});
|
|
31973
|
+
const settled = task.then(
|
|
31974
|
+
() => void 0,
|
|
31975
|
+
() => void 0
|
|
31976
|
+
);
|
|
31977
|
+
this.#pending.set(id2, settled);
|
|
31978
|
+
void settled.then(() => {
|
|
31979
|
+
if (this.#pending.get(id2) === settled) this.#pending.delete(id2);
|
|
31980
|
+
});
|
|
31981
|
+
return task;
|
|
31982
|
+
}
|
|
31983
|
+
/** Call inside the Thread's lifecycle operation. A timeout never cancels the owned close. */
|
|
31984
|
+
async release(id2, session, output) {
|
|
31985
|
+
this.#releasing.set(id2, { session, output });
|
|
31986
|
+
let closing = this.#closing.get(session);
|
|
31987
|
+
if (!closing) {
|
|
31988
|
+
closing = Promise.resolve().then(() => session.close());
|
|
31989
|
+
this.#closing.set(session, closing);
|
|
31990
|
+
const attempt = closing;
|
|
31991
|
+
void closing.catch(() => {
|
|
31992
|
+
if (this.#closing.get(session) === attempt) this.#closing.delete(session);
|
|
31993
|
+
});
|
|
31994
|
+
}
|
|
31995
|
+
let timeout;
|
|
31996
|
+
try {
|
|
31997
|
+
await Promise.race([
|
|
31998
|
+
closing.then(() => output),
|
|
31999
|
+
new Promise((_, reject) => {
|
|
32000
|
+
timeout = setTimeout(
|
|
32001
|
+
() => reject(new Error("Session shutdown timed out")),
|
|
32002
|
+
RELEASE_TIMEOUT_MS
|
|
32003
|
+
);
|
|
32004
|
+
timeout.unref();
|
|
32005
|
+
})
|
|
32006
|
+
]);
|
|
32007
|
+
this.#releasing.delete(id2);
|
|
32008
|
+
} catch (error51) {
|
|
32009
|
+
throw new ExternalSessionReleaseError(error51);
|
|
32010
|
+
} finally {
|
|
32011
|
+
if (timeout) clearTimeout(timeout);
|
|
32012
|
+
}
|
|
32013
|
+
}
|
|
32014
|
+
async drain() {
|
|
32015
|
+
while (this.#pending.size) await Promise.all(this.#pending.values());
|
|
32016
|
+
}
|
|
32017
|
+
};
|
|
32018
|
+
|
|
31910
32019
|
// packages/host-runtime/src/usage-metering.ts
|
|
31911
32020
|
var TOKENS_PER_PRICE_UNIT = 1e6;
|
|
31912
32021
|
var UsageMeter = class {
|
|
@@ -31922,6 +32031,8 @@ var UsageMeter = class {
|
|
|
31922
32031
|
#turnOutputMs = 0;
|
|
31923
32032
|
#timeToFirstOutputMs;
|
|
31924
32033
|
#outputTokensPerSecond;
|
|
32034
|
+
#timingUnavailable = false;
|
|
32035
|
+
#timedRequests = /* @__PURE__ */ new Set();
|
|
31925
32036
|
get metered() {
|
|
31926
32037
|
return this.#metered;
|
|
31927
32038
|
}
|
|
@@ -31938,12 +32049,53 @@ var UsageMeter = class {
|
|
|
31938
32049
|
this.#requests.set(request.requestId, request);
|
|
31939
32050
|
const duration4 = request.startedAtMs !== void 0 && request.completedAtMs !== void 0 ? request.completedAtMs - request.startedAtMs : 0;
|
|
31940
32051
|
if (!request.historical && duration4 > 0 && activeTurnId !== null && activeTurnId === this.#turnId) {
|
|
31941
|
-
this
|
|
31942
|
-
|
|
31943
|
-
|
|
32052
|
+
this.recordOutputTiming(
|
|
32053
|
+
{
|
|
32054
|
+
requestId: request.requestId,
|
|
32055
|
+
outputTokens: request.outputTokens,
|
|
32056
|
+
startedAtMs: request.startedAtMs ?? 0,
|
|
32057
|
+
completedAtMs: request.completedAtMs ?? 0
|
|
32058
|
+
},
|
|
32059
|
+
activeTurnId
|
|
32060
|
+
);
|
|
31944
32061
|
}
|
|
31945
32062
|
return true;
|
|
31946
32063
|
}
|
|
32064
|
+
/** Live timing is independent of history replacement and cannot create billing entries. */
|
|
32065
|
+
recordOutputTiming(request, turnId) {
|
|
32066
|
+
if (turnId !== this.#turnId || this.#timingUnavailable || this.#timedRequests.has(request.requestId))
|
|
32067
|
+
return false;
|
|
32068
|
+
if (!request.requestId || ![request.outputTokens, request.startedAtMs, request.completedAtMs].every(
|
|
32069
|
+
(value2) => Number.isSafeInteger(value2) && value2 >= 0
|
|
32070
|
+
) || request.completedAtMs <= request.startedAtMs) {
|
|
32071
|
+
this.invalidateOutputTiming(turnId);
|
|
32072
|
+
return true;
|
|
32073
|
+
}
|
|
32074
|
+
this.#timedRequests.add(request.requestId);
|
|
32075
|
+
this.#turnOutputTokens += request.outputTokens;
|
|
32076
|
+
this.#turnOutputMs += request.completedAtMs - request.startedAtMs;
|
|
32077
|
+
this.#outputTokensPerSecond = this.#turnOutputTokens / (this.#turnOutputMs / 1e3);
|
|
32078
|
+
return true;
|
|
32079
|
+
}
|
|
32080
|
+
invalidateOutputTiming(turnId) {
|
|
32081
|
+
if (turnId !== this.#turnId) return;
|
|
32082
|
+
this.#timingUnavailable = true;
|
|
32083
|
+
}
|
|
32084
|
+
/** Discard in-flight timing across connections, but keep the last valid displayed speed. */
|
|
32085
|
+
resetTurnTiming() {
|
|
32086
|
+
this.#turnId = null;
|
|
32087
|
+
this.#turnStartedAtMs = null;
|
|
32088
|
+
this.#timeToFirstOutputMs = void 0;
|
|
32089
|
+
this.#timedRequests.clear();
|
|
32090
|
+
this.#timingUnavailable = false;
|
|
32091
|
+
}
|
|
32092
|
+
/** Replace a read-only session history after recovery, append, rollback or file replacement. */
|
|
32093
|
+
replaceHistory(requests, complete) {
|
|
32094
|
+
this.#requests.clear();
|
|
32095
|
+
this.#invalidRecord = false;
|
|
32096
|
+
for (const request of requests) this.recordRequest({ ...request, historical: true }, null);
|
|
32097
|
+
this.recordHistory(complete);
|
|
32098
|
+
}
|
|
31947
32099
|
recordHistory(complete) {
|
|
31948
32100
|
this.#metered = true;
|
|
31949
32101
|
this.#historyComplete = complete;
|
|
@@ -31954,6 +32106,8 @@ var UsageMeter = class {
|
|
|
31954
32106
|
this.#turnOutputObserved = false;
|
|
31955
32107
|
this.#turnOutputTokens = 0;
|
|
31956
32108
|
this.#turnOutputMs = 0;
|
|
32109
|
+
this.#timedRequests.clear();
|
|
32110
|
+
this.#timingUnavailable = false;
|
|
31957
32111
|
}
|
|
31958
32112
|
/** Returns true when this is the Turn's first visible output. */
|
|
31959
32113
|
outputObserved(turnId, nowMs) {
|
|
@@ -31966,7 +32120,6 @@ var UsageMeter = class {
|
|
|
31966
32120
|
}
|
|
31967
32121
|
turnCompleted(turnId) {
|
|
31968
32122
|
if (turnId !== this.#turnId) return;
|
|
31969
|
-
if (this.#turnOutputMs === 0) this.#outputTokensPerSecond = void 0;
|
|
31970
32123
|
this.#turnId = null;
|
|
31971
32124
|
this.#turnStartedAtMs = null;
|
|
31972
32125
|
}
|
|
@@ -32001,12 +32154,12 @@ var UsageMeter = class {
|
|
|
32001
32154
|
const cacheHitRate = this.#sessionCacheHitRatePercent();
|
|
32002
32155
|
if (cacheHitRate !== null) usage.sessionCacheHitRatePercent = cacheHitRate;
|
|
32003
32156
|
}
|
|
32004
|
-
if (this.#outputTokensPerSecond !== void 0) {
|
|
32005
|
-
usage.outputTokensPerSecond = this.#outputTokensPerSecond;
|
|
32006
|
-
}
|
|
32007
32157
|
} else if (usage.totalCostUsd !== void 0) {
|
|
32008
32158
|
usage.costSource = "native";
|
|
32009
32159
|
}
|
|
32160
|
+
if (this.#timingUnavailable) delete usage.outputTokensPerSecond;
|
|
32161
|
+
if (this.#outputTokensPerSecond !== void 0)
|
|
32162
|
+
usage.outputTokensPerSecond = this.#outputTokensPerSecond;
|
|
32010
32163
|
if (this.#timeToFirstOutputMs !== void 0) {
|
|
32011
32164
|
usage.timeToFirstOutputMs = this.#timeToFirstOutputMs;
|
|
32012
32165
|
}
|
|
@@ -32145,6 +32298,11 @@ var ExternalThreadOpenError = class extends Error {
|
|
|
32145
32298
|
function errorMessage3(error51) {
|
|
32146
32299
|
return error51 instanceof Error ? error51.message : String(error51);
|
|
32147
32300
|
}
|
|
32301
|
+
function recoveryError(error51) {
|
|
32302
|
+
if (error51 instanceof ExternalThreadOpenError) return error51.rpcError;
|
|
32303
|
+
if (error51 instanceof ExternalSessionReleaseError) return { code: -32075, message: error51.message };
|
|
32304
|
+
return { code: -32076, message: "External Thread recovery failed; retry the request" };
|
|
32305
|
+
}
|
|
32148
32306
|
var ExternalThreadRuntime = class {
|
|
32149
32307
|
idleRelease;
|
|
32150
32308
|
#adapters;
|
|
@@ -32152,7 +32310,8 @@ var ExternalThreadRuntime = class {
|
|
|
32152
32310
|
#diagnose;
|
|
32153
32311
|
#environment;
|
|
32154
32312
|
#repository;
|
|
32155
|
-
#
|
|
32313
|
+
#lifecycle = new ExternalThreadLifecycle();
|
|
32314
|
+
#hasRunningDescendants;
|
|
32156
32315
|
#subagentRunning;
|
|
32157
32316
|
#threads = /* @__PURE__ */ new Map();
|
|
32158
32317
|
constructor(input) {
|
|
@@ -32162,12 +32321,13 @@ var ExternalThreadRuntime = class {
|
|
|
32162
32321
|
this.#consumeOutputs = input.consumeOutputs;
|
|
32163
32322
|
this.#diagnose = input.diagnose;
|
|
32164
32323
|
this.#subagentRunning = input.subagentRunning ?? (() => false);
|
|
32324
|
+
this.#hasRunningDescendants = input.hasRunningDescendants ?? (() => false);
|
|
32165
32325
|
this.idleRelease = new ExternalThreadIdleRelease({
|
|
32166
32326
|
threads: () => this.values(),
|
|
32167
32327
|
get: (id2) => this.get(id2),
|
|
32168
32328
|
remove: (id2) => this.remove(id2),
|
|
32169
32329
|
queue: input.idleRelease?.queue ?? new DesktopRequestQueue(),
|
|
32170
|
-
canRelease: input.idleRelease?.canRelease
|
|
32330
|
+
canRelease: (thread) => !thread.sessionUnavailable && !this.#lifecycle.busy(thread.id) && (input.idleRelease?.canRelease(thread) ?? false),
|
|
32171
32331
|
...input.idleRelease?.onClosed ? { onClosed: input.idleRelease.onClosed } : {},
|
|
32172
32332
|
diagnose: input.diagnose
|
|
32173
32333
|
});
|
|
@@ -32183,7 +32343,45 @@ var ExternalThreadRuntime = class {
|
|
|
32183
32343
|
}
|
|
32184
32344
|
clear() {
|
|
32185
32345
|
this.#threads.clear();
|
|
32186
|
-
|
|
32346
|
+
}
|
|
32347
|
+
drain() {
|
|
32348
|
+
return this.#lifecycle.drain();
|
|
32349
|
+
}
|
|
32350
|
+
#withLifecycle(threadId2, operation) {
|
|
32351
|
+
return this.#lifecycle.run(threadId2, async () => {
|
|
32352
|
+
await this.idleRelease.waitForClose(threadId2);
|
|
32353
|
+
return operation();
|
|
32354
|
+
});
|
|
32355
|
+
}
|
|
32356
|
+
markSessionUnavailable(thread) {
|
|
32357
|
+
thread.sessionUnavailable = true;
|
|
32358
|
+
}
|
|
32359
|
+
#idleForRestore(thread) {
|
|
32360
|
+
if (thread.running || thread.activeTurnId !== null) return false;
|
|
32361
|
+
if (thread.session.hasBackgroundWork?.()) return false;
|
|
32362
|
+
if (this.#hasRunningDescendants(thread.id)) return false;
|
|
32363
|
+
return true;
|
|
32364
|
+
}
|
|
32365
|
+
async #dropIdleSession(thread) {
|
|
32366
|
+
if (this.#threads.get(thread.id) !== thread) return;
|
|
32367
|
+
thread.sessionUnavailable = true;
|
|
32368
|
+
await this.#lifecycle.release(thread.id, thread.session, thread.outputTask);
|
|
32369
|
+
if (this.#threads.get(thread.id) !== thread) return;
|
|
32370
|
+
await this.#retireSubagents(thread);
|
|
32371
|
+
if (this.#threads.get(thread.id) === thread) this.#threads.delete(thread.id);
|
|
32372
|
+
}
|
|
32373
|
+
/** Called without awaiting from the output consumer: release itself drains that consumer. */
|
|
32374
|
+
async discardUnavailableIdle(thread) {
|
|
32375
|
+
const session = thread.session;
|
|
32376
|
+
try {
|
|
32377
|
+
await this.#withLifecycle(thread.id, async () => {
|
|
32378
|
+
if (this.#threads.get(thread.id) !== thread || thread.session !== session || !thread.sessionUnavailable || !this.#idleForRestore(thread))
|
|
32379
|
+
return;
|
|
32380
|
+
await this.#dropIdleSession(thread);
|
|
32381
|
+
});
|
|
32382
|
+
} catch (error51) {
|
|
32383
|
+
this.#diagnose(error51);
|
|
32384
|
+
}
|
|
32187
32385
|
}
|
|
32188
32386
|
register(input) {
|
|
32189
32387
|
const harnessId = input.record.harnessId;
|
|
@@ -32228,7 +32426,8 @@ var ExternalThreadRuntime = class {
|
|
|
32228
32426
|
ephemeralTurnIds: /* @__PURE__ */ new Set(),
|
|
32229
32427
|
persistenceError: null,
|
|
32230
32428
|
projectedTerminalTurnId: null,
|
|
32231
|
-
ignoredInteractionIds: /* @__PURE__ */ new Set()
|
|
32429
|
+
ignoredInteractionIds: /* @__PURE__ */ new Set(),
|
|
32430
|
+
sessionUnavailable: false
|
|
32232
32431
|
};
|
|
32233
32432
|
this.idleRelease.touch(externalThread);
|
|
32234
32433
|
externalThread.outputTask = this.#consumeOutputs(externalThread);
|
|
@@ -32255,11 +32454,12 @@ var ExternalThreadRuntime = class {
|
|
|
32255
32454
|
} catch (error51) {
|
|
32256
32455
|
this.#diagnose(error51);
|
|
32257
32456
|
}
|
|
32258
|
-
await this.#retireSubagents(current
|
|
32457
|
+
await this.#retireSubagents(current, true);
|
|
32259
32458
|
this.#threads.delete(current.id);
|
|
32260
32459
|
return this.register(input);
|
|
32261
32460
|
}
|
|
32262
|
-
async #retireSubagents(
|
|
32461
|
+
async #retireSubagents(parent, bestEffort = false) {
|
|
32462
|
+
const parentId = parent.id;
|
|
32263
32463
|
const ancestors = /* @__PURE__ */ new Map();
|
|
32264
32464
|
const descendant = async (record4) => {
|
|
32265
32465
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -32276,24 +32476,35 @@ var ExternalThreadRuntime = class {
|
|
|
32276
32476
|
}
|
|
32277
32477
|
return false;
|
|
32278
32478
|
};
|
|
32279
|
-
for (const [id2,
|
|
32280
|
-
if (await descendant(await this.#repository.find(id2)))
|
|
32281
|
-
await restoring.catch(this.#diagnose);
|
|
32282
|
-
}
|
|
32479
|
+
for (const [id2, pending] of this.#lifecycle.pending()) {
|
|
32480
|
+
if (id2 !== parentId && await descendant(await this.#repository.find(id2))) await pending;
|
|
32283
32481
|
}
|
|
32284
|
-
for (const child of this.#threads.values()) {
|
|
32482
|
+
for (const child of [...this.#threads.values()]) {
|
|
32285
32483
|
if (!await descendant(child.record)) continue;
|
|
32286
|
-
|
|
32287
|
-
|
|
32288
|
-
|
|
32289
|
-
|
|
32290
|
-
|
|
32291
|
-
|
|
32292
|
-
|
|
32484
|
+
const retire = async () => {
|
|
32485
|
+
if (this.#threads.get(parentId) !== parent || this.#threads.get(child.id) !== child) return;
|
|
32486
|
+
if (bestEffort) {
|
|
32487
|
+
this.#threads.delete(child.id);
|
|
32488
|
+
await child.session.close();
|
|
32489
|
+
await child.outputTask;
|
|
32490
|
+
} else {
|
|
32491
|
+
child.sessionUnavailable = true;
|
|
32492
|
+
await this.#lifecycle.release(child.id, child.session, child.outputTask);
|
|
32493
|
+
if (this.#threads.get(child.id) === child) this.#threads.delete(child.id);
|
|
32494
|
+
}
|
|
32495
|
+
};
|
|
32496
|
+
if (bestEffort) await retire().catch(this.#diagnose);
|
|
32497
|
+
else await this.#withLifecycle(child.id, retire);
|
|
32293
32498
|
}
|
|
32294
32499
|
}
|
|
32295
32500
|
async locate(threadId2) {
|
|
32296
|
-
|
|
32501
|
+
try {
|
|
32502
|
+
return await this.#withLifecycle(threadId2, () => this.#locate(threadId2));
|
|
32503
|
+
} catch (error51) {
|
|
32504
|
+
return { kind: "error", error: recoveryError(error51) };
|
|
32505
|
+
}
|
|
32506
|
+
}
|
|
32507
|
+
async #locate(threadId2) {
|
|
32297
32508
|
const loaded = this.#threads.get(threadId2);
|
|
32298
32509
|
if (loaded) {
|
|
32299
32510
|
const failure = this.idleRelease.failure(loaded);
|
|
@@ -32319,37 +32530,61 @@ var ExternalThreadRuntime = class {
|
|
|
32319
32530
|
return { kind: "external", record: record4, thread: null };
|
|
32320
32531
|
}
|
|
32321
32532
|
async resolve(threadId2) {
|
|
32322
|
-
|
|
32323
|
-
|
|
32324
|
-
|
|
32325
|
-
|
|
32326
|
-
|
|
32327
|
-
|
|
32328
|
-
|
|
32329
|
-
|
|
32330
|
-
|
|
32331
|
-
|
|
32332
|
-
|
|
32333
|
-
|
|
32334
|
-
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32533
|
+
try {
|
|
32534
|
+
return await this.#withLifecycle(threadId2, async () => {
|
|
32535
|
+
const location = await this.#locate(threadId2);
|
|
32536
|
+
if (location.kind !== "external") return location;
|
|
32537
|
+
if (location.thread) {
|
|
32538
|
+
if (location.thread.sessionUnavailable && this.#idleForRestore(location.thread)) {
|
|
32539
|
+
await this.#dropIdleSession(location.thread);
|
|
32540
|
+
const current = this.#threads.get(threadId2);
|
|
32541
|
+
if (current) return { kind: "external", thread: current, historyFresh: false };
|
|
32542
|
+
} else {
|
|
32543
|
+
this.idleRelease.touch(location.thread);
|
|
32544
|
+
return { kind: "external", thread: location.thread, historyFresh: false };
|
|
32545
|
+
}
|
|
32546
|
+
}
|
|
32547
|
+
return {
|
|
32548
|
+
kind: "external",
|
|
32549
|
+
thread: await this.#restore(location.record),
|
|
32550
|
+
historyFresh: true
|
|
32551
|
+
};
|
|
32338
32552
|
});
|
|
32339
|
-
|
|
32553
|
+
} catch (error51) {
|
|
32554
|
+
return { kind: "error", error: recoveryError(error51) };
|
|
32340
32555
|
}
|
|
32556
|
+
}
|
|
32557
|
+
async refresh(thread) {
|
|
32341
32558
|
try {
|
|
32342
|
-
return
|
|
32559
|
+
return await this.#withLifecycle(thread.id, () => this.#refresh(thread));
|
|
32343
32560
|
} catch (error51) {
|
|
32561
|
+
return recoveryError(error51);
|
|
32562
|
+
}
|
|
32563
|
+
}
|
|
32564
|
+
async #refresh(thread) {
|
|
32565
|
+
if (this.#threads.get(thread.id) !== thread) {
|
|
32344
32566
|
return {
|
|
32345
|
-
|
|
32346
|
-
|
|
32567
|
+
code: -32076,
|
|
32568
|
+
message: "External Session changed during recovery; retry the request"
|
|
32347
32569
|
};
|
|
32348
32570
|
}
|
|
32349
|
-
|
|
32350
|
-
|
|
32351
|
-
|
|
32352
|
-
|
|
32571
|
+
const failure = this.idleRelease.failure(thread);
|
|
32572
|
+
if (failure) return { code: -32075, message: failure };
|
|
32573
|
+
if (thread.sessionUnavailable && this.#idleForRestore(thread)) {
|
|
32574
|
+
const revived = await this.#reviveIdleSession(thread);
|
|
32575
|
+
if (revived) return revived;
|
|
32576
|
+
}
|
|
32577
|
+
let snapshot2 = await thread.session.readSnapshot();
|
|
32578
|
+
if (!snapshot2.ok) {
|
|
32579
|
+
if (snapshot2.error.code === "invalidState" && this.#idleForRestore(thread) && this.#threads.get(thread.id) === thread) {
|
|
32580
|
+
const revived = await this.#reviveIdleSession(thread);
|
|
32581
|
+
if (revived) return revived;
|
|
32582
|
+
snapshot2 = await thread.session.readSnapshot();
|
|
32583
|
+
if (!snapshot2.ok) return mapExternalThreadHarnessError(snapshot2.error, "read");
|
|
32584
|
+
} else {
|
|
32585
|
+
return mapExternalThreadHarnessError(snapshot2.error, "read");
|
|
32586
|
+
}
|
|
32587
|
+
}
|
|
32353
32588
|
try {
|
|
32354
32589
|
const aligned = await this.#repository.alignSnapshot(thread.record, snapshot2.value);
|
|
32355
32590
|
thread.record = aligned.record;
|
|
@@ -32366,6 +32601,122 @@ var ExternalThreadRuntime = class {
|
|
|
32366
32601
|
return { code: -32081, message: "External Thread history could not be persisted" };
|
|
32367
32602
|
}
|
|
32368
32603
|
}
|
|
32604
|
+
async #reviveIdleSession(thread) {
|
|
32605
|
+
thread.sessionUnavailable = true;
|
|
32606
|
+
const harnessId = thread.record.harnessId;
|
|
32607
|
+
const adapter = this.#adapters.get(harnessId);
|
|
32608
|
+
if (!adapter || !thread.record.nativeSessionRef) {
|
|
32609
|
+
await this.#dropIdleSession(thread);
|
|
32610
|
+
return { code: -32077, message: "External Harness is unavailable" };
|
|
32611
|
+
}
|
|
32612
|
+
await this.#lifecycle.release(thread.id, thread.session, thread.outputTask);
|
|
32613
|
+
await this.#retireSubagents(thread);
|
|
32614
|
+
if (this.#threads.get(thread.id) !== thread) {
|
|
32615
|
+
return { code: -32076, message: "External Thread recovery failed" };
|
|
32616
|
+
}
|
|
32617
|
+
const restoredSelection = decodeExternalTransportSelection(
|
|
32618
|
+
harnessId,
|
|
32619
|
+
thread.record.transportModelId
|
|
32620
|
+
);
|
|
32621
|
+
const opened = await adapter.open({
|
|
32622
|
+
kind: "resume",
|
|
32623
|
+
cwd: thread.record.cwd,
|
|
32624
|
+
environment: { ...this.#environment, [DELEGATION_THREAD_ID_ENV]: thread.record.hostThreadId },
|
|
32625
|
+
nativeRef: thread.record.nativeSessionRef,
|
|
32626
|
+
knownTurnRefs: thread.record.turnMappings.map(({ nativeTurnRef }) => nativeTurnRef),
|
|
32627
|
+
...restoredSelection?.model ? { model: restoredSelection.model } : {},
|
|
32628
|
+
...restoredSelection?.thinkingOptionId ? { thinkingOptionId: restoredSelection.thinkingOptionId } : {},
|
|
32629
|
+
...restoredSelection?.permissionModeId ? { permissionModeId: restoredSelection.permissionModeId } : {}
|
|
32630
|
+
});
|
|
32631
|
+
if (!opened.ok) {
|
|
32632
|
+
await this.#dropIdleSession(thread);
|
|
32633
|
+
return mapExternalThreadHarnessError(opened.error, "resume");
|
|
32634
|
+
}
|
|
32635
|
+
const session = opened.value;
|
|
32636
|
+
if (this.#threads.get(thread.id) !== thread) {
|
|
32637
|
+
await this.#lifecycle.release(thread.id, session, Promise.resolve());
|
|
32638
|
+
return { code: -32076, message: "External Thread recovery failed" };
|
|
32639
|
+
}
|
|
32640
|
+
try {
|
|
32641
|
+
const snapshot2 = await session.readSnapshot();
|
|
32642
|
+
if (!snapshot2.ok) {
|
|
32643
|
+
await this.#lifecycle.release(thread.id, session, Promise.resolve());
|
|
32644
|
+
await this.#dropIdleSession(thread);
|
|
32645
|
+
return mapExternalThreadHarnessError(snapshot2.error, "read");
|
|
32646
|
+
}
|
|
32647
|
+
let aligned = await this.#repository.alignSnapshot(thread.record, snapshot2.value);
|
|
32648
|
+
if (this.#threads.get(thread.id) !== thread) {
|
|
32649
|
+
await this.#lifecycle.release(thread.id, session, Promise.resolve());
|
|
32650
|
+
return { code: -32076, message: "External Thread recovery failed" };
|
|
32651
|
+
}
|
|
32652
|
+
const restoredState = snapshot2.value.state;
|
|
32653
|
+
const effectiveModel = restoredState ? restoredState.effectiveModel : restoredSelection?.model;
|
|
32654
|
+
const effectiveThinkingOptionId = restoredState ? restoredState.effectiveThinkingOptionId : restoredSelection?.thinkingOptionId;
|
|
32655
|
+
const effectivePermissionModeId = restoredState ? restoredState.effectivePermissionModeId : restoredSelection?.permissionModeId;
|
|
32656
|
+
let transportModelId = aligned.record.transportModelId;
|
|
32657
|
+
if (restoredState && effectiveModel) {
|
|
32658
|
+
const liveSelection = {
|
|
32659
|
+
model: effectiveModel,
|
|
32660
|
+
...effectiveThinkingOptionId ? { thinkingOptionId: effectiveThinkingOptionId } : {},
|
|
32661
|
+
...effectivePermissionModeId ? { permissionModeId: effectivePermissionModeId } : {}
|
|
32662
|
+
};
|
|
32663
|
+
transportModelId = encodeExternalTransportSelection(harnessId, liveSelection);
|
|
32664
|
+
if (transportModelId !== aligned.record.transportModelId) {
|
|
32665
|
+
try {
|
|
32666
|
+
aligned = {
|
|
32667
|
+
...aligned,
|
|
32668
|
+
record: await this.#repository.setTransportModelId(
|
|
32669
|
+
aligned.record.hostThreadId,
|
|
32670
|
+
transportModelId
|
|
32671
|
+
)
|
|
32672
|
+
};
|
|
32673
|
+
} catch (error51) {
|
|
32674
|
+
this.#diagnose(error51);
|
|
32675
|
+
}
|
|
32676
|
+
}
|
|
32677
|
+
}
|
|
32678
|
+
if (this.#threads.get(thread.id) !== thread) {
|
|
32679
|
+
await this.#lifecycle.release(thread.id, session, Promise.resolve());
|
|
32680
|
+
return { code: -32076, message: "External Thread recovery failed" };
|
|
32681
|
+
}
|
|
32682
|
+
const initialState = restoredState ?? session.initialState;
|
|
32683
|
+
const observerState = {
|
|
32684
|
+
...initialState,
|
|
32685
|
+
...effectiveModel ? { effectiveModel } : {},
|
|
32686
|
+
...effectiveThinkingOptionId ? { effectiveThinkingOptionId } : {},
|
|
32687
|
+
...effectivePermissionModeId ? { effectivePermissionModeId } : {}
|
|
32688
|
+
};
|
|
32689
|
+
thread.session = session;
|
|
32690
|
+
thread.record = aligned.record;
|
|
32691
|
+
thread.turns = aligned.turns;
|
|
32692
|
+
thread.historyHydrated = true;
|
|
32693
|
+
thread.sessionUnavailable = false;
|
|
32694
|
+
thread.persistenceError = null;
|
|
32695
|
+
thread.transportModelId = transportModelId;
|
|
32696
|
+
if (effectiveModel) thread.requestedModel = effectiveModel;
|
|
32697
|
+
if (effectiveThinkingOptionId) thread.requestedThinkingOptionId = effectiveThinkingOptionId;
|
|
32698
|
+
if (effectivePermissionModeId) thread.requestedPermissionModeId = effectivePermissionModeId;
|
|
32699
|
+
thread.stateObserver = new SessionStateObserver(observerState);
|
|
32700
|
+
thread.latestUsage = session.initialUsage;
|
|
32701
|
+
thread.usageMeter = new UsageMeter();
|
|
32702
|
+
thread.usageTurnId = null;
|
|
32703
|
+
thread.thread = externalThreadValue({
|
|
32704
|
+
record: aligned.record,
|
|
32705
|
+
turns: aligned.turns,
|
|
32706
|
+
sessionId: thread.sessionId,
|
|
32707
|
+
running: thread.running
|
|
32708
|
+
});
|
|
32709
|
+
thread.outputTask = this.#consumeOutputs(thread);
|
|
32710
|
+
this.idleRelease.clearOutputFailed(thread);
|
|
32711
|
+
return null;
|
|
32712
|
+
} catch (error51) {
|
|
32713
|
+
if (error51 instanceof ExternalSessionReleaseError) throw error51;
|
|
32714
|
+
await this.#lifecycle.release(thread.id, session, Promise.resolve());
|
|
32715
|
+
this.#diagnose(error51);
|
|
32716
|
+
await this.#dropIdleSession(thread);
|
|
32717
|
+
return { code: -32081, message: "External Thread history could not be persisted" };
|
|
32718
|
+
}
|
|
32719
|
+
}
|
|
32369
32720
|
async persistTerminalIdentity(thread, event) {
|
|
32370
32721
|
if (thread.persistenceError) return thread.persistenceError;
|
|
32371
32722
|
if (!event.nativeTurnRef) {
|
|
@@ -32504,7 +32855,8 @@ var ExternalThreadRuntime = class {
|
|
|
32504
32855
|
transportModelId
|
|
32505
32856
|
});
|
|
32506
32857
|
} catch (error51) {
|
|
32507
|
-
|
|
32858
|
+
if (error51 instanceof ExternalSessionReleaseError) throw error51;
|
|
32859
|
+
await this.#lifecycle.release(record4.hostThreadId, session, Promise.resolve());
|
|
32508
32860
|
throw error51;
|
|
32509
32861
|
}
|
|
32510
32862
|
}
|
|
@@ -32905,7 +33257,7 @@ function projectDelegationThreadSnapshot(input) {
|
|
|
32905
33257
|
// packages/host-runtime/src/harness-delegation-coordinator.ts
|
|
32906
33258
|
var IMPLICIT_DEDUPLICATION_MS = 3e4;
|
|
32907
33259
|
var NATIVE_REF_TIMEOUT_MS = 1e4;
|
|
32908
|
-
function
|
|
33260
|
+
function delay3(milliseconds) {
|
|
32909
33261
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
32910
33262
|
}
|
|
32911
33263
|
function terminal(status2) {
|
|
@@ -33295,7 +33647,7 @@ var HarnessDelegationCoordinator = class {
|
|
|
33295
33647
|
if (terminal(snapshot2.status)) return { ...snapshot2, timedOut: false };
|
|
33296
33648
|
const remaining = deadline - Date.now();
|
|
33297
33649
|
if (remaining <= 0) return { ...snapshot2, timedOut: true };
|
|
33298
|
-
await
|
|
33650
|
+
await delay3(Math.min(100, remaining));
|
|
33299
33651
|
}
|
|
33300
33652
|
}
|
|
33301
33653
|
async list(input) {
|
|
@@ -35429,15 +35781,21 @@ var HarnessPluginRegistry = class {
|
|
|
35429
35781
|
};
|
|
35430
35782
|
|
|
35431
35783
|
// packages/host-runtime/src/harness-plugin-loader.ts
|
|
35784
|
+
function hasValidSessionUsage(value2) {
|
|
35785
|
+
const capability = Reflect.get(value2, "sessionUsage");
|
|
35786
|
+
return capability === void 0 || capability !== null && typeof capability === "object" && ["observe", "read"].every((key) => typeof Reflect.get(capability, key) === "function") && ["requestOptions", "shouldForwardNotification", "reset"].every(
|
|
35787
|
+
(key) => Reflect.get(capability, key) === void 0 || typeof Reflect.get(capability, key) === "function"
|
|
35788
|
+
);
|
|
35789
|
+
}
|
|
35432
35790
|
function isAdapter(value2) {
|
|
35433
35791
|
if (!value2 || typeof value2 !== "object") return false;
|
|
35434
|
-
return ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
|
|
35792
|
+
return hasValidSessionUsage(value2) && ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
|
|
35435
35793
|
}
|
|
35436
35794
|
function isUsageAdapter(value2) {
|
|
35437
35795
|
if (!value2 || typeof value2 !== "object" || typeof Reflect.get(value2, "close") !== "function")
|
|
35438
35796
|
return false;
|
|
35439
35797
|
const capability = Reflect.get(value2, "usageStatistics");
|
|
35440
|
-
return !!capability && typeof capability === "object" && ["listSources", "readSource"].every(
|
|
35798
|
+
return hasValidSessionUsage(value2) && !!capability && typeof capability === "object" && ["listSources", "readSource"].every(
|
|
35441
35799
|
(key) => typeof Reflect.get(capability, key) === "function"
|
|
35442
35800
|
) && !("open" in value2) && !("inspect" in value2);
|
|
35443
35801
|
}
|
|
@@ -35578,11 +35936,12 @@ async function loadHarnessPlugins(options2) {
|
|
|
35578
35936
|
}
|
|
35579
35937
|
if (options2.signal?.aborted) return registry2;
|
|
35580
35938
|
let next = 0;
|
|
35939
|
+
let batch = pending;
|
|
35581
35940
|
const loaded = /* @__PURE__ */ new Map();
|
|
35582
35941
|
const worker = async () => {
|
|
35583
35942
|
for (; ; ) {
|
|
35584
35943
|
if (options2.signal?.aborted) return;
|
|
35585
|
-
const candidate =
|
|
35944
|
+
const candidate = batch[next++];
|
|
35586
35945
|
if (!candidate) return;
|
|
35587
35946
|
if (options2.signal?.aborted) return;
|
|
35588
35947
|
const { manifest } = candidate;
|
|
@@ -35628,10 +35987,19 @@ async function loadHarnessPlugins(options2) {
|
|
|
35628
35987
|
loaded.set(candidate, { descriptor, adapter });
|
|
35629
35988
|
}
|
|
35630
35989
|
};
|
|
35631
|
-
|
|
35632
|
-
|
|
35633
|
-
|
|
35634
|
-
|
|
35990
|
+
const batches = options2.onUsageAdaptersLoaded ? [
|
|
35991
|
+
pending.filter((c) => c.manifest.kind === "usage"),
|
|
35992
|
+
pending.filter((c) => c.manifest.kind !== "usage")
|
|
35993
|
+
] : [pending];
|
|
35994
|
+
for (const [index, candidates2] of batches.entries()) {
|
|
35995
|
+
batch = candidates2;
|
|
35996
|
+
next = 0;
|
|
35997
|
+
await Promise.all(Array.from({ length: Math.min(4, batch.length) }, () => worker()));
|
|
35998
|
+
for (const candidate of batch) {
|
|
35999
|
+
const entry = loaded.get(candidate);
|
|
36000
|
+
if (entry) await registry2.register(entry.descriptor, entry.adapter);
|
|
36001
|
+
}
|
|
36002
|
+
if (index === 0) options2.onUsageAdaptersLoaded?.(registry2.usageAdapters);
|
|
35635
36003
|
}
|
|
35636
36004
|
return registry2;
|
|
35637
36005
|
}
|
|
@@ -36533,16 +36901,16 @@ var OfficialRuntimeScope = class {
|
|
|
36533
36901
|
#scheduleRecovery() {
|
|
36534
36902
|
if (!this.#recovery || this.#closed || this.#recoveryTimer || this.#starting) return;
|
|
36535
36903
|
const { delaysMs } = this.#recovery;
|
|
36536
|
-
const
|
|
36904
|
+
const delay6 = delaysMs[Math.min(this.#recoveryAttempt, delaysMs.length - 1)] ?? 0;
|
|
36537
36905
|
this.#recoveryAttempt++;
|
|
36538
36906
|
this.#diagnosticOutput.write(
|
|
36539
|
-
`codexhost: official Codex is unavailable; restarting in ${String(
|
|
36907
|
+
`codexhost: official Codex is unavailable; restarting in ${String(delay6)}ms
|
|
36540
36908
|
`
|
|
36541
36909
|
);
|
|
36542
36910
|
const timer = setTimeout(() => {
|
|
36543
36911
|
this.#recoveryTimer = void 0;
|
|
36544
36912
|
this.start().catch(() => void 0);
|
|
36545
|
-
},
|
|
36913
|
+
}, delay6);
|
|
36546
36914
|
timer.unref();
|
|
36547
36915
|
this.#recoveryTimer = timer;
|
|
36548
36916
|
}
|
|
@@ -37289,11 +37657,163 @@ async function moveThreadSection(input) {
|
|
|
37289
37657
|
}
|
|
37290
37658
|
}
|
|
37291
37659
|
|
|
37660
|
+
// packages/host-runtime/src/observed-session-usage.ts
|
|
37661
|
+
var ObservedSessionUsage = class {
|
|
37662
|
+
constructor(providers, prices, diagnose) {
|
|
37663
|
+
this.providers = providers;
|
|
37664
|
+
this.prices = prices;
|
|
37665
|
+
this.diagnose = diagnose;
|
|
37666
|
+
}
|
|
37667
|
+
providers;
|
|
37668
|
+
prices;
|
|
37669
|
+
diagnose;
|
|
37670
|
+
#states = /* @__PURE__ */ new Map();
|
|
37671
|
+
#pending = /* @__PURE__ */ new Map();
|
|
37672
|
+
#abort = new AbortController();
|
|
37673
|
+
#initializeParams = null;
|
|
37674
|
+
initialized(params) {
|
|
37675
|
+
this.#initializeParams = params;
|
|
37676
|
+
for (const state of this.#states.values()) {
|
|
37677
|
+
state.meter.resetTurnTiming();
|
|
37678
|
+
state.activeTurnId = null;
|
|
37679
|
+
}
|
|
37680
|
+
for (const provider of this.providers()) {
|
|
37681
|
+
try {
|
|
37682
|
+
provider.reset?.();
|
|
37683
|
+
} catch (error51) {
|
|
37684
|
+
this.diagnose(error51);
|
|
37685
|
+
}
|
|
37686
|
+
}
|
|
37687
|
+
}
|
|
37688
|
+
requestOptions(method, params) {
|
|
37689
|
+
if (this.#abort.signal.aborted) return null;
|
|
37690
|
+
let result = null;
|
|
37691
|
+
for (const provider of this.providers()) {
|
|
37692
|
+
try {
|
|
37693
|
+
const patch = provider.requestOptions?.({
|
|
37694
|
+
method,
|
|
37695
|
+
params,
|
|
37696
|
+
initializeParams: this.#initializeParams
|
|
37697
|
+
});
|
|
37698
|
+
if (patch) result = Object.assign(result ?? {}, jsonObjectSchema.parse(patch));
|
|
37699
|
+
} catch (error51) {
|
|
37700
|
+
this.diagnose(error51);
|
|
37701
|
+
}
|
|
37702
|
+
}
|
|
37703
|
+
return result;
|
|
37704
|
+
}
|
|
37705
|
+
shouldForwardNotification(message2) {
|
|
37706
|
+
if (this.#abort.signal.aborted) return true;
|
|
37707
|
+
for (const provider of this.providers()) {
|
|
37708
|
+
try {
|
|
37709
|
+
if (provider.shouldForwardNotification?.(message2) === false) return false;
|
|
37710
|
+
} catch (error51) {
|
|
37711
|
+
this.diagnose(error51);
|
|
37712
|
+
}
|
|
37713
|
+
}
|
|
37714
|
+
return true;
|
|
37715
|
+
}
|
|
37716
|
+
observe(message2, now = Date.now()) {
|
|
37717
|
+
if (this.#abort.signal.aborted) return [];
|
|
37718
|
+
const changed = /* @__PURE__ */ new Set();
|
|
37719
|
+
for (const provider of this.providers()) {
|
|
37720
|
+
try {
|
|
37721
|
+
const observation = provider.observe(message2, now);
|
|
37722
|
+
if (!observation) continue;
|
|
37723
|
+
const state = this.#state(observation.sessionId, provider);
|
|
37724
|
+
if (state.provider !== provider) continue;
|
|
37725
|
+
if (observation.usage) {
|
|
37726
|
+
state.live = parseHostUsage(observation.usage);
|
|
37727
|
+
state.revision++;
|
|
37728
|
+
}
|
|
37729
|
+
const turn = observation.turn;
|
|
37730
|
+
if (turn?.phase === "started" && state.activeTurnId !== turn.id) {
|
|
37731
|
+
state.meter.resetTurnTiming();
|
|
37732
|
+
state.meter.turnStarted(turn.id, now);
|
|
37733
|
+
state.activeTurnId = turn.id;
|
|
37734
|
+
}
|
|
37735
|
+
const timing = observation.requestTiming;
|
|
37736
|
+
if (timing) state.meter.recordOutputTiming(timing, timing.turnId);
|
|
37737
|
+
if (observation.timingUnavailable)
|
|
37738
|
+
state.meter.invalidateOutputTiming(observation.timingUnavailable.turnId);
|
|
37739
|
+
if (turn?.phase === "completed") {
|
|
37740
|
+
state.meter.turnCompleted(turn.id);
|
|
37741
|
+
if (state.activeTurnId === turn.id) state.activeTurnId = null;
|
|
37742
|
+
}
|
|
37743
|
+
const firstOutput = turn?.phase === "output" && state.meter.outputObserved(turn.id, now);
|
|
37744
|
+
if (observation.usage || firstOutput || timing || observation.timingUnavailable || turn?.phase === "started" || turn?.phase === "completed")
|
|
37745
|
+
changed.add(observation.sessionId);
|
|
37746
|
+
} catch (error51) {
|
|
37747
|
+
this.diagnose(error51);
|
|
37748
|
+
}
|
|
37749
|
+
}
|
|
37750
|
+
return [...changed];
|
|
37751
|
+
}
|
|
37752
|
+
async read(sessionId) {
|
|
37753
|
+
if (this.#abort.signal.aborted) return null;
|
|
37754
|
+
let pending = this.#pending.get(sessionId);
|
|
37755
|
+
if (!pending) {
|
|
37756
|
+
pending = this.#refresh(sessionId).finally(() => this.#pending.delete(sessionId));
|
|
37757
|
+
this.#pending.set(sessionId, pending);
|
|
37758
|
+
}
|
|
37759
|
+
await pending;
|
|
37760
|
+
const state = this.#states.get(sessionId);
|
|
37761
|
+
if (!state || this.#abort.signal.aborted) return null;
|
|
37762
|
+
const native = state.live ?? state.history;
|
|
37763
|
+
const result = state.meter.derive(native, await this.prices.lookup());
|
|
37764
|
+
if (result?.unpricedModels?.length) this.prices.missing();
|
|
37765
|
+
return result;
|
|
37766
|
+
}
|
|
37767
|
+
async #refresh(sessionId) {
|
|
37768
|
+
const existing = this.#states.get(sessionId);
|
|
37769
|
+
for (const provider of existing ? [existing.provider] : this.providers()) {
|
|
37770
|
+
const revision = existing?.revision ?? 0;
|
|
37771
|
+
try {
|
|
37772
|
+
const history = await provider.read(sessionId, this.#abort.signal);
|
|
37773
|
+
if (this.#abort.signal.aborted) return;
|
|
37774
|
+
if (!history) {
|
|
37775
|
+
if (existing?.meter.metered) existing.meter.recordHistory(false);
|
|
37776
|
+
continue;
|
|
37777
|
+
}
|
|
37778
|
+
const state = this.#state(sessionId, provider);
|
|
37779
|
+
state.history = history.usage ? parseHostUsage(history.usage) : null;
|
|
37780
|
+
state.meter.replaceHistory(
|
|
37781
|
+
history.requests,
|
|
37782
|
+
history.complete && state.revision === revision
|
|
37783
|
+
);
|
|
37784
|
+
return;
|
|
37785
|
+
} catch (error51) {
|
|
37786
|
+
existing?.meter.recordHistory(false);
|
|
37787
|
+
if (!this.#abort.signal.aborted) this.diagnose(error51);
|
|
37788
|
+
}
|
|
37789
|
+
}
|
|
37790
|
+
}
|
|
37791
|
+
#state(id2, provider) {
|
|
37792
|
+
let state = this.#states.get(id2);
|
|
37793
|
+
if (!state) {
|
|
37794
|
+
state = {
|
|
37795
|
+
provider,
|
|
37796
|
+
meter: new UsageMeter(),
|
|
37797
|
+
live: null,
|
|
37798
|
+
history: null,
|
|
37799
|
+
revision: 0,
|
|
37800
|
+
activeTurnId: null
|
|
37801
|
+
};
|
|
37802
|
+
this.#states.set(id2, state);
|
|
37803
|
+
}
|
|
37804
|
+
return state;
|
|
37805
|
+
}
|
|
37806
|
+
close() {
|
|
37807
|
+
this.#abort.abort();
|
|
37808
|
+
this.#states.clear();
|
|
37809
|
+
}
|
|
37810
|
+
};
|
|
37811
|
+
|
|
37292
37812
|
// packages/host-runtime/src/app-server-host.ts
|
|
37293
37813
|
var CONSOLE_REQUEST_TIMEOUT_MS = 12e4;
|
|
37294
37814
|
var SUBAGENT_TERMINAL_REFRESH_DELAYS_MS = [0, 50, 100, 150];
|
|
37295
37815
|
var THREAD_USAGE_UPDATED_METHOD = "codexhost/thread/usage/updated";
|
|
37296
|
-
function
|
|
37816
|
+
function delay4(milliseconds) {
|
|
37297
37817
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
37298
37818
|
}
|
|
37299
37819
|
function isRecord18(value2) {
|
|
@@ -37486,10 +38006,12 @@ var AppServerHost = class {
|
|
|
37486
38006
|
#launchSettings;
|
|
37487
38007
|
#accountInspections = new HarnessAccountInspectionCache();
|
|
37488
38008
|
#externalRuntime;
|
|
38009
|
+
#executionHosts = /* @__PURE__ */ new WeakMap();
|
|
37489
38010
|
#externalPrewarms = new ExternalThreadPrewarms();
|
|
37490
38011
|
#externalSteering = new ExternalTurnSteering();
|
|
37491
38012
|
#liveCommandCache = new LiveCommandCatalogCache();
|
|
37492
38013
|
#modelPrices;
|
|
38014
|
+
#observedSessionUsage;
|
|
37493
38015
|
#repository;
|
|
37494
38016
|
#pendingDesktopApprovals = /* @__PURE__ */ new Map();
|
|
37495
38017
|
#pendingDesktopQuestions = /* @__PURE__ */ new Map();
|
|
@@ -37506,7 +38028,6 @@ var AppServerHost = class {
|
|
|
37506
38028
|
#activeWorkDrainWaiters = /* @__PURE__ */ new Set();
|
|
37507
38029
|
#pendingOfficialDelegationThreads = /* @__PURE__ */ new Set();
|
|
37508
38030
|
#pendingOfficialTerminalStatuses = /* @__PURE__ */ new Map();
|
|
37509
|
-
#officialUsageByThread = /* @__PURE__ */ new Map();
|
|
37510
38031
|
#officialRateLimits = new AccountRateLimits();
|
|
37511
38032
|
#routeObservationTracker = new RequestRouteObservationTracker();
|
|
37512
38033
|
#officialServerRequests = /* @__PURE__ */ new Map();
|
|
@@ -37524,6 +38045,7 @@ var AppServerHost = class {
|
|
|
37524
38045
|
#closeRequested = false;
|
|
37525
38046
|
#pluginLoadAbort = new AbortController();
|
|
37526
38047
|
#pluginLoading;
|
|
38048
|
+
#usagePluginsReady;
|
|
37527
38049
|
#desktopRequests = new DesktopRequestQueue();
|
|
37528
38050
|
#drainActiveWorkOnInputEnd = false;
|
|
37529
38051
|
#desktopInputEnded = false;
|
|
@@ -37535,6 +38057,13 @@ var AppServerHost = class {
|
|
|
37535
38057
|
...options2
|
|
37536
38058
|
};
|
|
37537
38059
|
this.#modelPrices = options2.modelPrices ?? new ModelPriceCatalog();
|
|
38060
|
+
this.#observedSessionUsage = new ObservedSessionUsage(
|
|
38061
|
+
() => [...this.#externalAdapters.values(), ...this.#usageOnlyAdapters].flatMap(
|
|
38062
|
+
(adapter) => adapter.sessionUsage ? [adapter.sessionUsage] : []
|
|
38063
|
+
),
|
|
38064
|
+
this.#modelPrices,
|
|
38065
|
+
(error51) => this.#diagnose(error51)
|
|
38066
|
+
);
|
|
37538
38067
|
this.#writer = new OrderedWriter(this.#options.desktopOutput, (value2) => {
|
|
37539
38068
|
this.#noteDesktopReply(value2);
|
|
37540
38069
|
return this.#takeConsoleReply(value2);
|
|
@@ -37612,6 +38141,7 @@ var AppServerHost = class {
|
|
|
37612
38141
|
consumeOutputs: (thread) => this.#consumeHarnessOutputs(thread),
|
|
37613
38142
|
diagnose: (error51) => this.#diagnose(error51),
|
|
37614
38143
|
subagentRunning: (threadId2) => this.#subagentThreadStatuses.get(threadId2) === "active",
|
|
38144
|
+
hasRunningDescendants: (threadId2) => this.#hasRunningSubagents(threadId2),
|
|
37615
38145
|
idleRelease: {
|
|
37616
38146
|
queue: this.#desktopRequests,
|
|
37617
38147
|
onClosed: async (thread) => {
|
|
@@ -37670,7 +38200,11 @@ var AppServerHost = class {
|
|
|
37670
38200
|
wait: (input) => this.#waitForPlugins().then(() => this.#delegationCoordinator.wait(input)),
|
|
37671
38201
|
list: (input) => this.#waitForPlugins().then(() => this.#delegationCoordinator.list(input)),
|
|
37672
38202
|
canHandleStart: (input) => this.#canHandleDelegationStart(input),
|
|
37673
|
-
ownsThread: (threadId2) => this.#ownsDelegationThread(threadId2)
|
|
38203
|
+
ownsThread: (threadId2) => this.#ownsDelegationThread(threadId2),
|
|
38204
|
+
executionHostId: (threadId2) => {
|
|
38205
|
+
const thread = this.#externalRuntime.get(threadId2);
|
|
38206
|
+
return thread ? this.#executionHosts.get(thread) : void 0;
|
|
38207
|
+
}
|
|
37674
38208
|
});
|
|
37675
38209
|
this.#unregisterDelegationApi = typeof unregisterDelegationApi === "function" ? unregisterDelegationApi : void 0;
|
|
37676
38210
|
}
|
|
@@ -37702,18 +38236,29 @@ var AppServerHost = class {
|
|
|
37702
38236
|
this.#options.usageStatistics?.attach(
|
|
37703
38237
|
() => [.../* @__PURE__ */ new Set([...this.#externalAdapters.values(), ...this.#usageOnlyAdapters])].flatMap(
|
|
37704
38238
|
(adapter) => adapter.usageStatistics ? [{ harness: adapter.harnessId, capability: adapter.usageStatistics }] : []
|
|
37705
|
-
)
|
|
38239
|
+
),
|
|
38240
|
+
() => this.#repository.list()
|
|
37706
38241
|
);
|
|
37707
38242
|
}
|
|
37708
38243
|
#waitForPlugins() {
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
38244
|
+
if (!this.#pluginLoading) {
|
|
38245
|
+
const ready = Promise.withResolvers();
|
|
38246
|
+
const usageReady = () => ready.resolve(void 0);
|
|
38247
|
+
this.#usagePluginsReady = ready.promise;
|
|
38248
|
+
this.#pluginLoading = this.#loadInstalledPlugins(usageReady).catch((error51) => {
|
|
38249
|
+
this.#diagnose(`Harness plugin load failed: ${errorMessage4(error51)}`);
|
|
38250
|
+
}).finally(usageReady);
|
|
38251
|
+
}
|
|
38252
|
+
return this.#pluginLoading;
|
|
37712
38253
|
}
|
|
37713
|
-
async #loadInstalledPlugins() {
|
|
38254
|
+
async #loadInstalledPlugins(usageReady) {
|
|
37714
38255
|
if (!this.#options.pluginRoots || this.#pluginLoadAbort.signal.aborted) return;
|
|
37715
38256
|
const plugins = await loadHarnessPlugins({
|
|
37716
38257
|
roots: this.#options.pluginRoots,
|
|
38258
|
+
onUsageAdaptersLoaded: (adapters) => {
|
|
38259
|
+
if (!this.#pluginLoadAbort.signal.aborted) this.#usageOnlyAdapters = [...adapters.values()];
|
|
38260
|
+
usageReady();
|
|
38261
|
+
},
|
|
37717
38262
|
launchCommandForPlugin: (id2) => this.#launchSettings.initialCommand(id2),
|
|
37718
38263
|
context: this.#options.pluginContext ?? {
|
|
37719
38264
|
environment: this.#options.environment ?? process.env,
|
|
@@ -37734,6 +38279,7 @@ var AppServerHost = class {
|
|
|
37734
38279
|
for (const [id2, adapter] of plugins.adapters) this.#externalAdapters.set(id2, adapter);
|
|
37735
38280
|
}
|
|
37736
38281
|
async #closeAdapters() {
|
|
38282
|
+
this.#observedSessionUsage.close();
|
|
37737
38283
|
const results = await Promise.allSettled([
|
|
37738
38284
|
this.#plugins?.close(),
|
|
37739
38285
|
...[...new Set(this.#options.externalAdapters?.values())].map(
|
|
@@ -37795,6 +38341,7 @@ var AppServerHost = class {
|
|
|
37795
38341
|
await Promise.allSettled(threads.map(({ session }) => session.close()));
|
|
37796
38342
|
await Promise.allSettled(threads.map(({ outputTask }) => outputTask));
|
|
37797
38343
|
await this.#closeAdapters();
|
|
38344
|
+
await this.#externalRuntime.drain();
|
|
37798
38345
|
for (const pending of [...this.#pendingDesktopApprovals.values()]) {
|
|
37799
38346
|
await this.#resolveDesktopApproval(pending.interaction.interactionId).catch(
|
|
37800
38347
|
() => void 0
|
|
@@ -37937,7 +38484,13 @@ var AppServerHost = class {
|
|
|
37937
38484
|
if (request.method === "initialize") {
|
|
37938
38485
|
try {
|
|
37939
38486
|
const nativeGeneration = this.#officialRuntimeScope.gate.phase === "ready" ? this.#officialRuntimeScope.owner.generation : void 0;
|
|
37940
|
-
|
|
38487
|
+
void this.#waitForPlugins();
|
|
38488
|
+
await this.#usagePluginsReady;
|
|
38489
|
+
const params = requestObject(request);
|
|
38490
|
+
const options2 = this.#observedSessionUsage.requestOptions("initialize", params);
|
|
38491
|
+
const nativeParams = options2 ? { ...params, ...options2 } : params;
|
|
38492
|
+
const response = await this.#officialRuntime.initializeProtocol(nativeParams);
|
|
38493
|
+
if (!isRecord18(response.error)) this.#observedSessionUsage.initialized(nativeParams);
|
|
37941
38494
|
await this.#writer.json({ ...response, id: request.id });
|
|
37942
38495
|
this.#nativeAccountObserver?.initialized(nativeGeneration);
|
|
37943
38496
|
} catch (error51) {
|
|
@@ -38768,6 +39321,15 @@ var AppServerHost = class {
|
|
|
38768
39321
|
return;
|
|
38769
39322
|
}
|
|
38770
39323
|
try {
|
|
39324
|
+
const options2 = isRecord18(request.params) ? this.#observedSessionUsage.requestOptions(request.method, request.params) : null;
|
|
39325
|
+
if (options2) {
|
|
39326
|
+
const original = parseJsonFrame(frame);
|
|
39327
|
+
if (isRecord18(original))
|
|
39328
|
+
frame = encodeJsonFrame({
|
|
39329
|
+
...original,
|
|
39330
|
+
params: { ...isRecord18(original.params) ? original.params : {}, ...options2 }
|
|
39331
|
+
});
|
|
39332
|
+
}
|
|
38771
39333
|
await this.#officialRuntime.sendFrame(frame);
|
|
38772
39334
|
this.#markDesktopRequestAnswered(request.id);
|
|
38773
39335
|
} catch {
|
|
@@ -38786,6 +39348,7 @@ var AppServerHost = class {
|
|
|
38786
39348
|
return;
|
|
38787
39349
|
}
|
|
38788
39350
|
const parsed2 = input.value;
|
|
39351
|
+
const observedAtMs = Date.now();
|
|
38789
39352
|
this.#observeOfficialTurnStartResponse(parsed2);
|
|
38790
39353
|
let forwarded = parsed2;
|
|
38791
39354
|
if (isRecord18(parsed2) && typeof parsed2.method === "string" && "id" in parsed2) {
|
|
@@ -38800,17 +39363,6 @@ var AppServerHost = class {
|
|
|
38800
39363
|
if (accountScopedNotification) {
|
|
38801
39364
|
if (parsed2.method === "account/updated") this.#officialRateLimits.reset(input.accountId);
|
|
38802
39365
|
}
|
|
38803
|
-
const tokenUsage = observeCodexTokenUsage(parsed2);
|
|
38804
|
-
if (tokenUsage) {
|
|
38805
|
-
const previous = this.#officialUsageByThread.get(tokenUsage.threadId);
|
|
38806
|
-
try {
|
|
38807
|
-
this.#officialUsageByThread.set(
|
|
38808
|
-
tokenUsage.threadId,
|
|
38809
|
-
parseHostUsage({ ...previous ?? {}, ...tokenUsage.usage })
|
|
38810
|
-
);
|
|
38811
|
-
} catch {
|
|
38812
|
-
}
|
|
38813
|
-
}
|
|
38814
39366
|
const rateLimits = observeCodexRateLimits(parsed2);
|
|
38815
39367
|
if (rateLimits) this.#officialRateLimits.observe(input.accountId, rateLimits);
|
|
38816
39368
|
try {
|
|
@@ -38819,8 +39371,16 @@ var AppServerHost = class {
|
|
|
38819
39371
|
this.#diagnose(error51);
|
|
38820
39372
|
}
|
|
38821
39373
|
this.#routeObservationTracker.bindOfficialResponse(parsed2);
|
|
38822
|
-
|
|
38823
|
-
|
|
39374
|
+
const privateTelemetry = isRecord18(parsed2) && typeof parsed2.method === "string" && !("id" in parsed2) && !this.#observedSessionUsage.shouldForwardNotification(parsed2);
|
|
39375
|
+
if (!privateTelemetry) {
|
|
39376
|
+
if (forwarded === parsed2) await this.#writer.frame(input.frame);
|
|
39377
|
+
else await this.#writer.json(forwarded);
|
|
39378
|
+
}
|
|
39379
|
+
void (this.#usagePluginsReady ?? Promise.resolve()).then(async () => {
|
|
39380
|
+
for (const threadId2 of this.#observedSessionUsage.observe(parsed2, observedAtMs)) {
|
|
39381
|
+
await this.#writer.json({ method: THREAD_USAGE_UPDATED_METHOD, params: { threadId: threadId2 } });
|
|
39382
|
+
}
|
|
39383
|
+
}).catch((error51) => this.#diagnose(error51));
|
|
38824
39384
|
this.#nativeAccountObserver?.observe(parsed2);
|
|
38825
39385
|
const deletedProjectId = observeDeletedProject(parsed2);
|
|
38826
39386
|
if (deletedProjectId) {
|
|
@@ -39761,12 +40321,13 @@ var AppServerHost = class {
|
|
|
39761
40321
|
);
|
|
39762
40322
|
return;
|
|
39763
40323
|
}
|
|
40324
|
+
await this.#waitForPlugins();
|
|
39764
40325
|
const accountId = await this.#currentCodexAccountId();
|
|
39765
40326
|
if (accountId) await this.#refreshOfficialRateLimits(accountId);
|
|
39766
40327
|
const accountCredits = this.#officialAccountCredits(accountId ?? void 0);
|
|
39767
40328
|
const result2 = threadUsageInspectionSchema.parse({
|
|
39768
40329
|
threadId: params.data.threadId,
|
|
39769
|
-
usage: this.#
|
|
40330
|
+
usage: await this.#observedSessionUsage.read(params.data.threadId),
|
|
39770
40331
|
...accountCredits ? { accountCredits } : {}
|
|
39771
40332
|
});
|
|
39772
40333
|
await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result2) }));
|
|
@@ -39974,7 +40535,7 @@ var AppServerHost = class {
|
|
|
39974
40535
|
this.#pendingExternalCommandRequests.delete(thread.id);
|
|
39975
40536
|
}
|
|
39976
40537
|
}
|
|
39977
|
-
async #beginExternalCommand(thread, descriptor, arguments_2, requestedTurnId) {
|
|
40538
|
+
async #beginExternalCommand(thread, descriptor, arguments_2, requestedTurnId, sourceHostId) {
|
|
39978
40539
|
const commands = thread.session.commands;
|
|
39979
40540
|
if (!commands) {
|
|
39980
40541
|
throw new ExternalCommandError(-32078, "External Harness does not expose commands");
|
|
@@ -40003,6 +40564,7 @@ var AppServerHost = class {
|
|
|
40003
40564
|
thread.ephemeralTurnIds.add(turnId);
|
|
40004
40565
|
if (descriptor.invocation === "/compact") this.#manualCompactionTurns.set(thread, turnId);
|
|
40005
40566
|
else this.#manualCompactionTurns.delete(thread);
|
|
40567
|
+
const restoreExecutionHost = this.#setExecutionHost(thread, sourceHostId);
|
|
40006
40568
|
try {
|
|
40007
40569
|
if (thread.unsubmittedPrewarm && await this.#externalRuntime.submitPrewarm(thread)) {
|
|
40008
40570
|
await this.#notifyExternalThreadStarted(thread.thread);
|
|
@@ -40015,6 +40577,7 @@ var AppServerHost = class {
|
|
|
40015
40577
|
if (!result.ok) throw new ExternalCommandError(-32073, result.error.message);
|
|
40016
40578
|
return { turnId: result.value.turnId, turn: projection.projector.pendingTurn(), gate };
|
|
40017
40579
|
} catch (error51) {
|
|
40580
|
+
restoreExecutionHost();
|
|
40018
40581
|
thread.running = false;
|
|
40019
40582
|
thread.activeTurnId = null;
|
|
40020
40583
|
thread.projectedTurns.delete(turnId);
|
|
@@ -40794,7 +41357,8 @@ var AppServerHost = class {
|
|
|
40794
41357
|
thread,
|
|
40795
41358
|
text,
|
|
40796
41359
|
void 0,
|
|
40797
|
-
typeof params.clientUserMessageId === "string" ? params.clientUserMessageId : void 0
|
|
41360
|
+
typeof params.clientUserMessageId === "string" ? params.clientUserMessageId : void 0,
|
|
41361
|
+
params[THREAD_REFERENCE_SOURCE_HOST_PARAM]
|
|
40798
41362
|
);
|
|
40799
41363
|
try {
|
|
40800
41364
|
await this.#writer.json(rpcEnvelope(request, { result: { turn: started.turn } }));
|
|
@@ -40820,7 +41384,8 @@ var AppServerHost = class {
|
|
|
40820
41384
|
thread,
|
|
40821
41385
|
text,
|
|
40822
41386
|
assertActive,
|
|
40823
|
-
typeof requestObject(request).clientUserMessageId === "string" ? requestObject(request).clientUserMessageId : void 0
|
|
41387
|
+
typeof requestObject(request).clientUserMessageId === "string" ? requestObject(request).clientUserMessageId : void 0,
|
|
41388
|
+
requestObject(request)[THREAD_REFERENCE_SOURCE_HOST_PARAM]
|
|
40824
41389
|
)
|
|
40825
41390
|
);
|
|
40826
41391
|
try {
|
|
@@ -40841,12 +41406,12 @@ var AppServerHost = class {
|
|
|
40841
41406
|
}
|
|
40842
41407
|
}
|
|
40843
41408
|
/** Keep command restoration and dispatch identical for start and steer submissions. */
|
|
40844
|
-
async #beginExternalInputTurn(thread, inputText, assertActive, clientUserMessageId) {
|
|
41409
|
+
async #beginExternalInputTurn(thread, inputText, assertActive, clientUserMessageId, sourceHostId) {
|
|
40845
41410
|
const text = restoreHarnessCommandMentions(inputText);
|
|
40846
41411
|
const commands = thread.session.commands;
|
|
40847
41412
|
if (!commands || !isExternalCommandCandidate(text)) {
|
|
40848
41413
|
assertActive?.();
|
|
40849
|
-
return this.#beginExternalTurn(thread, text, clientUserMessageId);
|
|
41414
|
+
return this.#beginExternalTurn(thread, text, clientUserMessageId, sourceHostId);
|
|
40850
41415
|
}
|
|
40851
41416
|
this.#pendingExternalCommandRequests.add(thread.id);
|
|
40852
41417
|
try {
|
|
@@ -40857,9 +41422,15 @@ var AppServerHost = class {
|
|
|
40857
41422
|
assertActive?.();
|
|
40858
41423
|
if (!command) {
|
|
40859
41424
|
this.#pendingExternalCommandRequests.delete(thread.id);
|
|
40860
|
-
return await this.#beginExternalTurn(thread, text, clientUserMessageId);
|
|
41425
|
+
return await this.#beginExternalTurn(thread, text, clientUserMessageId, sourceHostId);
|
|
40861
41426
|
}
|
|
40862
|
-
return await this.#beginExternalCommand(
|
|
41427
|
+
return await this.#beginExternalCommand(
|
|
41428
|
+
thread,
|
|
41429
|
+
command.descriptor,
|
|
41430
|
+
command.arguments,
|
|
41431
|
+
void 0,
|
|
41432
|
+
sourceHostId
|
|
41433
|
+
);
|
|
40863
41434
|
} catch (error51) {
|
|
40864
41435
|
if (error51 instanceof ExternalCommandError || error51 instanceof ExternalSteerError) throw error51;
|
|
40865
41436
|
this.#diagnose(error51);
|
|
@@ -40871,7 +41442,20 @@ var AppServerHost = class {
|
|
|
40871
41442
|
this.#pendingExternalCommandRequests.delete(thread.id);
|
|
40872
41443
|
}
|
|
40873
41444
|
}
|
|
40874
|
-
|
|
41445
|
+
/** Apply context before native execution; a rejected start restores the prior identity. */
|
|
41446
|
+
#setExecutionHost(thread, sourceHostId) {
|
|
41447
|
+
const previousHostId = this.#executionHosts.get(thread);
|
|
41448
|
+
if (typeof sourceHostId === "string" && sourceHostId.length > 0 && sourceHostId.length <= 1024 && !/[\u0000-\u0020\u007f]/u.test(sourceHostId)) {
|
|
41449
|
+
this.#executionHosts.set(thread, sourceHostId);
|
|
41450
|
+
} else {
|
|
41451
|
+
this.#executionHosts.delete(thread);
|
|
41452
|
+
}
|
|
41453
|
+
return () => {
|
|
41454
|
+
if (previousHostId === void 0) this.#executionHosts.delete(thread);
|
|
41455
|
+
else this.#executionHosts.set(thread, previousHostId);
|
|
41456
|
+
};
|
|
41457
|
+
}
|
|
41458
|
+
async #beginExternalTurn(thread, text, clientUserMessageId, sourceHostId) {
|
|
40875
41459
|
if (this.#closeRequested || this.#externalRuntime.get(thread.id) !== thread) {
|
|
40876
41460
|
throw new ExternalSteerError(-32073, "External Thread is no longer available");
|
|
40877
41461
|
}
|
|
@@ -40898,6 +41482,7 @@ var AppServerHost = class {
|
|
|
40898
41482
|
thread.projectedTerminalTurnId = null;
|
|
40899
41483
|
thread.projectedTurns.set(turnId, projection);
|
|
40900
41484
|
thread.responseGates.set(turnId, gate);
|
|
41485
|
+
const restoreExecutionHost = this.#setExecutionHost(thread, sourceHostId);
|
|
40901
41486
|
try {
|
|
40902
41487
|
if (thread.unsubmittedPrewarm && await this.#externalRuntime.submitPrewarm(thread)) {
|
|
40903
41488
|
await this.#notifyExternalThreadStarted(thread.thread);
|
|
@@ -40910,6 +41495,7 @@ var AppServerHost = class {
|
|
|
40910
41495
|
if (!result.ok) throw new ExternalSteerError(-32073, result.error.message);
|
|
40911
41496
|
return { turnId, turn: projection.projector.pendingTurn(), gate };
|
|
40912
41497
|
} catch (error51) {
|
|
41498
|
+
restoreExecutionHost();
|
|
40913
41499
|
thread.running = false;
|
|
40914
41500
|
thread.activeTurnId = null;
|
|
40915
41501
|
thread.projectedTurns.delete(turnId);
|
|
@@ -41138,7 +41724,11 @@ var AppServerHost = class {
|
|
|
41138
41724
|
if (event.type === "session.faulted") {
|
|
41139
41725
|
this.#externalSteering.fault(thread.id, new Error(event.error.message));
|
|
41140
41726
|
thread.stateObserver.fault(new Error(event.error.message));
|
|
41727
|
+
this.#externalRuntime.markSessionUnavailable(thread);
|
|
41141
41728
|
this.#diagnose(`${thread.harnessId} Harness Session faulted: ${event.error.message}`);
|
|
41729
|
+
if (!thread.running && !thread.activeTurnId && !this.#hasRunningSubagents(thread.id) && !thread.session.hasBackgroundWork?.()) {
|
|
41730
|
+
void this.#externalRuntime.discardUnavailableIdle(thread);
|
|
41731
|
+
}
|
|
41142
41732
|
return;
|
|
41143
41733
|
}
|
|
41144
41734
|
if (event.type === "turn.autonomous.started") {
|
|
@@ -41267,6 +41857,11 @@ var AppServerHost = class {
|
|
|
41267
41857
|
return { ...subagent, subagentId: record4.hostThreadId };
|
|
41268
41858
|
}
|
|
41269
41859
|
async #refreshOpenSubagentThread(threadId2, terminal3 = true) {
|
|
41860
|
+
await this.#desktopRequests.run(threadId2, async () => {
|
|
41861
|
+
await this.#refreshOpenSubagentThreadBody(threadId2, terminal3);
|
|
41862
|
+
});
|
|
41863
|
+
}
|
|
41864
|
+
async #refreshOpenSubagentThreadBody(threadId2, terminal3) {
|
|
41270
41865
|
const child = this.#externalRuntime.get(threadId2);
|
|
41271
41866
|
if (!child) return;
|
|
41272
41867
|
const previousItems = new Map(
|
|
@@ -41366,7 +41961,7 @@ var AppServerHost = class {
|
|
|
41366
41961
|
}
|
|
41367
41962
|
if (status2 === "idle" && previousStatus === "active") {
|
|
41368
41963
|
for (const [index, waitMs] of SUBAGENT_TERMINAL_REFRESH_DELAYS_MS.entries()) {
|
|
41369
|
-
if (waitMs > 0) await
|
|
41964
|
+
if (waitMs > 0) await delay4(waitMs);
|
|
41370
41965
|
await this.#refreshOpenSubagentThread(
|
|
41371
41966
|
threadId2,
|
|
41372
41967
|
index === SUBAGENT_TERMINAL_REFRESH_DELAYS_MS.length - 1
|
|
@@ -41744,7 +42339,7 @@ import { randomUUID as randomUUID15 } from "node:crypto";
|
|
|
41744
42339
|
import { chmod as chmod3, lstat as lstat5, mkdir as mkdir12, open as open9, readFile as readFile16, readdir as readdir5, rename as rename11, rm as rm11 } from "node:fs/promises";
|
|
41745
42340
|
import { uptime } from "node:os";
|
|
41746
42341
|
import path26 from "node:path";
|
|
41747
|
-
import { setTimeout as
|
|
42342
|
+
import { setTimeout as delay5 } from "node:timers/promises";
|
|
41748
42343
|
var LOCK_RETRY_COUNT = 200;
|
|
41749
42344
|
var LOCK_RETRY_DELAY_MS = 25;
|
|
41750
42345
|
var LOCK_MALFORMED_GRACE_MS = 1e3;
|
|
@@ -42000,7 +42595,7 @@ async function withRemoteAppServerSocketInitializationLock(socketPath, action) {
|
|
|
42000
42595
|
return await action();
|
|
42001
42596
|
}
|
|
42002
42597
|
}
|
|
42003
|
-
await
|
|
42598
|
+
await delay5(LOCK_RETRY_DELAY_MS);
|
|
42004
42599
|
}
|
|
42005
42600
|
throw new Error(
|
|
42006
42601
|
`Remote app-server socket initialization is already in progress at ${socketPath}`
|
|
@@ -43291,15 +43886,28 @@ var DelegationControlRegistry = class {
|
|
|
43291
43886
|
return (await this.#registrationForThread(input.threadId)).cancel(input);
|
|
43292
43887
|
}
|
|
43293
43888
|
async read(input) {
|
|
43294
|
-
|
|
43295
|
-
|
|
43889
|
+
const { callerThreadId: callerThreadId2, ...read } = input;
|
|
43890
|
+
if (read.hostId !== void 0 && read.hostId !== "local") {
|
|
43891
|
+
if (typeof callerThreadId2 === "string" && callerThreadId2) {
|
|
43892
|
+
const callers = await this.#matching(
|
|
43893
|
+
(registration) => registration.executionHostId?.(callerThreadId2) === read.hostId && registration.ownsThread(callerThreadId2)
|
|
43894
|
+
);
|
|
43895
|
+
if (callers.length > 1)
|
|
43896
|
+
only(callers, "Calling Thread has ambiguous execution Host context");
|
|
43897
|
+
if (callers.length === 1) {
|
|
43898
|
+
const { hostId: hostId2, ...local } = read;
|
|
43899
|
+
const snapshot2 = await (await this.#registrationForThread(local.threadId)).read(local);
|
|
43900
|
+
return { ...snapshot2, hostId: hostId2 };
|
|
43901
|
+
}
|
|
43902
|
+
}
|
|
43903
|
+
if (!read.hostId || !this.#remoteRead)
|
|
43296
43904
|
throw new DelegationControlError(
|
|
43297
43905
|
"RUNTIME_UNREACHABLE",
|
|
43298
43906
|
"Remote Thread reading is unavailable"
|
|
43299
43907
|
);
|
|
43300
|
-
return this.#remoteRead(
|
|
43908
|
+
return this.#remoteRead(read);
|
|
43301
43909
|
}
|
|
43302
|
-
return (await this.#registrationForThread(
|
|
43910
|
+
return (await this.#registrationForThread(read.threadId)).read(read);
|
|
43303
43911
|
}
|
|
43304
43912
|
async wait(input) {
|
|
43305
43913
|
if (input.hostId !== void 0 && input.hostId !== "local")
|
|
@@ -44462,13 +45070,15 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44462
45070
|
return authenticated ?? fetchLatestGitHubRelease({ signal: requestSignal });
|
|
44463
45071
|
});
|
|
44464
45072
|
let candidate = null;
|
|
45073
|
+
let currentStatusPath = null;
|
|
44465
45074
|
async function latestStatus(context) {
|
|
44466
|
-
|
|
44467
|
-
|
|
44468
|
-
|
|
44469
|
-
|
|
45075
|
+
if (!currentStatusPath) {
|
|
45076
|
+
const active = await discoverActiveUpdateStatus(context.common.stateDirectory);
|
|
45077
|
+
if (!active) return null;
|
|
45078
|
+
currentStatusPath = active.statusPath;
|
|
44470
45079
|
}
|
|
44471
|
-
|
|
45080
|
+
const status2 = await manager.readStatus(currentStatusPath);
|
|
45081
|
+
return status2 ? publicStatus(status2) : null;
|
|
44472
45082
|
}
|
|
44473
45083
|
async function installable(context, release) {
|
|
44474
45084
|
if (context.installation.kind === "npm") return true;
|
|
@@ -44552,6 +45162,7 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44552
45162
|
if (existing) return { status: existing };
|
|
44553
45163
|
throw new Error("Another update operation is already active");
|
|
44554
45164
|
}
|
|
45165
|
+
currentStatusPath = null;
|
|
44555
45166
|
let resolvePrepared;
|
|
44556
45167
|
let rejectPrepared;
|
|
44557
45168
|
const preparedReady = new Promise((resolve, reject) => {
|
|
@@ -44565,6 +45176,7 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44565
45176
|
}
|
|
44566
45177
|
const onPrepared = async (info) => {
|
|
44567
45178
|
await lock.setStatusPath(info.statusPath);
|
|
45179
|
+
currentStatusPath = info.statusPath;
|
|
44568
45180
|
resolvePrepared(info);
|
|
44569
45181
|
};
|
|
44570
45182
|
const prepareAndStart = async () => {
|
|
@@ -44594,7 +45206,7 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44594
45206
|
onPrepared
|
|
44595
45207
|
});
|
|
44596
45208
|
}
|
|
44597
|
-
if (platform !== "darwin") manager.start(prepared2);
|
|
45209
|
+
if (platform !== "darwin") await manager.start(prepared2);
|
|
44598
45210
|
} catch (error51) {
|
|
44599
45211
|
await lock.release();
|
|
44600
45212
|
rejectPrepared(error51);
|
|
@@ -44631,6 +45243,20 @@ import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
|
44631
45243
|
// packages/harness-adapter/dist/usage-statistics.js
|
|
44632
45244
|
var MAX_SESSION_ID_LENGTH = 512;
|
|
44633
45245
|
var MAX_CWD_LENGTH = 4096;
|
|
45246
|
+
var MAX_SESSION_TITLE_LENGTH = 200;
|
|
45247
|
+
function usageSessionTitle(value2) {
|
|
45248
|
+
if (typeof value2 !== "string")
|
|
45249
|
+
return void 0;
|
|
45250
|
+
const normalized = value2.replaceAll("\0", "").replaceAll(/\s+/gu, " ").trim();
|
|
45251
|
+
if (!normalized)
|
|
45252
|
+
return void 0;
|
|
45253
|
+
if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
|
|
45254
|
+
return normalized;
|
|
45255
|
+
const characters = [...normalized];
|
|
45256
|
+
if (characters.length <= MAX_SESSION_TITLE_LENGTH)
|
|
45257
|
+
return normalized;
|
|
45258
|
+
return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
|
|
45259
|
+
}
|
|
44634
45260
|
function sessionIdOk(value2) {
|
|
44635
45261
|
return typeof value2 === "string" && value2.length > 0 && value2.length <= MAX_SESSION_ID_LENGTH;
|
|
44636
45262
|
}
|
|
@@ -44674,6 +45300,12 @@ function parseHarnessUsageEntry(value2) {
|
|
|
44674
45300
|
return null;
|
|
44675
45301
|
if (entry.cwd !== void 0 && !cwdOk(entry.cwd))
|
|
44676
45302
|
return null;
|
|
45303
|
+
if (entry.sessionTitle !== void 0) {
|
|
45304
|
+
const sessionTitle = usageSessionTitle(entry.sessionTitle);
|
|
45305
|
+
if (sessionTitle === void 0)
|
|
45306
|
+
return null;
|
|
45307
|
+
entry.sessionTitle = sessionTitle;
|
|
45308
|
+
}
|
|
44677
45309
|
if (entry.tokensUnknown !== void 0 && entry.tokensUnknown !== true)
|
|
44678
45310
|
return null;
|
|
44679
45311
|
if (entry.costUsd !== void 0 && (typeof entry.costUsd !== "number" || !Number.isFinite(entry.costUsd) || entry.costUsd < 0)) {
|
|
@@ -44685,8 +45317,31 @@ function parseHarnessUsageEntry(value2) {
|
|
|
44685
45317
|
return entry;
|
|
44686
45318
|
}
|
|
44687
45319
|
|
|
45320
|
+
// packages/host-runtime/src/usage-session-titles.ts
|
|
45321
|
+
function withHostSessionTitles(result, records) {
|
|
45322
|
+
const titles = /* @__PURE__ */ new Map();
|
|
45323
|
+
for (const record4 of records) {
|
|
45324
|
+
const ref = record4.nativeSessionRef;
|
|
45325
|
+
const title = usageSessionTitle(record4.title);
|
|
45326
|
+
if (record4.state !== "ready" || !ref || ref.harnessId !== record4.harnessId || !title) continue;
|
|
45327
|
+
const key = `${record4.harnessId}\0${ref.nativeSessionId}`;
|
|
45328
|
+
const previous = titles.get(key);
|
|
45329
|
+
if (!previous || record4.updatedAt > previous.updatedAt)
|
|
45330
|
+
titles.set(key, { title, updatedAt: record4.updatedAt });
|
|
45331
|
+
}
|
|
45332
|
+
const decorate = (sessions) => sessions.map((session) => {
|
|
45333
|
+
const title = titles.get(`${session.harness}\0${session.sessionId}`)?.title;
|
|
45334
|
+
return title ? { ...session, title } : session;
|
|
45335
|
+
});
|
|
45336
|
+
return {
|
|
45337
|
+
...result,
|
|
45338
|
+
sessions: decorate(result.sessions),
|
|
45339
|
+
...result.recentSessions ? { recentSessions: decorate(result.recentSessions) } : {}
|
|
45340
|
+
};
|
|
45341
|
+
}
|
|
45342
|
+
|
|
44688
45343
|
// packages/host-runtime/src/usage-statistics.ts
|
|
44689
|
-
var CACHE_VERSION =
|
|
45344
|
+
var CACHE_VERSION = 7;
|
|
44690
45345
|
var STALE_AFTER_MS = 1e4;
|
|
44691
45346
|
var WARM_DELAY_MS = 3e3;
|
|
44692
45347
|
var PERSIST_INTERVAL_MS = 6e4;
|
|
@@ -44706,7 +45361,8 @@ function compact(entry) {
|
|
|
44706
45361
|
entry.cwd ?? null,
|
|
44707
45362
|
entry.costUsd ?? null,
|
|
44708
45363
|
entry.credits ?? null,
|
|
44709
|
-
entry.tokensUnknown === true
|
|
45364
|
+
entry.tokensUnknown === true,
|
|
45365
|
+
entry.sessionTitle ?? null
|
|
44710
45366
|
];
|
|
44711
45367
|
}
|
|
44712
45368
|
function interner() {
|
|
@@ -44720,9 +45376,9 @@ function interner() {
|
|
|
44720
45376
|
};
|
|
44721
45377
|
}
|
|
44722
45378
|
function expand(value2, intern) {
|
|
44723
|
-
if (!Array.isArray(value2) || value2.length !==
|
|
45379
|
+
if (!Array.isArray(value2) || value2.length !== 15) return null;
|
|
44724
45380
|
const [id2, occurredAtMs, model2, input, cached2, written, written1h, output, reasoning] = value2;
|
|
44725
|
-
const [sessionId, cwd, costUsd, credits, tokensUnknown] = value2.slice(9);
|
|
45381
|
+
const [sessionId, cwd, costUsd, credits, tokensUnknown, sessionTitle] = value2.slice(9);
|
|
44726
45382
|
return parseHarnessUsageEntry({
|
|
44727
45383
|
id: id2,
|
|
44728
45384
|
occurredAtMs,
|
|
@@ -44737,7 +45393,8 @@ function expand(value2, intern) {
|
|
|
44737
45393
|
...cwd !== null ? { cwd: intern(cwd) } : {},
|
|
44738
45394
|
...costUsd !== null ? { costUsd } : {},
|
|
44739
45395
|
...credits !== null ? { credits } : {},
|
|
44740
|
-
...tokensUnknown === true ? { tokensUnknown } : {}
|
|
45396
|
+
...tokensUnknown === true ? { tokensUnknown } : {},
|
|
45397
|
+
...sessionTitle !== null ? { sessionTitle: intern(sessionTitle) } : {}
|
|
44741
45398
|
});
|
|
44742
45399
|
}
|
|
44743
45400
|
function message(error51) {
|
|
@@ -44828,6 +45485,7 @@ var UsageStatistics = class {
|
|
|
44828
45485
|
#caches = /* @__PURE__ */ new Map();
|
|
44829
45486
|
#failures = /* @__PURE__ */ new Map();
|
|
44830
45487
|
#sources = null;
|
|
45488
|
+
#readThreadTitles;
|
|
44831
45489
|
#loaded = null;
|
|
44832
45490
|
#refreshing = null;
|
|
44833
45491
|
#refreshedAtMs = 0;
|
|
@@ -44842,9 +45500,10 @@ var UsageStatistics = class {
|
|
|
44842
45500
|
this.#options = options2;
|
|
44843
45501
|
this.#directory = path36.join(options2.directory, `v${CACHE_VERSION}`);
|
|
44844
45502
|
}
|
|
44845
|
-
/**
|
|
44846
|
-
attach(sources) {
|
|
45503
|
+
/** Attach native storage and, optionally, fresh persisted Desktop title metadata. */
|
|
45504
|
+
attach(sources, readThreadTitles) {
|
|
44847
45505
|
this.#sources = sources;
|
|
45506
|
+
this.#readThreadTitles = readThreadTitles;
|
|
44848
45507
|
this.#scheduleWarm();
|
|
44849
45508
|
}
|
|
44850
45509
|
/** Reads every source in the background soon after the sources are attached. */
|
|
@@ -44871,11 +45530,19 @@ var UsageStatistics = class {
|
|
|
44871
45530
|
}
|
|
44872
45531
|
const now = this.#now();
|
|
44873
45532
|
if (!this.#refreshing && now - this.#refreshedAtMs > STALE_AFTER_MS) this.#startRefresh();
|
|
44874
|
-
|
|
45533
|
+
const result = this.#aggregate(
|
|
44875
45534
|
typeof params === "string" ? { range: params } : params,
|
|
44876
45535
|
now,
|
|
44877
45536
|
await this.#options.prices.lookup()
|
|
44878
45537
|
);
|
|
45538
|
+
if (this.#readThreadTitles && (result.sessions.length || result.recentSessions?.length)) {
|
|
45539
|
+
try {
|
|
45540
|
+
return withHostSessionTitles(result, await this.#readThreadTitles());
|
|
45541
|
+
} catch (error51) {
|
|
45542
|
+
this.#options.diagnose?.(`Usage statistics Thread titles unavailable: ${message(error51)}`);
|
|
45543
|
+
}
|
|
45544
|
+
}
|
|
45545
|
+
return result;
|
|
44879
45546
|
}
|
|
44880
45547
|
/** Completes the current refresh, for tests and callers that need a settled result. */
|
|
44881
45548
|
async settled() {
|
|
@@ -45153,6 +45820,10 @@ var UsageStatistics = class {
|
|
|
45153
45820
|
sessions.set(key, session);
|
|
45154
45821
|
}
|
|
45155
45822
|
session.project ??= project2;
|
|
45823
|
+
if (entry.sessionTitle && (session.titleAtMs === void 0 || entry.occurredAtMs >= session.titleAtMs)) {
|
|
45824
|
+
session.title = entry.sessionTitle;
|
|
45825
|
+
session.titleAtMs = entry.occurredAtMs;
|
|
45826
|
+
}
|
|
45156
45827
|
if (model2 !== null) session.models.set(model2, (session.models.get(model2) ?? 0) + 1);
|
|
45157
45828
|
session.firstAtMs = Math.min(session.firstAtMs, entry.occurredAtMs);
|
|
45158
45829
|
session.lastAtMs = Math.max(session.lastAtMs, entry.occurredAtMs);
|
|
@@ -45160,21 +45831,26 @@ var UsageStatistics = class {
|
|
|
45160
45831
|
}
|
|
45161
45832
|
}
|
|
45162
45833
|
const allSessions = [...sessions.values()];
|
|
45163
|
-
const
|
|
45164
|
-
...[...allSessions].sort((left, right) => right.totals.costUsd - left.totals.costUsd).slice(0, SESSIONS_PER_MEASURE),
|
|
45165
|
-
...[...allSessions].sort((left, right) => tokensWithoutCache(right.totals) - tokensWithoutCache(left.totals)).slice(0, SESSIONS_PER_MEASURE)
|
|
45166
|
-
]);
|
|
45167
|
-
const sessionList = [...top].map((session) => ({
|
|
45834
|
+
const toSession = (session) => ({
|
|
45168
45835
|
harness: session.harness,
|
|
45169
45836
|
sessionId: session.sessionId,
|
|
45170
45837
|
project: session.project,
|
|
45838
|
+
...session.title ? { title: session.title } : {},
|
|
45171
45839
|
models: [...session.models].sort((left, right) => right[1] - left[1]).slice(0, MODELS_PER_SESSION).map(([name]) => name),
|
|
45172
45840
|
firstAtMs: session.firstAtMs,
|
|
45173
45841
|
lastAtMs: session.lastAtMs,
|
|
45174
45842
|
...session.totals
|
|
45175
|
-
})
|
|
45843
|
+
});
|
|
45844
|
+
const top = /* @__PURE__ */ new Set([
|
|
45845
|
+
...[...allSessions].sort((left, right) => right.totals.costUsd - left.totals.costUsd).slice(0, SESSIONS_PER_MEASURE),
|
|
45846
|
+
...[...allSessions].sort((left, right) => tokensWithoutCache(right.totals) - tokensWithoutCache(left.totals)).slice(0, SESSIONS_PER_MEASURE)
|
|
45847
|
+
]);
|
|
45848
|
+
const sessionList = [...top].map(toSession).sort(
|
|
45176
45849
|
(left, right) => right.costUsd - left.costUsd || tokensWithoutCache(right) - tokensWithoutCache(left)
|
|
45177
45850
|
);
|
|
45851
|
+
const recentSessions = [...allSessions].sort(
|
|
45852
|
+
(left, right) => right.lastAtMs - left.lastAtMs || right.firstAtMs - left.firstAtMs || left.sessionId.localeCompare(right.sessionId)
|
|
45853
|
+
).slice(0, SESSIONS_PER_MEASURE).map(toSession);
|
|
45178
45854
|
if (unlistedModel) this.#options.prices.missing();
|
|
45179
45855
|
const byName = (left, right) => left === right ? 0 : left === null ? 1 : right === null ? -1 : left.localeCompare(right);
|
|
45180
45856
|
return {
|
|
@@ -45219,7 +45895,8 @@ var UsageStatistics = class {
|
|
|
45219
45895
|
byHarness: grouped(byHarness, (name) => ({ harness: name })),
|
|
45220
45896
|
byModel: grouped(byModel, (name) => ({ model: name })).slice(0, 4096).map((row) => ({ ...row, harnessPricedRequests: harnessPriced.get(row.model) ?? 0 })),
|
|
45221
45897
|
byProject: grouped(byProject, (name) => ({ project: name })).slice(0, 4096),
|
|
45222
|
-
sessions: sessionList
|
|
45898
|
+
sessions: sessionList,
|
|
45899
|
+
recentSessions
|
|
45223
45900
|
};
|
|
45224
45901
|
}
|
|
45225
45902
|
};
|
|
@@ -45254,9 +45931,21 @@ function requiredRuntimeConfiguration(environment) {
|
|
|
45254
45931
|
if (!stockCodexPath) throw new Error(`${STOCK_CODEX_PATH_ENV} is required`);
|
|
45255
45932
|
return { stockCodexPath };
|
|
45256
45933
|
}
|
|
45257
|
-
function delegationCliEnvironment(environment) {
|
|
45258
|
-
|
|
45259
|
-
|
|
45934
|
+
function delegationCliEnvironment(environment, hostRuntimePath) {
|
|
45935
|
+
let packagedCli;
|
|
45936
|
+
if (hostRuntimePath && path37.isAbsolute(hostRuntimePath)) {
|
|
45937
|
+
const candidate = path37.resolve(
|
|
45938
|
+
path37.dirname(hostRuntimePath),
|
|
45939
|
+
"../bin",
|
|
45940
|
+
process.platform === "win32" ? "codexhost.exe" : "codexhost"
|
|
45941
|
+
);
|
|
45942
|
+
try {
|
|
45943
|
+
if (statSync(candidate).isFile()) packagedCli = candidate;
|
|
45944
|
+
} catch {
|
|
45945
|
+
}
|
|
45946
|
+
}
|
|
45947
|
+
const cliPath = environment[DELEGATION_CLI_PATH_ENV] ?? environment[UPDATE_RUNTIME_ENV.launcherExecutable] ?? packagedCli;
|
|
45948
|
+
const nodePath2 = environment[DELEGATION_CLI_NODE_PATH_ENV] ?? environment[UPDATE_RUNTIME_ENV.npmNodePath] ?? (packagedCli && cliPath === packagedCli ? process.execPath : void 0);
|
|
45260
45949
|
return {
|
|
45261
45950
|
...cliPath ? { [DELEGATION_CLI_PATH_ENV]: cliPath } : {},
|
|
45262
45951
|
...nodePath2 && path37.isAbsolute(nodePath2) ? { [DELEGATION_CLI_NODE_PATH_ENV]: nodePath2 } : {}
|
|
@@ -45274,7 +45963,7 @@ async function prepareDelegationRuntime(input) {
|
|
|
45274
45963
|
const server = await startDelegationControlServer({ token, api: registry2, watchApi: registry2 });
|
|
45275
45964
|
const environment = {
|
|
45276
45965
|
...input.environment,
|
|
45277
|
-
...delegationCliEnvironment(input.environment),
|
|
45966
|
+
...delegationCliEnvironment(input.environment, input.hostRuntimePath),
|
|
45278
45967
|
[DELEGATION_RUNTIME_ENDPOINT_ENV]: server.endpoint,
|
|
45279
45968
|
[DELEGATION_RUNTIME_TOKEN_ENV]: token
|
|
45280
45969
|
};
|
|
@@ -45349,6 +46038,7 @@ async function runHostRuntime(input) {
|
|
|
45349
46038
|
const environment = remoteControlPlan?.environment ?? input.environment;
|
|
45350
46039
|
return prepareDelegationRuntime({
|
|
45351
46040
|
environment,
|
|
46041
|
+
...hostRuntimePath ? { hostRuntimePath } : {},
|
|
45352
46042
|
createHost: async (delegationEnvironment, onDelegationApi, registry2) => {
|
|
45353
46043
|
const official = await prepareLocalCodex({
|
|
45354
46044
|
stockCodexPath,
|
|
@@ -45457,6 +46147,7 @@ async function runHostRuntime(input) {
|
|
|
45457
46147
|
if (!listenUrl) throw new Error("Remote app-server listener URL is unavailable");
|
|
45458
46148
|
return prepareDelegationRuntime({
|
|
45459
46149
|
environment: input.environment,
|
|
46150
|
+
...hostRuntimePath ? { hostRuntimePath } : {},
|
|
45460
46151
|
createHost: async (delegationEnvironment, _onDelegationApi, registry2) => {
|
|
45461
46152
|
const socketPath = remoteAppServerSocketPath(delegationEnvironment, listenUrl);
|
|
45462
46153
|
const officialPlan = createRemoteOfficialAppServerPlan(input.arguments, socketPath);
|
|
@@ -47690,7 +48381,7 @@ import {
|
|
|
47690
48381
|
readdirSync,
|
|
47691
48382
|
renameSync,
|
|
47692
48383
|
rmSync,
|
|
47693
|
-
statSync,
|
|
48384
|
+
statSync as statSync2,
|
|
47694
48385
|
writeFileSync
|
|
47695
48386
|
} from "node:fs";
|
|
47696
48387
|
import os12 from "node:os";
|
|
@@ -47720,7 +48411,7 @@ function pruneRuntimeLogs(input) {
|
|
|
47720
48411
|
if (!Number.isSafeInteger(pid)) continue;
|
|
47721
48412
|
try {
|
|
47722
48413
|
const filePath = path42.join(input.directory, entry.name);
|
|
47723
|
-
const stats =
|
|
48414
|
+
const stats = statSync2(filePath);
|
|
47724
48415
|
files.push({
|
|
47725
48416
|
filePath,
|
|
47726
48417
|
pid,
|