@codexhost/cli-darwin-x64 0.13.0 → 0.13.1
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 +457 -156
- package/app/plugins/antigravity/plugin.mjs +8 -1
- package/app/plugins/claude-code/plugin.mjs +34469 -23777
- package/app/plugins/codebuddy/plugin.mjs +47 -4
- package/app/plugins/codex-usage/plugin.mjs +639 -18
- 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 +8 -1
- 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 +11568 -7287
- 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
|
@@ -4894,39 +4894,25 @@ async function regularFile(filePath) {
|
|
|
4894
4894
|
throw error51;
|
|
4895
4895
|
}
|
|
4896
4896
|
}
|
|
4897
|
-
async function
|
|
4897
|
+
async function discoverActiveUpdateStatus(stateDirectory) {
|
|
4898
4898
|
if (!path4.isAbsolute(stateDirectory))
|
|
4899
4899
|
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;
|
|
4900
|
+
const lockPath = path4.join(stateDirectory, LOCK_FILE);
|
|
4901
|
+
if (!await regularFile(lockPath))
|
|
4902
|
+
return null;
|
|
4906
4903
|
try {
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
if (error51.code === "ENOENT")
|
|
4904
|
+
const lock = JSON.parse(await readFile3(lockPath, "utf8"));
|
|
4905
|
+
if (typeof lock.statusPath !== "string" || !path4.isAbsolute(lock.statusPath))
|
|
4910
4906
|
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
|
-
}
|
|
4907
|
+
if (!await regularFile(lock.statusPath))
|
|
4908
|
+
return null;
|
|
4909
|
+
const status2 = parseUpdateStatus(JSON.parse(await readFile3(lock.statusPath, "utf8")));
|
|
4910
|
+
if (TERMINAL_PHASES.has(status2.phase))
|
|
4911
|
+
return null;
|
|
4912
|
+
return { statusPath: lock.statusPath, status: status2 };
|
|
4913
|
+
} catch {
|
|
4914
|
+
return null;
|
|
4927
4915
|
}
|
|
4928
|
-
candidates.sort((left, right) => right.status.updatedAt - left.status.updatedAt);
|
|
4929
|
-
return candidates[0] ?? null;
|
|
4930
4916
|
}
|
|
4931
4917
|
async function cleanupTerminalUpdateState(stateDirectory, options2 = {}) {
|
|
4932
4918
|
const now = Math.floor((options2.now ?? Date.now()) / 1e3);
|
|
@@ -5124,6 +5110,48 @@ async function verifyDownloadedArtifact(source, filePath, result) {
|
|
|
5124
5110
|
}
|
|
5125
5111
|
}
|
|
5126
5112
|
|
|
5113
|
+
// packages/update-manager/dist/updater-ready.js
|
|
5114
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
5115
|
+
async function waitForUpdaterReady(child, readStatus, timeoutMs = 1e4) {
|
|
5116
|
+
let failure;
|
|
5117
|
+
const onError = (error51) => {
|
|
5118
|
+
failure = error51;
|
|
5119
|
+
};
|
|
5120
|
+
const onExit = () => {
|
|
5121
|
+
failure = new Error("Background Updater exited before completing handoff");
|
|
5122
|
+
};
|
|
5123
|
+
child.on("error", onError);
|
|
5124
|
+
child.on("exit", onExit);
|
|
5125
|
+
const deadline = performance.now() + timeoutMs;
|
|
5126
|
+
try {
|
|
5127
|
+
while (true) {
|
|
5128
|
+
const status2 = await readStatus();
|
|
5129
|
+
if (status2?.phase === "failed")
|
|
5130
|
+
throw new Error(status2.error ?? "Background Updater failed");
|
|
5131
|
+
if (failure)
|
|
5132
|
+
throw failure;
|
|
5133
|
+
if (child.exitCode != null || child.signalCode != null) {
|
|
5134
|
+
throw new Error("Background Updater exited before completing handoff");
|
|
5135
|
+
}
|
|
5136
|
+
if (status2?.phase === "waiting-for-exit")
|
|
5137
|
+
return;
|
|
5138
|
+
if (status2 && status2.phase !== "prepared") {
|
|
5139
|
+
throw new Error(`Unexpected Updater phase during handoff: ${status2.phase}`);
|
|
5140
|
+
}
|
|
5141
|
+
if (performance.now() >= deadline) {
|
|
5142
|
+
throw new Error("Background Updater did not confirm handoff before the startup timeout");
|
|
5143
|
+
}
|
|
5144
|
+
await delay(20);
|
|
5145
|
+
}
|
|
5146
|
+
} catch (error51) {
|
|
5147
|
+
child.kill();
|
|
5148
|
+
throw error51;
|
|
5149
|
+
} finally {
|
|
5150
|
+
child.removeListener("error", onError);
|
|
5151
|
+
child.removeListener("exit", onExit);
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
|
|
5127
5155
|
// packages/update-manager/dist/update-manager.js
|
|
5128
5156
|
var REQUEST_SCHEMA_VERSION = 1;
|
|
5129
5157
|
var WINDOWS_RENAME_RETRY_DELAYS_MS = [10, 30, 70, 150, 300];
|
|
@@ -5133,7 +5161,7 @@ function errorMessage(error51) {
|
|
|
5133
5161
|
function systemErrorCode(error51) {
|
|
5134
5162
|
return typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : null;
|
|
5135
5163
|
}
|
|
5136
|
-
function
|
|
5164
|
+
function delay2(milliseconds) {
|
|
5137
5165
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
5138
5166
|
}
|
|
5139
5167
|
async function replaceStatusFile(temporaryPath, statusPath) {
|
|
@@ -5149,7 +5177,7 @@ async function replaceStatusFile(temporaryPath, statusPath) {
|
|
|
5149
5177
|
throw error51;
|
|
5150
5178
|
}
|
|
5151
5179
|
retryIndex += 1;
|
|
5152
|
-
await
|
|
5180
|
+
await delay2(retryDelay);
|
|
5153
5181
|
}
|
|
5154
5182
|
}
|
|
5155
5183
|
}
|
|
@@ -5369,14 +5397,20 @@ function createBackgroundUpdateManager(dependencies2 = {}) {
|
|
|
5369
5397
|
app_path: appPath
|
|
5370
5398
|
}, artifact.artifactPath);
|
|
5371
5399
|
},
|
|
5372
|
-
start(prepared) {
|
|
5400
|
+
async start(prepared) {
|
|
5373
5401
|
if (!preparedRequests.delete(prepared.requestPath)) {
|
|
5374
5402
|
throw new Error("background update was not prepared by this manager or was already started");
|
|
5375
5403
|
}
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5404
|
+
try {
|
|
5405
|
+
const child = spawnUpdater(prepared.helperPath, prepared.requestPath);
|
|
5406
|
+
await waitForUpdaterReady(child, () => this.readStatus(prepared.statusPath));
|
|
5407
|
+
if (child.pid === void 0)
|
|
5408
|
+
throw new Error("background Updater did not report a process ID");
|
|
5409
|
+
return Object.freeze({ ...prepared, updaterPid: child.pid });
|
|
5410
|
+
} catch (error51) {
|
|
5411
|
+
await writeFailedStatus(prepared.statusPath, prepared.version, prepared.installation, error51);
|
|
5412
|
+
throw error51;
|
|
5413
|
+
}
|
|
5380
5414
|
},
|
|
5381
5415
|
async readStatus(statusPathValue) {
|
|
5382
5416
|
const statusPath = requireAbsolutePath(statusPathValue, "update status path");
|
|
@@ -20071,6 +20105,8 @@ var usageStatisticsSessionSchema = external_exports.strictObject({
|
|
|
20071
20105
|
harness,
|
|
20072
20106
|
sessionId: external_exports.string().min(1).max(512),
|
|
20073
20107
|
project: project.nullable(),
|
|
20108
|
+
/** Persisted Desktop title for a mapped Session, otherwise its native name; may be absent. */
|
|
20109
|
+
title: external_exports.string().min(1).max(200).optional(),
|
|
20074
20110
|
/** Models the session used, most requests first. */
|
|
20075
20111
|
models: external_exports.array(model).max(16),
|
|
20076
20112
|
firstAtMs: count,
|
|
@@ -20137,7 +20173,12 @@ var usageStatisticsResultSchema = external_exports.strictObject({
|
|
|
20137
20173
|
* The sessions that spent the most, by cost and by tokens without cache, all filters applied;
|
|
20138
20174
|
* requests without a native session are not listed here but count everywhere else.
|
|
20139
20175
|
*/
|
|
20140
|
-
sessions: external_exports.array(usageStatisticsSessionSchema).max(100)
|
|
20176
|
+
sessions: external_exports.array(usageStatisticsSessionSchema).max(100),
|
|
20177
|
+
/**
|
|
20178
|
+
* The sessions last active in the range, newest first, all filters applied. Optional for older
|
|
20179
|
+
* Hosts; missing is treated as no list rather than no sessions.
|
|
20180
|
+
*/
|
|
20181
|
+
recentSessions: external_exports.array(usageStatisticsSessionSchema).max(100).optional()
|
|
20141
20182
|
});
|
|
20142
20183
|
|
|
20143
20184
|
// packages/shared-contracts/dist/ids.js
|
|
@@ -26894,61 +26935,6 @@ function finitePercent(value2) {
|
|
|
26894
26935
|
function optionalReset(value2) {
|
|
26895
26936
|
return nonNegativeSafeInteger(value2);
|
|
26896
26937
|
}
|
|
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
26938
|
function parseRateLimitWindow(value2) {
|
|
26953
26939
|
if (!isRecord9(value2))
|
|
26954
26940
|
return null;
|
|
@@ -31922,6 +31908,8 @@ var UsageMeter = class {
|
|
|
31922
31908
|
#turnOutputMs = 0;
|
|
31923
31909
|
#timeToFirstOutputMs;
|
|
31924
31910
|
#outputTokensPerSecond;
|
|
31911
|
+
#timingUnavailable = false;
|
|
31912
|
+
#timedRequests = /* @__PURE__ */ new Set();
|
|
31925
31913
|
get metered() {
|
|
31926
31914
|
return this.#metered;
|
|
31927
31915
|
}
|
|
@@ -31938,12 +31926,53 @@ var UsageMeter = class {
|
|
|
31938
31926
|
this.#requests.set(request.requestId, request);
|
|
31939
31927
|
const duration4 = request.startedAtMs !== void 0 && request.completedAtMs !== void 0 ? request.completedAtMs - request.startedAtMs : 0;
|
|
31940
31928
|
if (!request.historical && duration4 > 0 && activeTurnId !== null && activeTurnId === this.#turnId) {
|
|
31941
|
-
this
|
|
31942
|
-
|
|
31943
|
-
|
|
31929
|
+
this.recordOutputTiming(
|
|
31930
|
+
{
|
|
31931
|
+
requestId: request.requestId,
|
|
31932
|
+
outputTokens: request.outputTokens,
|
|
31933
|
+
startedAtMs: request.startedAtMs ?? 0,
|
|
31934
|
+
completedAtMs: request.completedAtMs ?? 0
|
|
31935
|
+
},
|
|
31936
|
+
activeTurnId
|
|
31937
|
+
);
|
|
31938
|
+
}
|
|
31939
|
+
return true;
|
|
31940
|
+
}
|
|
31941
|
+
/** Live timing is independent of history replacement and cannot create billing entries. */
|
|
31942
|
+
recordOutputTiming(request, turnId) {
|
|
31943
|
+
if (turnId !== this.#turnId || this.#timingUnavailable || this.#timedRequests.has(request.requestId))
|
|
31944
|
+
return false;
|
|
31945
|
+
if (!request.requestId || ![request.outputTokens, request.startedAtMs, request.completedAtMs].every(
|
|
31946
|
+
(value2) => Number.isSafeInteger(value2) && value2 >= 0
|
|
31947
|
+
) || request.completedAtMs <= request.startedAtMs) {
|
|
31948
|
+
this.invalidateOutputTiming(turnId);
|
|
31949
|
+
return true;
|
|
31944
31950
|
}
|
|
31951
|
+
this.#timedRequests.add(request.requestId);
|
|
31952
|
+
this.#turnOutputTokens += request.outputTokens;
|
|
31953
|
+
this.#turnOutputMs += request.completedAtMs - request.startedAtMs;
|
|
31954
|
+
this.#outputTokensPerSecond = this.#turnOutputTokens / (this.#turnOutputMs / 1e3);
|
|
31945
31955
|
return true;
|
|
31946
31956
|
}
|
|
31957
|
+
invalidateOutputTiming(turnId) {
|
|
31958
|
+
if (turnId !== this.#turnId) return;
|
|
31959
|
+
this.#timingUnavailable = true;
|
|
31960
|
+
}
|
|
31961
|
+
/** Discard in-flight timing across connections, but keep the last valid displayed speed. */
|
|
31962
|
+
resetTurnTiming() {
|
|
31963
|
+
this.#turnId = null;
|
|
31964
|
+
this.#turnStartedAtMs = null;
|
|
31965
|
+
this.#timeToFirstOutputMs = void 0;
|
|
31966
|
+
this.#timedRequests.clear();
|
|
31967
|
+
this.#timingUnavailable = false;
|
|
31968
|
+
}
|
|
31969
|
+
/** Replace a read-only session history after recovery, append, rollback or file replacement. */
|
|
31970
|
+
replaceHistory(requests, complete) {
|
|
31971
|
+
this.#requests.clear();
|
|
31972
|
+
this.#invalidRecord = false;
|
|
31973
|
+
for (const request of requests) this.recordRequest({ ...request, historical: true }, null);
|
|
31974
|
+
this.recordHistory(complete);
|
|
31975
|
+
}
|
|
31947
31976
|
recordHistory(complete) {
|
|
31948
31977
|
this.#metered = true;
|
|
31949
31978
|
this.#historyComplete = complete;
|
|
@@ -31954,6 +31983,8 @@ var UsageMeter = class {
|
|
|
31954
31983
|
this.#turnOutputObserved = false;
|
|
31955
31984
|
this.#turnOutputTokens = 0;
|
|
31956
31985
|
this.#turnOutputMs = 0;
|
|
31986
|
+
this.#timedRequests.clear();
|
|
31987
|
+
this.#timingUnavailable = false;
|
|
31957
31988
|
}
|
|
31958
31989
|
/** Returns true when this is the Turn's first visible output. */
|
|
31959
31990
|
outputObserved(turnId, nowMs) {
|
|
@@ -31966,7 +31997,6 @@ var UsageMeter = class {
|
|
|
31966
31997
|
}
|
|
31967
31998
|
turnCompleted(turnId) {
|
|
31968
31999
|
if (turnId !== this.#turnId) return;
|
|
31969
|
-
if (this.#turnOutputMs === 0) this.#outputTokensPerSecond = void 0;
|
|
31970
32000
|
this.#turnId = null;
|
|
31971
32001
|
this.#turnStartedAtMs = null;
|
|
31972
32002
|
}
|
|
@@ -32001,12 +32031,12 @@ var UsageMeter = class {
|
|
|
32001
32031
|
const cacheHitRate = this.#sessionCacheHitRatePercent();
|
|
32002
32032
|
if (cacheHitRate !== null) usage.sessionCacheHitRatePercent = cacheHitRate;
|
|
32003
32033
|
}
|
|
32004
|
-
if (this.#outputTokensPerSecond !== void 0) {
|
|
32005
|
-
usage.outputTokensPerSecond = this.#outputTokensPerSecond;
|
|
32006
|
-
}
|
|
32007
32034
|
} else if (usage.totalCostUsd !== void 0) {
|
|
32008
32035
|
usage.costSource = "native";
|
|
32009
32036
|
}
|
|
32037
|
+
if (this.#timingUnavailable) delete usage.outputTokensPerSecond;
|
|
32038
|
+
if (this.#outputTokensPerSecond !== void 0)
|
|
32039
|
+
usage.outputTokensPerSecond = this.#outputTokensPerSecond;
|
|
32010
32040
|
if (this.#timeToFirstOutputMs !== void 0) {
|
|
32011
32041
|
usage.timeToFirstOutputMs = this.#timeToFirstOutputMs;
|
|
32012
32042
|
}
|
|
@@ -32905,7 +32935,7 @@ function projectDelegationThreadSnapshot(input) {
|
|
|
32905
32935
|
// packages/host-runtime/src/harness-delegation-coordinator.ts
|
|
32906
32936
|
var IMPLICIT_DEDUPLICATION_MS = 3e4;
|
|
32907
32937
|
var NATIVE_REF_TIMEOUT_MS = 1e4;
|
|
32908
|
-
function
|
|
32938
|
+
function delay3(milliseconds) {
|
|
32909
32939
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
32910
32940
|
}
|
|
32911
32941
|
function terminal(status2) {
|
|
@@ -33295,7 +33325,7 @@ var HarnessDelegationCoordinator = class {
|
|
|
33295
33325
|
if (terminal(snapshot2.status)) return { ...snapshot2, timedOut: false };
|
|
33296
33326
|
const remaining = deadline - Date.now();
|
|
33297
33327
|
if (remaining <= 0) return { ...snapshot2, timedOut: true };
|
|
33298
|
-
await
|
|
33328
|
+
await delay3(Math.min(100, remaining));
|
|
33299
33329
|
}
|
|
33300
33330
|
}
|
|
33301
33331
|
async list(input) {
|
|
@@ -35429,15 +35459,21 @@ var HarnessPluginRegistry = class {
|
|
|
35429
35459
|
};
|
|
35430
35460
|
|
|
35431
35461
|
// packages/host-runtime/src/harness-plugin-loader.ts
|
|
35462
|
+
function hasValidSessionUsage(value2) {
|
|
35463
|
+
const capability = Reflect.get(value2, "sessionUsage");
|
|
35464
|
+
return capability === void 0 || capability !== null && typeof capability === "object" && ["observe", "read"].every((key) => typeof Reflect.get(capability, key) === "function") && ["requestOptions", "shouldForwardNotification", "reset"].every(
|
|
35465
|
+
(key) => Reflect.get(capability, key) === void 0 || typeof Reflect.get(capability, key) === "function"
|
|
35466
|
+
);
|
|
35467
|
+
}
|
|
35432
35468
|
function isAdapter(value2) {
|
|
35433
35469
|
if (!value2 || typeof value2 !== "object") return false;
|
|
35434
|
-
return ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
|
|
35470
|
+
return hasValidSessionUsage(value2) && ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
|
|
35435
35471
|
}
|
|
35436
35472
|
function isUsageAdapter(value2) {
|
|
35437
35473
|
if (!value2 || typeof value2 !== "object" || typeof Reflect.get(value2, "close") !== "function")
|
|
35438
35474
|
return false;
|
|
35439
35475
|
const capability = Reflect.get(value2, "usageStatistics");
|
|
35440
|
-
return !!capability && typeof capability === "object" && ["listSources", "readSource"].every(
|
|
35476
|
+
return hasValidSessionUsage(value2) && !!capability && typeof capability === "object" && ["listSources", "readSource"].every(
|
|
35441
35477
|
(key) => typeof Reflect.get(capability, key) === "function"
|
|
35442
35478
|
) && !("open" in value2) && !("inspect" in value2);
|
|
35443
35479
|
}
|
|
@@ -35578,11 +35614,12 @@ async function loadHarnessPlugins(options2) {
|
|
|
35578
35614
|
}
|
|
35579
35615
|
if (options2.signal?.aborted) return registry2;
|
|
35580
35616
|
let next = 0;
|
|
35617
|
+
let batch = pending;
|
|
35581
35618
|
const loaded = /* @__PURE__ */ new Map();
|
|
35582
35619
|
const worker = async () => {
|
|
35583
35620
|
for (; ; ) {
|
|
35584
35621
|
if (options2.signal?.aborted) return;
|
|
35585
|
-
const candidate =
|
|
35622
|
+
const candidate = batch[next++];
|
|
35586
35623
|
if (!candidate) return;
|
|
35587
35624
|
if (options2.signal?.aborted) return;
|
|
35588
35625
|
const { manifest } = candidate;
|
|
@@ -35628,10 +35665,19 @@ async function loadHarnessPlugins(options2) {
|
|
|
35628
35665
|
loaded.set(candidate, { descriptor, adapter });
|
|
35629
35666
|
}
|
|
35630
35667
|
};
|
|
35631
|
-
|
|
35632
|
-
|
|
35633
|
-
|
|
35634
|
-
|
|
35668
|
+
const batches = options2.onUsageAdaptersLoaded ? [
|
|
35669
|
+
pending.filter((c) => c.manifest.kind === "usage"),
|
|
35670
|
+
pending.filter((c) => c.manifest.kind !== "usage")
|
|
35671
|
+
] : [pending];
|
|
35672
|
+
for (const [index, candidates2] of batches.entries()) {
|
|
35673
|
+
batch = candidates2;
|
|
35674
|
+
next = 0;
|
|
35675
|
+
await Promise.all(Array.from({ length: Math.min(4, batch.length) }, () => worker()));
|
|
35676
|
+
for (const candidate of batch) {
|
|
35677
|
+
const entry = loaded.get(candidate);
|
|
35678
|
+
if (entry) await registry2.register(entry.descriptor, entry.adapter);
|
|
35679
|
+
}
|
|
35680
|
+
if (index === 0) options2.onUsageAdaptersLoaded?.(registry2.usageAdapters);
|
|
35635
35681
|
}
|
|
35636
35682
|
return registry2;
|
|
35637
35683
|
}
|
|
@@ -36533,16 +36579,16 @@ var OfficialRuntimeScope = class {
|
|
|
36533
36579
|
#scheduleRecovery() {
|
|
36534
36580
|
if (!this.#recovery || this.#closed || this.#recoveryTimer || this.#starting) return;
|
|
36535
36581
|
const { delaysMs } = this.#recovery;
|
|
36536
|
-
const
|
|
36582
|
+
const delay6 = delaysMs[Math.min(this.#recoveryAttempt, delaysMs.length - 1)] ?? 0;
|
|
36537
36583
|
this.#recoveryAttempt++;
|
|
36538
36584
|
this.#diagnosticOutput.write(
|
|
36539
|
-
`codexhost: official Codex is unavailable; restarting in ${String(
|
|
36585
|
+
`codexhost: official Codex is unavailable; restarting in ${String(delay6)}ms
|
|
36540
36586
|
`
|
|
36541
36587
|
);
|
|
36542
36588
|
const timer = setTimeout(() => {
|
|
36543
36589
|
this.#recoveryTimer = void 0;
|
|
36544
36590
|
this.start().catch(() => void 0);
|
|
36545
|
-
},
|
|
36591
|
+
}, delay6);
|
|
36546
36592
|
timer.unref();
|
|
36547
36593
|
this.#recoveryTimer = timer;
|
|
36548
36594
|
}
|
|
@@ -37289,11 +37335,163 @@ async function moveThreadSection(input) {
|
|
|
37289
37335
|
}
|
|
37290
37336
|
}
|
|
37291
37337
|
|
|
37338
|
+
// packages/host-runtime/src/observed-session-usage.ts
|
|
37339
|
+
var ObservedSessionUsage = class {
|
|
37340
|
+
constructor(providers, prices, diagnose) {
|
|
37341
|
+
this.providers = providers;
|
|
37342
|
+
this.prices = prices;
|
|
37343
|
+
this.diagnose = diagnose;
|
|
37344
|
+
}
|
|
37345
|
+
providers;
|
|
37346
|
+
prices;
|
|
37347
|
+
diagnose;
|
|
37348
|
+
#states = /* @__PURE__ */ new Map();
|
|
37349
|
+
#pending = /* @__PURE__ */ new Map();
|
|
37350
|
+
#abort = new AbortController();
|
|
37351
|
+
#initializeParams = null;
|
|
37352
|
+
initialized(params) {
|
|
37353
|
+
this.#initializeParams = params;
|
|
37354
|
+
for (const state of this.#states.values()) {
|
|
37355
|
+
state.meter.resetTurnTiming();
|
|
37356
|
+
state.activeTurnId = null;
|
|
37357
|
+
}
|
|
37358
|
+
for (const provider of this.providers()) {
|
|
37359
|
+
try {
|
|
37360
|
+
provider.reset?.();
|
|
37361
|
+
} catch (error51) {
|
|
37362
|
+
this.diagnose(error51);
|
|
37363
|
+
}
|
|
37364
|
+
}
|
|
37365
|
+
}
|
|
37366
|
+
requestOptions(method, params) {
|
|
37367
|
+
if (this.#abort.signal.aborted) return null;
|
|
37368
|
+
let result = null;
|
|
37369
|
+
for (const provider of this.providers()) {
|
|
37370
|
+
try {
|
|
37371
|
+
const patch = provider.requestOptions?.({
|
|
37372
|
+
method,
|
|
37373
|
+
params,
|
|
37374
|
+
initializeParams: this.#initializeParams
|
|
37375
|
+
});
|
|
37376
|
+
if (patch) result = Object.assign(result ?? {}, jsonObjectSchema.parse(patch));
|
|
37377
|
+
} catch (error51) {
|
|
37378
|
+
this.diagnose(error51);
|
|
37379
|
+
}
|
|
37380
|
+
}
|
|
37381
|
+
return result;
|
|
37382
|
+
}
|
|
37383
|
+
shouldForwardNotification(message2) {
|
|
37384
|
+
if (this.#abort.signal.aborted) return true;
|
|
37385
|
+
for (const provider of this.providers()) {
|
|
37386
|
+
try {
|
|
37387
|
+
if (provider.shouldForwardNotification?.(message2) === false) return false;
|
|
37388
|
+
} catch (error51) {
|
|
37389
|
+
this.diagnose(error51);
|
|
37390
|
+
}
|
|
37391
|
+
}
|
|
37392
|
+
return true;
|
|
37393
|
+
}
|
|
37394
|
+
observe(message2, now = Date.now()) {
|
|
37395
|
+
if (this.#abort.signal.aborted) return [];
|
|
37396
|
+
const changed = /* @__PURE__ */ new Set();
|
|
37397
|
+
for (const provider of this.providers()) {
|
|
37398
|
+
try {
|
|
37399
|
+
const observation = provider.observe(message2, now);
|
|
37400
|
+
if (!observation) continue;
|
|
37401
|
+
const state = this.#state(observation.sessionId, provider);
|
|
37402
|
+
if (state.provider !== provider) continue;
|
|
37403
|
+
if (observation.usage) {
|
|
37404
|
+
state.live = parseHostUsage(observation.usage);
|
|
37405
|
+
state.revision++;
|
|
37406
|
+
}
|
|
37407
|
+
const turn = observation.turn;
|
|
37408
|
+
if (turn?.phase === "started" && state.activeTurnId !== turn.id) {
|
|
37409
|
+
state.meter.resetTurnTiming();
|
|
37410
|
+
state.meter.turnStarted(turn.id, now);
|
|
37411
|
+
state.activeTurnId = turn.id;
|
|
37412
|
+
}
|
|
37413
|
+
const timing = observation.requestTiming;
|
|
37414
|
+
if (timing) state.meter.recordOutputTiming(timing, timing.turnId);
|
|
37415
|
+
if (observation.timingUnavailable)
|
|
37416
|
+
state.meter.invalidateOutputTiming(observation.timingUnavailable.turnId);
|
|
37417
|
+
if (turn?.phase === "completed") {
|
|
37418
|
+
state.meter.turnCompleted(turn.id);
|
|
37419
|
+
if (state.activeTurnId === turn.id) state.activeTurnId = null;
|
|
37420
|
+
}
|
|
37421
|
+
const firstOutput = turn?.phase === "output" && state.meter.outputObserved(turn.id, now);
|
|
37422
|
+
if (observation.usage || firstOutput || timing || observation.timingUnavailable || turn?.phase === "started" || turn?.phase === "completed")
|
|
37423
|
+
changed.add(observation.sessionId);
|
|
37424
|
+
} catch (error51) {
|
|
37425
|
+
this.diagnose(error51);
|
|
37426
|
+
}
|
|
37427
|
+
}
|
|
37428
|
+
return [...changed];
|
|
37429
|
+
}
|
|
37430
|
+
async read(sessionId) {
|
|
37431
|
+
if (this.#abort.signal.aborted) return null;
|
|
37432
|
+
let pending = this.#pending.get(sessionId);
|
|
37433
|
+
if (!pending) {
|
|
37434
|
+
pending = this.#refresh(sessionId).finally(() => this.#pending.delete(sessionId));
|
|
37435
|
+
this.#pending.set(sessionId, pending);
|
|
37436
|
+
}
|
|
37437
|
+
await pending;
|
|
37438
|
+
const state = this.#states.get(sessionId);
|
|
37439
|
+
if (!state || this.#abort.signal.aborted) return null;
|
|
37440
|
+
const native = state.live ?? state.history;
|
|
37441
|
+
const result = state.meter.derive(native, await this.prices.lookup());
|
|
37442
|
+
if (result?.unpricedModels?.length) this.prices.missing();
|
|
37443
|
+
return result;
|
|
37444
|
+
}
|
|
37445
|
+
async #refresh(sessionId) {
|
|
37446
|
+
const existing = this.#states.get(sessionId);
|
|
37447
|
+
for (const provider of existing ? [existing.provider] : this.providers()) {
|
|
37448
|
+
const revision = existing?.revision ?? 0;
|
|
37449
|
+
try {
|
|
37450
|
+
const history = await provider.read(sessionId, this.#abort.signal);
|
|
37451
|
+
if (this.#abort.signal.aborted) return;
|
|
37452
|
+
if (!history) {
|
|
37453
|
+
if (existing?.meter.metered) existing.meter.recordHistory(false);
|
|
37454
|
+
continue;
|
|
37455
|
+
}
|
|
37456
|
+
const state = this.#state(sessionId, provider);
|
|
37457
|
+
state.history = history.usage ? parseHostUsage(history.usage) : null;
|
|
37458
|
+
state.meter.replaceHistory(
|
|
37459
|
+
history.requests,
|
|
37460
|
+
history.complete && state.revision === revision
|
|
37461
|
+
);
|
|
37462
|
+
return;
|
|
37463
|
+
} catch (error51) {
|
|
37464
|
+
existing?.meter.recordHistory(false);
|
|
37465
|
+
if (!this.#abort.signal.aborted) this.diagnose(error51);
|
|
37466
|
+
}
|
|
37467
|
+
}
|
|
37468
|
+
}
|
|
37469
|
+
#state(id2, provider) {
|
|
37470
|
+
let state = this.#states.get(id2);
|
|
37471
|
+
if (!state) {
|
|
37472
|
+
state = {
|
|
37473
|
+
provider,
|
|
37474
|
+
meter: new UsageMeter(),
|
|
37475
|
+
live: null,
|
|
37476
|
+
history: null,
|
|
37477
|
+
revision: 0,
|
|
37478
|
+
activeTurnId: null
|
|
37479
|
+
};
|
|
37480
|
+
this.#states.set(id2, state);
|
|
37481
|
+
}
|
|
37482
|
+
return state;
|
|
37483
|
+
}
|
|
37484
|
+
close() {
|
|
37485
|
+
this.#abort.abort();
|
|
37486
|
+
this.#states.clear();
|
|
37487
|
+
}
|
|
37488
|
+
};
|
|
37489
|
+
|
|
37292
37490
|
// packages/host-runtime/src/app-server-host.ts
|
|
37293
37491
|
var CONSOLE_REQUEST_TIMEOUT_MS = 12e4;
|
|
37294
37492
|
var SUBAGENT_TERMINAL_REFRESH_DELAYS_MS = [0, 50, 100, 150];
|
|
37295
37493
|
var THREAD_USAGE_UPDATED_METHOD = "codexhost/thread/usage/updated";
|
|
37296
|
-
function
|
|
37494
|
+
function delay4(milliseconds) {
|
|
37297
37495
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
37298
37496
|
}
|
|
37299
37497
|
function isRecord18(value2) {
|
|
@@ -37490,6 +37688,7 @@ var AppServerHost = class {
|
|
|
37490
37688
|
#externalSteering = new ExternalTurnSteering();
|
|
37491
37689
|
#liveCommandCache = new LiveCommandCatalogCache();
|
|
37492
37690
|
#modelPrices;
|
|
37691
|
+
#observedSessionUsage;
|
|
37493
37692
|
#repository;
|
|
37494
37693
|
#pendingDesktopApprovals = /* @__PURE__ */ new Map();
|
|
37495
37694
|
#pendingDesktopQuestions = /* @__PURE__ */ new Map();
|
|
@@ -37506,7 +37705,6 @@ var AppServerHost = class {
|
|
|
37506
37705
|
#activeWorkDrainWaiters = /* @__PURE__ */ new Set();
|
|
37507
37706
|
#pendingOfficialDelegationThreads = /* @__PURE__ */ new Set();
|
|
37508
37707
|
#pendingOfficialTerminalStatuses = /* @__PURE__ */ new Map();
|
|
37509
|
-
#officialUsageByThread = /* @__PURE__ */ new Map();
|
|
37510
37708
|
#officialRateLimits = new AccountRateLimits();
|
|
37511
37709
|
#routeObservationTracker = new RequestRouteObservationTracker();
|
|
37512
37710
|
#officialServerRequests = /* @__PURE__ */ new Map();
|
|
@@ -37524,6 +37722,7 @@ var AppServerHost = class {
|
|
|
37524
37722
|
#closeRequested = false;
|
|
37525
37723
|
#pluginLoadAbort = new AbortController();
|
|
37526
37724
|
#pluginLoading;
|
|
37725
|
+
#usagePluginsReady;
|
|
37527
37726
|
#desktopRequests = new DesktopRequestQueue();
|
|
37528
37727
|
#drainActiveWorkOnInputEnd = false;
|
|
37529
37728
|
#desktopInputEnded = false;
|
|
@@ -37535,6 +37734,13 @@ var AppServerHost = class {
|
|
|
37535
37734
|
...options2
|
|
37536
37735
|
};
|
|
37537
37736
|
this.#modelPrices = options2.modelPrices ?? new ModelPriceCatalog();
|
|
37737
|
+
this.#observedSessionUsage = new ObservedSessionUsage(
|
|
37738
|
+
() => [...this.#externalAdapters.values(), ...this.#usageOnlyAdapters].flatMap(
|
|
37739
|
+
(adapter) => adapter.sessionUsage ? [adapter.sessionUsage] : []
|
|
37740
|
+
),
|
|
37741
|
+
this.#modelPrices,
|
|
37742
|
+
(error51) => this.#diagnose(error51)
|
|
37743
|
+
);
|
|
37538
37744
|
this.#writer = new OrderedWriter(this.#options.desktopOutput, (value2) => {
|
|
37539
37745
|
this.#noteDesktopReply(value2);
|
|
37540
37746
|
return this.#takeConsoleReply(value2);
|
|
@@ -37702,18 +37908,29 @@ var AppServerHost = class {
|
|
|
37702
37908
|
this.#options.usageStatistics?.attach(
|
|
37703
37909
|
() => [.../* @__PURE__ */ new Set([...this.#externalAdapters.values(), ...this.#usageOnlyAdapters])].flatMap(
|
|
37704
37910
|
(adapter) => adapter.usageStatistics ? [{ harness: adapter.harnessId, capability: adapter.usageStatistics }] : []
|
|
37705
|
-
)
|
|
37911
|
+
),
|
|
37912
|
+
() => this.#repository.list()
|
|
37706
37913
|
);
|
|
37707
37914
|
}
|
|
37708
37915
|
#waitForPlugins() {
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
37916
|
+
if (!this.#pluginLoading) {
|
|
37917
|
+
const ready = Promise.withResolvers();
|
|
37918
|
+
const usageReady = () => ready.resolve(void 0);
|
|
37919
|
+
this.#usagePluginsReady = ready.promise;
|
|
37920
|
+
this.#pluginLoading = this.#loadInstalledPlugins(usageReady).catch((error51) => {
|
|
37921
|
+
this.#diagnose(`Harness plugin load failed: ${errorMessage4(error51)}`);
|
|
37922
|
+
}).finally(usageReady);
|
|
37923
|
+
}
|
|
37924
|
+
return this.#pluginLoading;
|
|
37712
37925
|
}
|
|
37713
|
-
async #loadInstalledPlugins() {
|
|
37926
|
+
async #loadInstalledPlugins(usageReady) {
|
|
37714
37927
|
if (!this.#options.pluginRoots || this.#pluginLoadAbort.signal.aborted) return;
|
|
37715
37928
|
const plugins = await loadHarnessPlugins({
|
|
37716
37929
|
roots: this.#options.pluginRoots,
|
|
37930
|
+
onUsageAdaptersLoaded: (adapters) => {
|
|
37931
|
+
if (!this.#pluginLoadAbort.signal.aborted) this.#usageOnlyAdapters = [...adapters.values()];
|
|
37932
|
+
usageReady();
|
|
37933
|
+
},
|
|
37717
37934
|
launchCommandForPlugin: (id2) => this.#launchSettings.initialCommand(id2),
|
|
37718
37935
|
context: this.#options.pluginContext ?? {
|
|
37719
37936
|
environment: this.#options.environment ?? process.env,
|
|
@@ -37734,6 +37951,7 @@ var AppServerHost = class {
|
|
|
37734
37951
|
for (const [id2, adapter] of plugins.adapters) this.#externalAdapters.set(id2, adapter);
|
|
37735
37952
|
}
|
|
37736
37953
|
async #closeAdapters() {
|
|
37954
|
+
this.#observedSessionUsage.close();
|
|
37737
37955
|
const results = await Promise.allSettled([
|
|
37738
37956
|
this.#plugins?.close(),
|
|
37739
37957
|
...[...new Set(this.#options.externalAdapters?.values())].map(
|
|
@@ -37937,7 +38155,13 @@ var AppServerHost = class {
|
|
|
37937
38155
|
if (request.method === "initialize") {
|
|
37938
38156
|
try {
|
|
37939
38157
|
const nativeGeneration = this.#officialRuntimeScope.gate.phase === "ready" ? this.#officialRuntimeScope.owner.generation : void 0;
|
|
37940
|
-
|
|
38158
|
+
void this.#waitForPlugins();
|
|
38159
|
+
await this.#usagePluginsReady;
|
|
38160
|
+
const params = requestObject(request);
|
|
38161
|
+
const options2 = this.#observedSessionUsage.requestOptions("initialize", params);
|
|
38162
|
+
const nativeParams = options2 ? { ...params, ...options2 } : params;
|
|
38163
|
+
const response = await this.#officialRuntime.initializeProtocol(nativeParams);
|
|
38164
|
+
if (!isRecord18(response.error)) this.#observedSessionUsage.initialized(nativeParams);
|
|
37941
38165
|
await this.#writer.json({ ...response, id: request.id });
|
|
37942
38166
|
this.#nativeAccountObserver?.initialized(nativeGeneration);
|
|
37943
38167
|
} catch (error51) {
|
|
@@ -38768,6 +38992,15 @@ var AppServerHost = class {
|
|
|
38768
38992
|
return;
|
|
38769
38993
|
}
|
|
38770
38994
|
try {
|
|
38995
|
+
const options2 = isRecord18(request.params) ? this.#observedSessionUsage.requestOptions(request.method, request.params) : null;
|
|
38996
|
+
if (options2) {
|
|
38997
|
+
const original = parseJsonFrame(frame);
|
|
38998
|
+
if (isRecord18(original))
|
|
38999
|
+
frame = encodeJsonFrame({
|
|
39000
|
+
...original,
|
|
39001
|
+
params: { ...isRecord18(original.params) ? original.params : {}, ...options2 }
|
|
39002
|
+
});
|
|
39003
|
+
}
|
|
38771
39004
|
await this.#officialRuntime.sendFrame(frame);
|
|
38772
39005
|
this.#markDesktopRequestAnswered(request.id);
|
|
38773
39006
|
} catch {
|
|
@@ -38786,6 +39019,7 @@ var AppServerHost = class {
|
|
|
38786
39019
|
return;
|
|
38787
39020
|
}
|
|
38788
39021
|
const parsed2 = input.value;
|
|
39022
|
+
const observedAtMs = Date.now();
|
|
38789
39023
|
this.#observeOfficialTurnStartResponse(parsed2);
|
|
38790
39024
|
let forwarded = parsed2;
|
|
38791
39025
|
if (isRecord18(parsed2) && typeof parsed2.method === "string" && "id" in parsed2) {
|
|
@@ -38800,17 +39034,6 @@ var AppServerHost = class {
|
|
|
38800
39034
|
if (accountScopedNotification) {
|
|
38801
39035
|
if (parsed2.method === "account/updated") this.#officialRateLimits.reset(input.accountId);
|
|
38802
39036
|
}
|
|
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
39037
|
const rateLimits = observeCodexRateLimits(parsed2);
|
|
38815
39038
|
if (rateLimits) this.#officialRateLimits.observe(input.accountId, rateLimits);
|
|
38816
39039
|
try {
|
|
@@ -38819,8 +39042,16 @@ var AppServerHost = class {
|
|
|
38819
39042
|
this.#diagnose(error51);
|
|
38820
39043
|
}
|
|
38821
39044
|
this.#routeObservationTracker.bindOfficialResponse(parsed2);
|
|
38822
|
-
|
|
38823
|
-
|
|
39045
|
+
const privateTelemetry = isRecord18(parsed2) && typeof parsed2.method === "string" && !("id" in parsed2) && !this.#observedSessionUsage.shouldForwardNotification(parsed2);
|
|
39046
|
+
if (!privateTelemetry) {
|
|
39047
|
+
if (forwarded === parsed2) await this.#writer.frame(input.frame);
|
|
39048
|
+
else await this.#writer.json(forwarded);
|
|
39049
|
+
}
|
|
39050
|
+
void (this.#usagePluginsReady ?? Promise.resolve()).then(async () => {
|
|
39051
|
+
for (const threadId2 of this.#observedSessionUsage.observe(parsed2, observedAtMs)) {
|
|
39052
|
+
await this.#writer.json({ method: THREAD_USAGE_UPDATED_METHOD, params: { threadId: threadId2 } });
|
|
39053
|
+
}
|
|
39054
|
+
}).catch((error51) => this.#diagnose(error51));
|
|
38824
39055
|
this.#nativeAccountObserver?.observe(parsed2);
|
|
38825
39056
|
const deletedProjectId = observeDeletedProject(parsed2);
|
|
38826
39057
|
if (deletedProjectId) {
|
|
@@ -39761,12 +39992,13 @@ var AppServerHost = class {
|
|
|
39761
39992
|
);
|
|
39762
39993
|
return;
|
|
39763
39994
|
}
|
|
39995
|
+
await this.#waitForPlugins();
|
|
39764
39996
|
const accountId = await this.#currentCodexAccountId();
|
|
39765
39997
|
if (accountId) await this.#refreshOfficialRateLimits(accountId);
|
|
39766
39998
|
const accountCredits = this.#officialAccountCredits(accountId ?? void 0);
|
|
39767
39999
|
const result2 = threadUsageInspectionSchema.parse({
|
|
39768
40000
|
threadId: params.data.threadId,
|
|
39769
|
-
usage: this.#
|
|
40001
|
+
usage: await this.#observedSessionUsage.read(params.data.threadId),
|
|
39770
40002
|
...accountCredits ? { accountCredits } : {}
|
|
39771
40003
|
});
|
|
39772
40004
|
await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result2) }));
|
|
@@ -41366,7 +41598,7 @@ var AppServerHost = class {
|
|
|
41366
41598
|
}
|
|
41367
41599
|
if (status2 === "idle" && previousStatus === "active") {
|
|
41368
41600
|
for (const [index, waitMs] of SUBAGENT_TERMINAL_REFRESH_DELAYS_MS.entries()) {
|
|
41369
|
-
if (waitMs > 0) await
|
|
41601
|
+
if (waitMs > 0) await delay4(waitMs);
|
|
41370
41602
|
await this.#refreshOpenSubagentThread(
|
|
41371
41603
|
threadId2,
|
|
41372
41604
|
index === SUBAGENT_TERMINAL_REFRESH_DELAYS_MS.length - 1
|
|
@@ -41744,7 +41976,7 @@ import { randomUUID as randomUUID15 } from "node:crypto";
|
|
|
41744
41976
|
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
41977
|
import { uptime } from "node:os";
|
|
41746
41978
|
import path26 from "node:path";
|
|
41747
|
-
import { setTimeout as
|
|
41979
|
+
import { setTimeout as delay5 } from "node:timers/promises";
|
|
41748
41980
|
var LOCK_RETRY_COUNT = 200;
|
|
41749
41981
|
var LOCK_RETRY_DELAY_MS = 25;
|
|
41750
41982
|
var LOCK_MALFORMED_GRACE_MS = 1e3;
|
|
@@ -42000,7 +42232,7 @@ async function withRemoteAppServerSocketInitializationLock(socketPath, action) {
|
|
|
42000
42232
|
return await action();
|
|
42001
42233
|
}
|
|
42002
42234
|
}
|
|
42003
|
-
await
|
|
42235
|
+
await delay5(LOCK_RETRY_DELAY_MS);
|
|
42004
42236
|
}
|
|
42005
42237
|
throw new Error(
|
|
42006
42238
|
`Remote app-server socket initialization is already in progress at ${socketPath}`
|
|
@@ -44462,13 +44694,15 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44462
44694
|
return authenticated ?? fetchLatestGitHubRelease({ signal: requestSignal });
|
|
44463
44695
|
});
|
|
44464
44696
|
let candidate = null;
|
|
44697
|
+
let currentStatusPath = null;
|
|
44465
44698
|
async function latestStatus(context) {
|
|
44466
|
-
|
|
44467
|
-
|
|
44468
|
-
|
|
44469
|
-
|
|
44699
|
+
if (!currentStatusPath) {
|
|
44700
|
+
const active = await discoverActiveUpdateStatus(context.common.stateDirectory);
|
|
44701
|
+
if (!active) return null;
|
|
44702
|
+
currentStatusPath = active.statusPath;
|
|
44470
44703
|
}
|
|
44471
|
-
|
|
44704
|
+
const status2 = await manager.readStatus(currentStatusPath);
|
|
44705
|
+
return status2 ? publicStatus(status2) : null;
|
|
44472
44706
|
}
|
|
44473
44707
|
async function installable(context, release) {
|
|
44474
44708
|
if (context.installation.kind === "npm") return true;
|
|
@@ -44552,6 +44786,7 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44552
44786
|
if (existing) return { status: existing };
|
|
44553
44787
|
throw new Error("Another update operation is already active");
|
|
44554
44788
|
}
|
|
44789
|
+
currentStatusPath = null;
|
|
44555
44790
|
let resolvePrepared;
|
|
44556
44791
|
let rejectPrepared;
|
|
44557
44792
|
const preparedReady = new Promise((resolve, reject) => {
|
|
@@ -44565,6 +44800,7 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44565
44800
|
}
|
|
44566
44801
|
const onPrepared = async (info) => {
|
|
44567
44802
|
await lock.setStatusPath(info.statusPath);
|
|
44803
|
+
currentStatusPath = info.statusPath;
|
|
44568
44804
|
resolvePrepared(info);
|
|
44569
44805
|
};
|
|
44570
44806
|
const prepareAndStart = async () => {
|
|
@@ -44594,7 +44830,7 @@ function createHostUpdateCoordinator(options2) {
|
|
|
44594
44830
|
onPrepared
|
|
44595
44831
|
});
|
|
44596
44832
|
}
|
|
44597
|
-
if (platform !== "darwin") manager.start(prepared2);
|
|
44833
|
+
if (platform !== "darwin") await manager.start(prepared2);
|
|
44598
44834
|
} catch (error51) {
|
|
44599
44835
|
await lock.release();
|
|
44600
44836
|
rejectPrepared(error51);
|
|
@@ -44631,6 +44867,20 @@ import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
|
44631
44867
|
// packages/harness-adapter/dist/usage-statistics.js
|
|
44632
44868
|
var MAX_SESSION_ID_LENGTH = 512;
|
|
44633
44869
|
var MAX_CWD_LENGTH = 4096;
|
|
44870
|
+
var MAX_SESSION_TITLE_LENGTH = 200;
|
|
44871
|
+
function usageSessionTitle(value2) {
|
|
44872
|
+
if (typeof value2 !== "string")
|
|
44873
|
+
return void 0;
|
|
44874
|
+
const normalized = value2.replaceAll("\0", "").replaceAll(/\s+/gu, " ").trim();
|
|
44875
|
+
if (!normalized)
|
|
44876
|
+
return void 0;
|
|
44877
|
+
if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
|
|
44878
|
+
return normalized;
|
|
44879
|
+
const characters = [...normalized];
|
|
44880
|
+
if (characters.length <= MAX_SESSION_TITLE_LENGTH)
|
|
44881
|
+
return normalized;
|
|
44882
|
+
return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
|
|
44883
|
+
}
|
|
44634
44884
|
function sessionIdOk(value2) {
|
|
44635
44885
|
return typeof value2 === "string" && value2.length > 0 && value2.length <= MAX_SESSION_ID_LENGTH;
|
|
44636
44886
|
}
|
|
@@ -44674,6 +44924,12 @@ function parseHarnessUsageEntry(value2) {
|
|
|
44674
44924
|
return null;
|
|
44675
44925
|
if (entry.cwd !== void 0 && !cwdOk(entry.cwd))
|
|
44676
44926
|
return null;
|
|
44927
|
+
if (entry.sessionTitle !== void 0) {
|
|
44928
|
+
const sessionTitle = usageSessionTitle(entry.sessionTitle);
|
|
44929
|
+
if (sessionTitle === void 0)
|
|
44930
|
+
return null;
|
|
44931
|
+
entry.sessionTitle = sessionTitle;
|
|
44932
|
+
}
|
|
44677
44933
|
if (entry.tokensUnknown !== void 0 && entry.tokensUnknown !== true)
|
|
44678
44934
|
return null;
|
|
44679
44935
|
if (entry.costUsd !== void 0 && (typeof entry.costUsd !== "number" || !Number.isFinite(entry.costUsd) || entry.costUsd < 0)) {
|
|
@@ -44685,8 +44941,31 @@ function parseHarnessUsageEntry(value2) {
|
|
|
44685
44941
|
return entry;
|
|
44686
44942
|
}
|
|
44687
44943
|
|
|
44944
|
+
// packages/host-runtime/src/usage-session-titles.ts
|
|
44945
|
+
function withHostSessionTitles(result, records) {
|
|
44946
|
+
const titles = /* @__PURE__ */ new Map();
|
|
44947
|
+
for (const record4 of records) {
|
|
44948
|
+
const ref = record4.nativeSessionRef;
|
|
44949
|
+
const title = usageSessionTitle(record4.title);
|
|
44950
|
+
if (record4.state !== "ready" || !ref || ref.harnessId !== record4.harnessId || !title) continue;
|
|
44951
|
+
const key = `${record4.harnessId}\0${ref.nativeSessionId}`;
|
|
44952
|
+
const previous = titles.get(key);
|
|
44953
|
+
if (!previous || record4.updatedAt > previous.updatedAt)
|
|
44954
|
+
titles.set(key, { title, updatedAt: record4.updatedAt });
|
|
44955
|
+
}
|
|
44956
|
+
const decorate = (sessions) => sessions.map((session) => {
|
|
44957
|
+
const title = titles.get(`${session.harness}\0${session.sessionId}`)?.title;
|
|
44958
|
+
return title ? { ...session, title } : session;
|
|
44959
|
+
});
|
|
44960
|
+
return {
|
|
44961
|
+
...result,
|
|
44962
|
+
sessions: decorate(result.sessions),
|
|
44963
|
+
...result.recentSessions ? { recentSessions: decorate(result.recentSessions) } : {}
|
|
44964
|
+
};
|
|
44965
|
+
}
|
|
44966
|
+
|
|
44688
44967
|
// packages/host-runtime/src/usage-statistics.ts
|
|
44689
|
-
var CACHE_VERSION =
|
|
44968
|
+
var CACHE_VERSION = 7;
|
|
44690
44969
|
var STALE_AFTER_MS = 1e4;
|
|
44691
44970
|
var WARM_DELAY_MS = 3e3;
|
|
44692
44971
|
var PERSIST_INTERVAL_MS = 6e4;
|
|
@@ -44706,7 +44985,8 @@ function compact(entry) {
|
|
|
44706
44985
|
entry.cwd ?? null,
|
|
44707
44986
|
entry.costUsd ?? null,
|
|
44708
44987
|
entry.credits ?? null,
|
|
44709
|
-
entry.tokensUnknown === true
|
|
44988
|
+
entry.tokensUnknown === true,
|
|
44989
|
+
entry.sessionTitle ?? null
|
|
44710
44990
|
];
|
|
44711
44991
|
}
|
|
44712
44992
|
function interner() {
|
|
@@ -44720,9 +45000,9 @@ function interner() {
|
|
|
44720
45000
|
};
|
|
44721
45001
|
}
|
|
44722
45002
|
function expand(value2, intern) {
|
|
44723
|
-
if (!Array.isArray(value2) || value2.length !==
|
|
45003
|
+
if (!Array.isArray(value2) || value2.length !== 15) return null;
|
|
44724
45004
|
const [id2, occurredAtMs, model2, input, cached2, written, written1h, output, reasoning] = value2;
|
|
44725
|
-
const [sessionId, cwd, costUsd, credits, tokensUnknown] = value2.slice(9);
|
|
45005
|
+
const [sessionId, cwd, costUsd, credits, tokensUnknown, sessionTitle] = value2.slice(9);
|
|
44726
45006
|
return parseHarnessUsageEntry({
|
|
44727
45007
|
id: id2,
|
|
44728
45008
|
occurredAtMs,
|
|
@@ -44737,7 +45017,8 @@ function expand(value2, intern) {
|
|
|
44737
45017
|
...cwd !== null ? { cwd: intern(cwd) } : {},
|
|
44738
45018
|
...costUsd !== null ? { costUsd } : {},
|
|
44739
45019
|
...credits !== null ? { credits } : {},
|
|
44740
|
-
...tokensUnknown === true ? { tokensUnknown } : {}
|
|
45020
|
+
...tokensUnknown === true ? { tokensUnknown } : {},
|
|
45021
|
+
...sessionTitle !== null ? { sessionTitle: intern(sessionTitle) } : {}
|
|
44741
45022
|
});
|
|
44742
45023
|
}
|
|
44743
45024
|
function message(error51) {
|
|
@@ -44828,6 +45109,7 @@ var UsageStatistics = class {
|
|
|
44828
45109
|
#caches = /* @__PURE__ */ new Map();
|
|
44829
45110
|
#failures = /* @__PURE__ */ new Map();
|
|
44830
45111
|
#sources = null;
|
|
45112
|
+
#readThreadTitles;
|
|
44831
45113
|
#loaded = null;
|
|
44832
45114
|
#refreshing = null;
|
|
44833
45115
|
#refreshedAtMs = 0;
|
|
@@ -44842,9 +45124,10 @@ var UsageStatistics = class {
|
|
|
44842
45124
|
this.#options = options2;
|
|
44843
45125
|
this.#directory = path36.join(options2.directory, `v${CACHE_VERSION}`);
|
|
44844
45126
|
}
|
|
44845
|
-
/**
|
|
44846
|
-
attach(sources) {
|
|
45127
|
+
/** Attach native storage and, optionally, fresh persisted Desktop title metadata. */
|
|
45128
|
+
attach(sources, readThreadTitles) {
|
|
44847
45129
|
this.#sources = sources;
|
|
45130
|
+
this.#readThreadTitles = readThreadTitles;
|
|
44848
45131
|
this.#scheduleWarm();
|
|
44849
45132
|
}
|
|
44850
45133
|
/** Reads every source in the background soon after the sources are attached. */
|
|
@@ -44871,11 +45154,19 @@ var UsageStatistics = class {
|
|
|
44871
45154
|
}
|
|
44872
45155
|
const now = this.#now();
|
|
44873
45156
|
if (!this.#refreshing && now - this.#refreshedAtMs > STALE_AFTER_MS) this.#startRefresh();
|
|
44874
|
-
|
|
45157
|
+
const result = this.#aggregate(
|
|
44875
45158
|
typeof params === "string" ? { range: params } : params,
|
|
44876
45159
|
now,
|
|
44877
45160
|
await this.#options.prices.lookup()
|
|
44878
45161
|
);
|
|
45162
|
+
if (this.#readThreadTitles && (result.sessions.length || result.recentSessions?.length)) {
|
|
45163
|
+
try {
|
|
45164
|
+
return withHostSessionTitles(result, await this.#readThreadTitles());
|
|
45165
|
+
} catch (error51) {
|
|
45166
|
+
this.#options.diagnose?.(`Usage statistics Thread titles unavailable: ${message(error51)}`);
|
|
45167
|
+
}
|
|
45168
|
+
}
|
|
45169
|
+
return result;
|
|
44879
45170
|
}
|
|
44880
45171
|
/** Completes the current refresh, for tests and callers that need a settled result. */
|
|
44881
45172
|
async settled() {
|
|
@@ -45153,6 +45444,10 @@ var UsageStatistics = class {
|
|
|
45153
45444
|
sessions.set(key, session);
|
|
45154
45445
|
}
|
|
45155
45446
|
session.project ??= project2;
|
|
45447
|
+
if (entry.sessionTitle && (session.titleAtMs === void 0 || entry.occurredAtMs >= session.titleAtMs)) {
|
|
45448
|
+
session.title = entry.sessionTitle;
|
|
45449
|
+
session.titleAtMs = entry.occurredAtMs;
|
|
45450
|
+
}
|
|
45156
45451
|
if (model2 !== null) session.models.set(model2, (session.models.get(model2) ?? 0) + 1);
|
|
45157
45452
|
session.firstAtMs = Math.min(session.firstAtMs, entry.occurredAtMs);
|
|
45158
45453
|
session.lastAtMs = Math.max(session.lastAtMs, entry.occurredAtMs);
|
|
@@ -45160,21 +45455,26 @@ var UsageStatistics = class {
|
|
|
45160
45455
|
}
|
|
45161
45456
|
}
|
|
45162
45457
|
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) => ({
|
|
45458
|
+
const toSession = (session) => ({
|
|
45168
45459
|
harness: session.harness,
|
|
45169
45460
|
sessionId: session.sessionId,
|
|
45170
45461
|
project: session.project,
|
|
45462
|
+
...session.title ? { title: session.title } : {},
|
|
45171
45463
|
models: [...session.models].sort((left, right) => right[1] - left[1]).slice(0, MODELS_PER_SESSION).map(([name]) => name),
|
|
45172
45464
|
firstAtMs: session.firstAtMs,
|
|
45173
45465
|
lastAtMs: session.lastAtMs,
|
|
45174
45466
|
...session.totals
|
|
45175
|
-
})
|
|
45467
|
+
});
|
|
45468
|
+
const top = /* @__PURE__ */ new Set([
|
|
45469
|
+
...[...allSessions].sort((left, right) => right.totals.costUsd - left.totals.costUsd).slice(0, SESSIONS_PER_MEASURE),
|
|
45470
|
+
...[...allSessions].sort((left, right) => tokensWithoutCache(right.totals) - tokensWithoutCache(left.totals)).slice(0, SESSIONS_PER_MEASURE)
|
|
45471
|
+
]);
|
|
45472
|
+
const sessionList = [...top].map(toSession).sort(
|
|
45176
45473
|
(left, right) => right.costUsd - left.costUsd || tokensWithoutCache(right) - tokensWithoutCache(left)
|
|
45177
45474
|
);
|
|
45475
|
+
const recentSessions = [...allSessions].sort(
|
|
45476
|
+
(left, right) => right.lastAtMs - left.lastAtMs || right.firstAtMs - left.firstAtMs || left.sessionId.localeCompare(right.sessionId)
|
|
45477
|
+
).slice(0, SESSIONS_PER_MEASURE).map(toSession);
|
|
45178
45478
|
if (unlistedModel) this.#options.prices.missing();
|
|
45179
45479
|
const byName = (left, right) => left === right ? 0 : left === null ? 1 : right === null ? -1 : left.localeCompare(right);
|
|
45180
45480
|
return {
|
|
@@ -45219,7 +45519,8 @@ var UsageStatistics = class {
|
|
|
45219
45519
|
byHarness: grouped(byHarness, (name) => ({ harness: name })),
|
|
45220
45520
|
byModel: grouped(byModel, (name) => ({ model: name })).slice(0, 4096).map((row) => ({ ...row, harnessPricedRequests: harnessPriced.get(row.model) ?? 0 })),
|
|
45221
45521
|
byProject: grouped(byProject, (name) => ({ project: name })).slice(0, 4096),
|
|
45222
|
-
sessions: sessionList
|
|
45522
|
+
sessions: sessionList,
|
|
45523
|
+
recentSessions
|
|
45223
45524
|
};
|
|
45224
45525
|
}
|
|
45225
45526
|
};
|