@alook/daemon 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +2018 -325
- package/dist/index.js +1964 -323
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -98,7 +98,7 @@ var init_nanoid = () => {};
|
|
|
98
98
|
// src/cli/index.ts
|
|
99
99
|
import { Command, CommanderError } from "commander";
|
|
100
100
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
101
|
-
import { randomUUID as
|
|
101
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
102
102
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
103
103
|
|
|
104
104
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
@@ -14397,6 +14397,7 @@ var exports_community_machine_schema = {};
|
|
|
14397
14397
|
__export(exports_community_machine_schema, {
|
|
14398
14398
|
communityMachineToken: () => communityMachineToken,
|
|
14399
14399
|
communityMachineCredential: () => communityMachineCredential,
|
|
14400
|
+
communityMachineBackendQuota: () => communityMachineBackendQuota,
|
|
14400
14401
|
communityMachine: () => communityMachine,
|
|
14401
14402
|
communityDiagnosticReport: () => communityDiagnosticReport,
|
|
14402
14403
|
communityBotBinding: () => communityBotBinding,
|
|
@@ -16000,6 +16001,8 @@ var user = sqliteTable("user", {
|
|
|
16000
16001
|
email: text("email").unique().notNull(),
|
|
16001
16002
|
emailVerified: integer2("emailVerified", { mode: "boolean" }),
|
|
16002
16003
|
image: text("image"),
|
|
16004
|
+
avatarVersion: integer2("avatarVersion").notNull().default(0),
|
|
16005
|
+
avatarObjectKey: text("avatarObjectKey"),
|
|
16003
16006
|
createdAt: text("createdAt").notNull().$defaultFn(() => new Date().toISOString()),
|
|
16004
16007
|
updatedAt: text("updatedAt").notNull().$defaultFn(() => new Date().toISOString()),
|
|
16005
16008
|
isBot: integer2("isBot", { mode: "boolean" }).notNull().default(false),
|
|
@@ -16673,8 +16676,23 @@ var communityBotBinding = sqliteTable("community_bot_binding", {
|
|
|
16673
16676
|
runtime: text("runtime").notNull(),
|
|
16674
16677
|
instruction: text("instruction").notNull().default(""),
|
|
16675
16678
|
modelName: text("model_name"),
|
|
16679
|
+
reasoningEffort: text("reasoning_effort"),
|
|
16680
|
+
runtimeConfigRevision: integer2("runtime_config_revision").notNull().default(0),
|
|
16676
16681
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
16677
16682
|
}, (t) => [index("idx_community_bot_binding_machine").on(t.machineId)]);
|
|
16683
|
+
var communityMachineBackendQuota = sqliteTable("community_machine_backend_quota", {
|
|
16684
|
+
machineId: text("machine_id").notNull().references(() => communityMachine.id, { onDelete: "cascade" }),
|
|
16685
|
+
agentBackendId: text("agent_backend_id").$type().notNull(),
|
|
16686
|
+
sourceEpoch: text("source_epoch").notNull(),
|
|
16687
|
+
status: text("status").$type().notNull(),
|
|
16688
|
+
planName: text("plan_name"),
|
|
16689
|
+
freshForSeconds: integer2("fresh_for_seconds"),
|
|
16690
|
+
limits: text("limits", { mode: "json" }).$type(),
|
|
16691
|
+
errorCode: text("error_code"),
|
|
16692
|
+
retryable: integer2("retryable", { mode: "boolean" }),
|
|
16693
|
+
observedAt: text("observed_at").notNull(),
|
|
16694
|
+
updatedAt: text("updated_at").notNull()
|
|
16695
|
+
}, (t) => [primaryKey({ columns: [t.machineId, t.agentBackendId] })]);
|
|
16678
16696
|
var communityAgentRunnerKey = sqliteTable("community_agent_runner_key", {
|
|
16679
16697
|
id: text("id").primaryKey().$defaultFn(() => "crkid_" + nanoid3()),
|
|
16680
16698
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -16853,6 +16871,11 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
|
|
|
16853
16871
|
config: exports_external.unknown(),
|
|
16854
16872
|
launchId: exports_external.string().min(1)
|
|
16855
16873
|
}),
|
|
16874
|
+
exports_external.object({
|
|
16875
|
+
type: exports_external.literal("agent:runtime_config_update"),
|
|
16876
|
+
agentId: exports_external.string().min(1),
|
|
16877
|
+
config: exports_external.unknown()
|
|
16878
|
+
}),
|
|
16856
16879
|
exports_external.object({
|
|
16857
16880
|
type: exports_external.literal("machine:reset_all"),
|
|
16858
16881
|
resets: exports_external.array(exports_external.object({
|
|
@@ -16899,7 +16922,8 @@ var MAX_MESSAGE_CONTENT_LENGTH = 4000;
|
|
|
16899
16922
|
var MAX_EMOJI_BYTES = 32;
|
|
16900
16923
|
var MAX_ATTACHMENTS_PER_MESSAGE = 10;
|
|
16901
16924
|
var MAX_ATTACHMENT_SIZE_BYTES = 25 * 1024 * 1024;
|
|
16902
|
-
var
|
|
16925
|
+
var MAX_ATTACHMENT_THUMBNAIL_EDGE_PX = 1024;
|
|
16926
|
+
var MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 512 * 1024;
|
|
16903
16927
|
var MAX_SERVER_ICON_SIZE_BYTES = 5 * 1024 * 1024;
|
|
16904
16928
|
var ALLOWED_ICON_MIME_TYPES = [
|
|
16905
16929
|
"image/png",
|
|
@@ -16908,6 +16932,92 @@ var ALLOWED_ICON_MIME_TYPES = [
|
|
|
16908
16932
|
"image/gif"
|
|
16909
16933
|
];
|
|
16910
16934
|
var MAX_ICON_SOURCE_FILE_SIZE_BYTES = 15 * 1024 * 1024;
|
|
16935
|
+
// ../shared/src/provider-telemetry.ts
|
|
16936
|
+
var safeToken = exports_external.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
16937
|
+
var boundedText = exports_external.string().min(1).refine((value) => new TextEncoder().encode(value).length <= 64, { message: "must be at most 64 UTF-8 bytes" });
|
|
16938
|
+
var DailyUsageMetricSchema = safeToken.nullable();
|
|
16939
|
+
var DailyUsageSnapshotSchema = exports_external.object({
|
|
16940
|
+
botId: exports_external.string().min(1),
|
|
16941
|
+
day: exports_external.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
16942
|
+
metrics: exports_external.object({
|
|
16943
|
+
input: DailyUsageMetricSchema,
|
|
16944
|
+
output: DailyUsageMetricSchema,
|
|
16945
|
+
cache: DailyUsageMetricSchema
|
|
16946
|
+
}).strict()
|
|
16947
|
+
}).strict();
|
|
16948
|
+
var QuotaProductIdentitySchema = exports_external.discriminatedUnion("kind", [
|
|
16949
|
+
exports_external.object({ kind: exports_external.literal("reported"), id: boundedText, displayName: boundedText }).strict(),
|
|
16950
|
+
exports_external.object({ kind: exports_external.literal("unknown"), displayName: boundedText }).strict()
|
|
16951
|
+
]);
|
|
16952
|
+
var QuotaModelIdentitySchema = exports_external.discriminatedUnion("kind", [
|
|
16953
|
+
exports_external.object({ kind: exports_external.literal("reported"), id: boundedText }).strict(),
|
|
16954
|
+
exports_external.object({ kind: exports_external.literal("not_applicable") }).strict(),
|
|
16955
|
+
exports_external.object({ kind: exports_external.literal("unknown") }).strict()
|
|
16956
|
+
]);
|
|
16957
|
+
var QuotaWindowIdentitySchema = exports_external.discriminatedUnion("kind", [
|
|
16958
|
+
exports_external.object({
|
|
16959
|
+
kind: exports_external.literal("rolling"),
|
|
16960
|
+
durationSeconds: exports_external.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
16961
|
+
displayName: boundedText
|
|
16962
|
+
}).strict(),
|
|
16963
|
+
exports_external.object({
|
|
16964
|
+
kind: exports_external.literal("calendar"),
|
|
16965
|
+
period: exports_external.enum(["day", "week", "month"]),
|
|
16966
|
+
displayName: boundedText
|
|
16967
|
+
}).strict(),
|
|
16968
|
+
exports_external.object({
|
|
16969
|
+
kind: exports_external.literal("provider_defined"),
|
|
16970
|
+
id: boundedText,
|
|
16971
|
+
durationSeconds: exports_external.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
16972
|
+
displayName: boundedText
|
|
16973
|
+
}).strict()
|
|
16974
|
+
]);
|
|
16975
|
+
var QuotaLimitSchema = exports_external.object({
|
|
16976
|
+
bucket: exports_external.object({
|
|
16977
|
+
limitId: boundedText,
|
|
16978
|
+
product: QuotaProductIdentitySchema,
|
|
16979
|
+
model: QuotaModelIdentitySchema,
|
|
16980
|
+
window: QuotaWindowIdentitySchema
|
|
16981
|
+
}).strict(),
|
|
16982
|
+
usedPercent: exports_external.number().finite().min(0).max(100),
|
|
16983
|
+
resetsAt: exports_external.string().datetime({ offset: true }).optional()
|
|
16984
|
+
}).strict();
|
|
16985
|
+
function quotaIdentity(limit) {
|
|
16986
|
+
const { product, model, window: window2, limitId } = limit.bucket;
|
|
16987
|
+
const productKey = product.kind === "reported" ? `reported:${product.id}` : "unknown";
|
|
16988
|
+
const modelKey = model.kind === "reported" ? `reported:${model.id}` : model.kind;
|
|
16989
|
+
const windowKey = window2.kind === "rolling" ? `rolling:${window2.durationSeconds}` : window2.kind === "calendar" ? `calendar:${window2.period}` : `provider_defined:${window2.id}:${window2.durationSeconds === undefined ? "absent" : window2.durationSeconds}`;
|
|
16990
|
+
return `${productKey}\x00${modelKey}\x00${windowKey}\x00${limitId}`;
|
|
16991
|
+
}
|
|
16992
|
+
var AvailableQuotaObservationSchema = exports_external.object({
|
|
16993
|
+
status: exports_external.literal("available"),
|
|
16994
|
+
sourceEpoch: exports_external.string().regex(/^[A-Za-z0-9_-]{22}$/),
|
|
16995
|
+
planName: boundedText.optional(),
|
|
16996
|
+
freshForSeconds: exports_external.number().int().positive().max(86400),
|
|
16997
|
+
limits: exports_external.array(QuotaLimitSchema).min(1).max(8)
|
|
16998
|
+
}).strict().superRefine((value, ctx) => {
|
|
16999
|
+
const identities = new Set;
|
|
17000
|
+
for (const [index2, limit] of value.limits.entries()) {
|
|
17001
|
+
const identity = quotaIdentity(limit);
|
|
17002
|
+
if (identities.has(identity)) {
|
|
17003
|
+
ctx.addIssue({ code: "custom", message: "duplicate quota bucket identity", path: ["limits", index2] });
|
|
17004
|
+
}
|
|
17005
|
+
identities.add(identity);
|
|
17006
|
+
}
|
|
17007
|
+
});
|
|
17008
|
+
var ProviderQuotaObservationSchema = exports_external.union([
|
|
17009
|
+
AvailableQuotaObservationSchema,
|
|
17010
|
+
exports_external.object({
|
|
17011
|
+
status: exports_external.literal("error"),
|
|
17012
|
+
sourceEpoch: exports_external.string().regex(/^[A-Za-z0-9_-]{22}$/),
|
|
17013
|
+
code: exports_external.enum(["unavailable", "unauthorized", "network", "provider_error", "invalid_response"]),
|
|
17014
|
+
retryable: exports_external.boolean()
|
|
17015
|
+
}).strict()
|
|
17016
|
+
]);
|
|
17017
|
+
var ProviderQuotaSnapshotSchema = exports_external.object({
|
|
17018
|
+
agentBackendId: exports_external.enum(["claude", "codex"]),
|
|
17019
|
+
observation: ProviderQuotaObservationSchema
|
|
17020
|
+
}).strict();
|
|
16911
17021
|
// ../shared/src/utils/slug.ts
|
|
16912
17022
|
init_nanoid();
|
|
16913
17023
|
var slugId = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz");
|
|
@@ -17422,12 +17532,61 @@ var CreateThreadRequestSchema = exports_external.object({
|
|
|
17422
17532
|
attachment_ids: exports_external.array(exports_external.string()).optional()
|
|
17423
17533
|
});
|
|
17424
17534
|
var RUNTIME_ID_RE = /^[A-Za-z0-9._@/-]+$/;
|
|
17535
|
+
var REASONING_EFFORT_RE = /^[A-Za-z0-9._-]+$/;
|
|
17536
|
+
var COMMUNITY_REASONING_EFFORT_MAX = 32;
|
|
17537
|
+
var COMMUNITY_REASONING_DESCRIPTION_MAX = 256;
|
|
17538
|
+
var COMMUNITY_REASONING_OPTIONS_MAX = 16;
|
|
17539
|
+
var COMMUNITY_REASONING_MODELS_MAX = 512;
|
|
17540
|
+
var ReasoningEffortSchema = exports_external.string().min(1).max(COMMUNITY_REASONING_EFFORT_MAX).regex(REASONING_EFFORT_RE, "invalid reasoning effort charset");
|
|
17541
|
+
var RuntimeReasoningOptionSchema = exports_external.object({
|
|
17542
|
+
value: ReasoningEffortSchema,
|
|
17543
|
+
description: exports_external.string().max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional()
|
|
17544
|
+
});
|
|
17545
|
+
var RuntimeReasoningModelSchema = exports_external.object({
|
|
17546
|
+
id: exports_external.string().min(1).max(100),
|
|
17547
|
+
displayName: exports_external.string().min(1).max(COMMUNITY_REASONING_DESCRIPTION_MAX).optional().catch(undefined),
|
|
17548
|
+
supportedReasoningEfforts: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_OPTIONS_MAX).transform((options) => {
|
|
17549
|
+
const seen = new Set;
|
|
17550
|
+
return options.flatMap((candidate) => {
|
|
17551
|
+
const parsed = RuntimeReasoningOptionSchema.safeParse(candidate);
|
|
17552
|
+
if (!parsed.success)
|
|
17553
|
+
return [];
|
|
17554
|
+
const option = parsed.data;
|
|
17555
|
+
if (seen.has(option.value))
|
|
17556
|
+
return [];
|
|
17557
|
+
seen.add(option.value);
|
|
17558
|
+
return [option];
|
|
17559
|
+
});
|
|
17560
|
+
}),
|
|
17561
|
+
defaultReasoningEffort: ReasoningEffortSchema.optional().catch(undefined)
|
|
17562
|
+
}).transform((model) => {
|
|
17563
|
+
const { defaultReasoningEffort, ...rest } = model;
|
|
17564
|
+
return defaultReasoningEffort !== undefined && model.supportedReasoningEfforts.some((option) => option.value === defaultReasoningEffort) ? { ...rest, defaultReasoningEffort } : rest;
|
|
17565
|
+
});
|
|
17566
|
+
var RuntimeReasoningCatalogSchema = exports_external.object({
|
|
17567
|
+
updateMode: exports_external.enum(["live_next_turn", "context_preserving_restart", "unsupported"]),
|
|
17568
|
+
defaultModelId: exports_external.string().min(1).max(100).optional().catch(undefined),
|
|
17569
|
+
models: exports_external.array(exports_external.unknown()).max(COMMUNITY_REASONING_MODELS_MAX).transform((models) => {
|
|
17570
|
+
const seen = new Set;
|
|
17571
|
+
return models.flatMap((candidate) => {
|
|
17572
|
+
const parsed = RuntimeReasoningModelSchema.safeParse(candidate);
|
|
17573
|
+
if (!parsed.success)
|
|
17574
|
+
return [];
|
|
17575
|
+
const model = parsed.data;
|
|
17576
|
+
if (seen.has(model.id))
|
|
17577
|
+
return [];
|
|
17578
|
+
seen.add(model.id);
|
|
17579
|
+
return [model];
|
|
17580
|
+
});
|
|
17581
|
+
})
|
|
17582
|
+
});
|
|
17425
17583
|
var CommunityMachineRuntimeSchema = exports_external.object({
|
|
17426
17584
|
id: exports_external.string().min(1).max(COMMUNITY_RUNTIME_ID_MAX).regex(RUNTIME_ID_RE, "invalid runtime id charset"),
|
|
17427
17585
|
version: exports_external.string().max(COMMUNITY_RUNTIME_VERSION_MAX).optional(),
|
|
17428
17586
|
status: exports_external.enum(["healthy", "unhealthy"]).catch("healthy").default("healthy"),
|
|
17429
17587
|
lastError: exports_external.string().max(128).optional(),
|
|
17430
|
-
lastErrorAt: exports_external.string().optional()
|
|
17588
|
+
lastErrorAt: exports_external.string().optional(),
|
|
17589
|
+
reasoning: RuntimeReasoningCatalogSchema.optional().catch(undefined)
|
|
17431
17590
|
});
|
|
17432
17591
|
var CommunityMachineRuntimeListSchema = exports_external.array(CommunityMachineRuntimeSchema).max(COMMUNITY_RUNTIME_LIST_MAX).transform((list) => {
|
|
17433
17592
|
const seen = new Set;
|
|
@@ -17479,7 +17638,8 @@ var HostReadyMessageSchema = exports_external.object({
|
|
|
17479
17638
|
platform: exports_external.string().optional(),
|
|
17480
17639
|
arch: exports_external.string().optional(),
|
|
17481
17640
|
osRelease: exports_external.string().optional(),
|
|
17482
|
-
daemonVersion: exports_external.string().optional()
|
|
17641
|
+
daemonVersion: exports_external.string().optional(),
|
|
17642
|
+
providerQuotas: exports_external.array(ProviderQuotaSnapshotSchema).max(2).optional().default([])
|
|
17483
17643
|
});
|
|
17484
17644
|
var CommunityDaemonReadySchema = exports_external.object({
|
|
17485
17645
|
runtimeReport: CommunityMachineRuntimeListSchema.optional(),
|
|
@@ -17500,7 +17660,9 @@ var SessionErrorFrameSchema = exports_external.object({
|
|
|
17500
17660
|
var AgentActivityMessageSchema = exports_external.object({
|
|
17501
17661
|
type: exports_external.literal("agent_activity"),
|
|
17502
17662
|
agentId: exports_external.string(),
|
|
17503
|
-
state: exports_external.enum(["idle", "starting", "running", "stopping"])
|
|
17663
|
+
state: exports_external.enum(["idle", "starting", "running", "stopping"]),
|
|
17664
|
+
dailyUsage: exports_external.array(DailyUsageSnapshotSchema).max(7).optional(),
|
|
17665
|
+
quota: ProviderQuotaSnapshotSchema.optional()
|
|
17504
17666
|
});
|
|
17505
17667
|
var AgentTypingMessageSchema = exports_external.object({
|
|
17506
17668
|
type: exports_external.literal("agent_typing"),
|
|
@@ -17575,15 +17737,17 @@ var CommunityBotCreateRequestSchema = exports_external.object({
|
|
|
17575
17737
|
machineId: exports_external.string().min(1),
|
|
17576
17738
|
runtime: exports_external.string().min(1),
|
|
17577
17739
|
image: BotImageUrlSchema.optional(),
|
|
17578
|
-
model: exports_external.string().trim().min(1).max(100).nullable().optional()
|
|
17740
|
+
model: exports_external.string().trim().min(1).max(100).nullable().optional(),
|
|
17741
|
+
reasoningEffort: ReasoningEffortSchema.nullable().optional()
|
|
17579
17742
|
});
|
|
17580
17743
|
var CommunityBotPatchRequestSchema = exports_external.object({
|
|
17581
17744
|
name: exports_external.string().trim().min(COMMUNITY_BOT_NAME_MIN).max(COMMUNITY_BOT_NAME_MAX).refine(isMentionSafeName, { message: MENTION_SAFE_NAME_MSG }).optional(),
|
|
17582
17745
|
description: exports_external.string().max(COMMUNITY_BOT_DESCRIPTION_MAX).optional(),
|
|
17583
17746
|
image: BotImageUrlSchema.nullable().optional(),
|
|
17584
17747
|
model: exports_external.string().trim().min(1).max(100).nullable().optional(),
|
|
17585
|
-
runtime: exports_external.string().trim().min(1).max(COMMUNITY_RUNTIME_ID_MAX).optional()
|
|
17586
|
-
|
|
17748
|
+
runtime: exports_external.string().trim().min(1).max(COMMUNITY_RUNTIME_ID_MAX).optional(),
|
|
17749
|
+
reasoningEffort: ReasoningEffortSchema.nullable().optional()
|
|
17750
|
+
}).refine((v) => v.name !== undefined || v.description !== undefined || v.image !== undefined || v.runtime !== undefined || ("reasoningEffort" in v) || ("model" in v), {
|
|
17587
17751
|
message: "at least one field must be provided"
|
|
17588
17752
|
});
|
|
17589
17753
|
var CommunityBotAddToServerRequestSchema = exports_external.object({
|
|
@@ -17785,6 +17949,7 @@ __export(exports_community_schema, {
|
|
|
17785
17949
|
communityChannelMember: () => communityChannelMember,
|
|
17786
17950
|
communityChannel: () => communityChannel,
|
|
17787
17951
|
communityCategory: () => communityCategory,
|
|
17952
|
+
communityBotDailyTokenUsage: () => communityBotDailyTokenUsage,
|
|
17788
17953
|
communityBotDailyActivity: () => communityBotDailyActivity,
|
|
17789
17954
|
communityBotApprovalRequest: () => communityBotApprovalRequest,
|
|
17790
17955
|
communityBotActivityEvent: () => communityBotActivityEvent,
|
|
@@ -18037,6 +18202,17 @@ var communityBotDailyActivity = sqliteTable("community_bot_daily_activity", {
|
|
|
18037
18202
|
handledCount: integer2("handled_count").notNull().default(0),
|
|
18038
18203
|
sentCount: integer2("sent_count").notNull().default(0)
|
|
18039
18204
|
}, (t) => [primaryKey({ columns: [t.botId, t.day] })]);
|
|
18205
|
+
var communityBotDailyTokenUsage = sqliteTable("community_bot_daily_token_usage", {
|
|
18206
|
+
botId: text("bot_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
18207
|
+
day: text("day").notNull(),
|
|
18208
|
+
inputTokens: integer2("input_tokens"),
|
|
18209
|
+
outputTokens: integer2("output_tokens"),
|
|
18210
|
+
cacheTokens: integer2("cache_tokens"),
|
|
18211
|
+
updatedAt: text("updated_at").notNull()
|
|
18212
|
+
}, (t) => [
|
|
18213
|
+
primaryKey({ columns: [t.botId, t.day] }),
|
|
18214
|
+
index("idx_community_bot_daily_token_usage_day").on(t.day)
|
|
18215
|
+
]);
|
|
18040
18216
|
var communityMessageMark = sqliteTable("community_message_mark", {
|
|
18041
18217
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
18042
18218
|
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
@@ -18161,7 +18337,8 @@ var listedMessageProjection = {
|
|
|
18161
18337
|
clientNonce: communityMessage.clientNonce,
|
|
18162
18338
|
authorName: user.name,
|
|
18163
18339
|
authorEmail: user.email,
|
|
18164
|
-
authorImage: user.image
|
|
18340
|
+
authorImage: user.image,
|
|
18341
|
+
authorAvatarVersion: user.avatarVersion
|
|
18165
18342
|
};
|
|
18166
18343
|
|
|
18167
18344
|
// ../shared/src/db/queries/user.ts
|
|
@@ -18171,6 +18348,7 @@ var publicUserColumns = {
|
|
|
18171
18348
|
email: user.email,
|
|
18172
18349
|
emailVerified: user.emailVerified,
|
|
18173
18350
|
image: user.image,
|
|
18351
|
+
avatarVersion: user.avatarVersion,
|
|
18174
18352
|
createdAt: user.createdAt,
|
|
18175
18353
|
updatedAt: user.updatedAt,
|
|
18176
18354
|
discriminator: user.discriminator
|
|
@@ -18181,6 +18359,12 @@ var internalUserColumns = {
|
|
|
18181
18359
|
ownerUserId: user.ownerUserId,
|
|
18182
18360
|
deletedAt: user.deletedAt
|
|
18183
18361
|
};
|
|
18362
|
+
var avatarPublishColumns = {
|
|
18363
|
+
id: user.id,
|
|
18364
|
+
image: user.image,
|
|
18365
|
+
avatarVersion: user.avatarVersion,
|
|
18366
|
+
avatarObjectKey: user.avatarObjectKey
|
|
18367
|
+
};
|
|
18184
18368
|
|
|
18185
18369
|
// ../shared/src/db/queries/community/channel.ts
|
|
18186
18370
|
var CHANNEL_COLUMNS = {
|
|
@@ -18233,7 +18417,8 @@ var friendApprovalProfileSchema = exports_external.strictObject({
|
|
|
18233
18417
|
id: string4,
|
|
18234
18418
|
name: string4,
|
|
18235
18419
|
discriminator: string4,
|
|
18236
|
-
image: nullableString
|
|
18420
|
+
image: nullableString,
|
|
18421
|
+
avatarVersion: exports_external.number().int().nonnegative()
|
|
18237
18422
|
});
|
|
18238
18423
|
var FriendApprovalPayloadSchema = exports_external.strictObject({
|
|
18239
18424
|
friendshipId: string4,
|
|
@@ -18259,6 +18444,7 @@ var messageSchema = exports_external.strictObject({
|
|
|
18259
18444
|
authorId: string4,
|
|
18260
18445
|
authorName: string4,
|
|
18261
18446
|
authorAvatar: string4.optional(),
|
|
18447
|
+
authorAvatarVersion: exports_external.number().int().nonnegative(),
|
|
18262
18448
|
content: string4,
|
|
18263
18449
|
type: exports_external.enum(["chat", "system"]),
|
|
18264
18450
|
systemKind: exports_external.literal("thread").optional(),
|
|
@@ -18266,6 +18452,7 @@ var messageSchema = exports_external.strictObject({
|
|
|
18266
18452
|
replyToId: nullableString.optional(),
|
|
18267
18453
|
replyTo: exports_external.strictObject({
|
|
18268
18454
|
id: string4,
|
|
18455
|
+
authorId: string4.optional(),
|
|
18269
18456
|
authorName: string4,
|
|
18270
18457
|
text: string4,
|
|
18271
18458
|
deleted: exports_external.boolean().optional()
|
|
@@ -18460,6 +18647,7 @@ var communityMemberJoinSchema = exports_external.strictObject({
|
|
|
18460
18647
|
name: string4,
|
|
18461
18648
|
discriminator: string4,
|
|
18462
18649
|
avatar: string4.optional(),
|
|
18650
|
+
avatarVersion: exports_external.number().int().nonnegative(),
|
|
18463
18651
|
role: string4,
|
|
18464
18652
|
joinedAt: string4
|
|
18465
18653
|
})
|
|
@@ -18562,6 +18750,22 @@ var communityStatusUpdateSchema = exports_external.strictObject({
|
|
|
18562
18750
|
statusEmoji: nullableString,
|
|
18563
18751
|
statusText: nullableString
|
|
18564
18752
|
});
|
|
18753
|
+
var communityIdentityUpdateSchema = exports_external.strictObject({
|
|
18754
|
+
type: exports_external.literal("community:identity.update"),
|
|
18755
|
+
userId: string4,
|
|
18756
|
+
avatar: string4,
|
|
18757
|
+
avatarVersion: exports_external.number().int().positive()
|
|
18758
|
+
});
|
|
18759
|
+
var communityProfileUpdateSchema = exports_external.strictObject({
|
|
18760
|
+
type: exports_external.literal("community:profile.update"),
|
|
18761
|
+
userId: string4,
|
|
18762
|
+
name: string4,
|
|
18763
|
+
discriminator: string4,
|
|
18764
|
+
aboutMe: string4,
|
|
18765
|
+
bannerColor: nullableString,
|
|
18766
|
+
kind: exports_external.enum(["human", "bot"]),
|
|
18767
|
+
ownerUserId: nullableString
|
|
18768
|
+
});
|
|
18565
18769
|
var machineRuntimeSchema = CommunityMachineRuntimeSchema.strict();
|
|
18566
18770
|
var CommunityMachineSummarySchema2 = exports_external.strictObject({
|
|
18567
18771
|
id: string4,
|
|
@@ -18650,6 +18854,8 @@ var CommunityWsEventDiscriminatedSchema = exports_external.discriminatedUnion("t
|
|
|
18650
18854
|
communityInboxChangedSchema,
|
|
18651
18855
|
communityPresenceUpdateSchema,
|
|
18652
18856
|
communityStatusUpdateSchema,
|
|
18857
|
+
communityIdentityUpdateSchema,
|
|
18858
|
+
communityProfileUpdateSchema,
|
|
18653
18859
|
communityMachineCreatedSchema,
|
|
18654
18860
|
communityMachineStatusSchema,
|
|
18655
18861
|
communityMachineUpdatedSchema,
|
|
@@ -18696,6 +18902,8 @@ var WS_EVENTS = {
|
|
|
18696
18902
|
INBOX_CHANGED: "community:inbox.changed",
|
|
18697
18903
|
PRESENCE_UPDATE: "community:presence.update",
|
|
18698
18904
|
STATUS_UPDATE: "community:status.update",
|
|
18905
|
+
IDENTITY_UPDATE: "community:identity.update",
|
|
18906
|
+
PROFILE_UPDATE: "community:profile.update",
|
|
18699
18907
|
MACHINE_CREATED: "community:machine.created",
|
|
18700
18908
|
MACHINE_STATUS: "community:machine.status",
|
|
18701
18909
|
MACHINE_UPDATED: "community:machine.updated",
|
|
@@ -19274,7 +19482,7 @@ import * as fs12 from "fs";
|
|
|
19274
19482
|
import * as path13 from "path";
|
|
19275
19483
|
import * as crypto5 from "crypto";
|
|
19276
19484
|
import * as os3 from "os";
|
|
19277
|
-
import { homedir as
|
|
19485
|
+
import { homedir as homedir6 } from "os";
|
|
19278
19486
|
|
|
19279
19487
|
// src/discovery.ts
|
|
19280
19488
|
import * as path9 from "path";
|
|
@@ -20054,6 +20262,9 @@ class ProcessLane {
|
|
|
20054
20262
|
return false;
|
|
20055
20263
|
return proc.kill("SIGINT");
|
|
20056
20264
|
}
|
|
20265
|
+
updateSettings(input) {
|
|
20266
|
+
return this.driver.updateSettings?.(input) ?? Promise.resolve({ status: "unsupported" });
|
|
20267
|
+
}
|
|
20057
20268
|
attachProcess(proc) {
|
|
20058
20269
|
proc.stdout?.on("data", (chunk2) => {
|
|
20059
20270
|
const chunkText = chunk2.toString();
|
|
@@ -20128,8 +20339,8 @@ function resolveLaunchFieldsOrDefault(input) {
|
|
|
20128
20339
|
const envVars = Object.fromEntries(Object.entries(normalized.environment ?? {}).filter(([key]) => !controlled.has(key)));
|
|
20129
20340
|
const providerEnv = {};
|
|
20130
20341
|
const model = normalized.model.kind === "default" ? undefined : normalized.model.name;
|
|
20131
|
-
if (
|
|
20132
|
-
providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION =
|
|
20342
|
+
if (model && normalized.provider?.kind === "custom_endpoint") {
|
|
20343
|
+
providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = model;
|
|
20133
20344
|
}
|
|
20134
20345
|
if (normalized.provider?.kind === "custom_endpoint") {
|
|
20135
20346
|
providerEnv.ANTHROPIC_BASE_URL = normalized.provider.apiUrl;
|
|
@@ -20441,25 +20652,19 @@ class ClaudeEventNormalizer {
|
|
|
20441
20652
|
}
|
|
20442
20653
|
buildUsageTelemetry(event) {
|
|
20443
20654
|
const u = event?.usage;
|
|
20444
|
-
if (!u
|
|
20655
|
+
if (!u)
|
|
20445
20656
|
return null;
|
|
20657
|
+
const metric = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
20658
|
+
const cacheParts = [u.cache_read_input_tokens, u.cache_creation_input_tokens].filter((value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0);
|
|
20659
|
+
const cache = cacheParts.length > 0 && Number.isSafeInteger(cacheParts.reduce((sum, value) => sum + value, 0)) ? cacheParts.reduce((sum, value) => sum + value, 0) : null;
|
|
20446
20660
|
return {
|
|
20447
20661
|
kind: "telemetry",
|
|
20448
20662
|
name: "token_usage",
|
|
20449
20663
|
source: "claude_result_usage",
|
|
20450
|
-
|
|
20451
|
-
|
|
20452
|
-
|
|
20453
|
-
|
|
20454
|
-
cachedInputTokens: u?.cache_read_input_tokens,
|
|
20455
|
-
cacheCreationInputTokens: u?.cache_creation_input_tokens,
|
|
20456
|
-
totalCostUsd: event?.total_cost_usd,
|
|
20457
|
-
durationMs: event?.duration_ms,
|
|
20458
|
-
durationApiMs: event?.duration_api_ms,
|
|
20459
|
-
numTurns: event?.num_turns,
|
|
20460
|
-
resultSubtype: event?.subtype,
|
|
20461
|
-
resultIsError: event?.is_error,
|
|
20462
|
-
serviceTier: u?.service_tier
|
|
20664
|
+
usage: {
|
|
20665
|
+
input: metric(u.input_tokens),
|
|
20666
|
+
output: metric(u.output_tokens),
|
|
20667
|
+
cache
|
|
20463
20668
|
}
|
|
20464
20669
|
};
|
|
20465
20670
|
}
|
|
@@ -20470,6 +20675,7 @@ import { execFileSync as execFileSync2 } from "child_process";
|
|
|
20470
20675
|
import * as fs5 from "fs";
|
|
20471
20676
|
import * as path5 from "path";
|
|
20472
20677
|
var PROBE_TIMEOUT_MS = 5000;
|
|
20678
|
+
var PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
20473
20679
|
function resolveCommandOnPath(command, deps = {}) {
|
|
20474
20680
|
if (deps.which)
|
|
20475
20681
|
return deps.which(command);
|
|
@@ -20520,6 +20726,23 @@ function probeCommandVersion(command, args = [], deps = {}, platform = process.p
|
|
|
20520
20726
|
return { ok: false, error: String(code) };
|
|
20521
20727
|
}
|
|
20522
20728
|
}
|
|
20729
|
+
function probeCommandOutput(command, args, platform = process.platform) {
|
|
20730
|
+
try {
|
|
20731
|
+
const output = execFileSync2(command, args, {
|
|
20732
|
+
encoding: "utf8",
|
|
20733
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
20734
|
+
maxBuffer: PROBE_OUTPUT_MAX_BYTES,
|
|
20735
|
+
shell: needsWindowsShimShell(command, platform),
|
|
20736
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
20737
|
+
input: "",
|
|
20738
|
+
env: { ...process.env, CI: "1" }
|
|
20739
|
+
});
|
|
20740
|
+
return { ok: true, output };
|
|
20741
|
+
} catch (err) {
|
|
20742
|
+
const code = err?.code ?? "command_probe_failed";
|
|
20743
|
+
return { ok: false, error: String(code) };
|
|
20744
|
+
}
|
|
20745
|
+
}
|
|
20523
20746
|
function resolveHomePath(relativePath, deps = {}) {
|
|
20524
20747
|
return path5.join(deps.homeDir || process.env.HOME || ".", relativePath);
|
|
20525
20748
|
}
|
|
@@ -20626,6 +20849,14 @@ class ClaudeTurnProtocol {
|
|
|
20626
20849
|
}
|
|
20627
20850
|
|
|
20628
20851
|
// agent-driver/dist/adapters/claude/index.js
|
|
20852
|
+
var CLAUDE_MODEL_CATALOG = {
|
|
20853
|
+
updateMode: "unsupported",
|
|
20854
|
+
models: ["opus", "sonnet", "haiku"].map((id) => ({
|
|
20855
|
+
id,
|
|
20856
|
+
supportedReasoningEfforts: []
|
|
20857
|
+
}))
|
|
20858
|
+
};
|
|
20859
|
+
|
|
20629
20860
|
class ClaudeDriver {
|
|
20630
20861
|
id = "claude";
|
|
20631
20862
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
@@ -20642,10 +20873,11 @@ class ClaudeDriver {
|
|
|
20642
20873
|
}
|
|
20643
20874
|
probe(command) {
|
|
20644
20875
|
const explicit = command?.trim();
|
|
20645
|
-
|
|
20646
|
-
|
|
20647
|
-
|
|
20648
|
-
|
|
20876
|
+
const base = explicit ? (() => {
|
|
20877
|
+
const result = probeCommandVersion(explicit);
|
|
20878
|
+
return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
|
|
20879
|
+
})() : probeClaude();
|
|
20880
|
+
return base.status === "healthy" ? { ...base, reasoning: CLAUDE_MODEL_CATALOG } : base;
|
|
20649
20881
|
}
|
|
20650
20882
|
async openLane(ctx, options) {
|
|
20651
20883
|
return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
|
|
@@ -20691,49 +20923,153 @@ class ClaudeDriver {
|
|
|
20691
20923
|
}
|
|
20692
20924
|
|
|
20693
20925
|
// agent-driver/dist/adapters/codex/telemetry.js
|
|
20694
|
-
function
|
|
20926
|
+
function metric(value) {
|
|
20927
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
20928
|
+
}
|
|
20929
|
+
function nonCachedInput(input, cached2) {
|
|
20930
|
+
if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0 || typeof cached2 !== "number" || !Number.isSafeInteger(cached2) || cached2 < 0 || cached2 > input)
|
|
20931
|
+
return null;
|
|
20932
|
+
return input - cached2;
|
|
20933
|
+
}
|
|
20934
|
+
function canonicalId(value, fallback) {
|
|
20935
|
+
return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).length <= 64 ? value : fallback;
|
|
20936
|
+
}
|
|
20937
|
+
function mappedPlanName(value) {
|
|
20938
|
+
switch (value) {
|
|
20939
|
+
case "free":
|
|
20940
|
+
return "Free";
|
|
20941
|
+
case "plus":
|
|
20942
|
+
return "Plus";
|
|
20943
|
+
case "pro":
|
|
20944
|
+
return "Pro";
|
|
20945
|
+
case "team":
|
|
20946
|
+
return "Team";
|
|
20947
|
+
case "business":
|
|
20948
|
+
return "Business";
|
|
20949
|
+
case "enterprise":
|
|
20950
|
+
return "Enterprise";
|
|
20951
|
+
case "edu":
|
|
20952
|
+
return "Education";
|
|
20953
|
+
default:
|
|
20954
|
+
return;
|
|
20955
|
+
}
|
|
20956
|
+
}
|
|
20957
|
+
function quotaWindow(minutes, slot) {
|
|
20958
|
+
if (typeof minutes !== "number" || !Number.isSafeInteger(minutes) || minutes <= 0)
|
|
20959
|
+
return null;
|
|
20960
|
+
if (minutes === 1440)
|
|
20961
|
+
return { kind: "calendar", period: "day", displayName: "Daily usage limit" };
|
|
20962
|
+
if (minutes === 10080)
|
|
20963
|
+
return { kind: "calendar", period: "week", displayName: "Weekly usage limit" };
|
|
20964
|
+
if (minutes === 43200)
|
|
20965
|
+
return { kind: "calendar", period: "month", displayName: "Monthly usage limit" };
|
|
20966
|
+
return {
|
|
20967
|
+
kind: "rolling",
|
|
20968
|
+
durationSeconds: minutes * 60,
|
|
20969
|
+
displayName: slot === "primary" && minutes === 300 ? "5 hour usage limit" : `${minutes} minute usage limit`
|
|
20970
|
+
};
|
|
20971
|
+
}
|
|
20972
|
+
function resetIso(value) {
|
|
20973
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
20974
|
+
const date5 = new Date(value < 10000000000 ? value * 1000 : value);
|
|
20975
|
+
return Number.isNaN(date5.getTime()) ? undefined : date5.toISOString();
|
|
20976
|
+
}
|
|
20977
|
+
if (typeof value === "string") {
|
|
20978
|
+
const date5 = new Date(value);
|
|
20979
|
+
return Number.isNaN(date5.getTime()) ? undefined : date5.toISOString();
|
|
20980
|
+
}
|
|
20981
|
+
return;
|
|
20982
|
+
}
|
|
20983
|
+
function mapCodexQuotaSnapshots(snapshots, sourceEpoch) {
|
|
20984
|
+
const limits = [];
|
|
20985
|
+
let planName;
|
|
20986
|
+
for (const snapshot of snapshots) {
|
|
20987
|
+
const limitId = canonicalId(snapshot?.limitId ?? snapshot?.limit_id, "codex");
|
|
20988
|
+
const spark = /spark/i.test(limitId) || /spark/i.test(String(snapshot?.limitName ?? snapshot?.limit_name ?? ""));
|
|
20989
|
+
const product = spark ? { kind: "reported", id: "codex-spark", displayName: "Spark" } : { kind: "reported", id: "codex", displayName: "Codex" };
|
|
20990
|
+
const model = spark ? { kind: "reported", id: "gpt-5.3-codex-spark" } : { kind: "not_applicable" };
|
|
20991
|
+
planName ??= mappedPlanName(snapshot?.planType ?? snapshot?.plan_type);
|
|
20992
|
+
for (const slot of ["primary", "secondary"]) {
|
|
20993
|
+
const value = snapshot?.[slot];
|
|
20994
|
+
const window2 = quotaWindow(value?.windowDurationMins ?? value?.window_duration_mins, slot);
|
|
20995
|
+
const usedPercent = value?.usedPercent ?? value?.used_percent;
|
|
20996
|
+
if (!window2 || typeof usedPercent !== "number" || !Number.isFinite(usedPercent) || usedPercent < 0 || usedPercent > 100)
|
|
20997
|
+
continue;
|
|
20998
|
+
const resetsAt = resetIso(value?.resetsAt ?? value?.resets_at);
|
|
20999
|
+
limits.push({
|
|
21000
|
+
bucket: { limitId, product, model, window: window2 },
|
|
21001
|
+
usedPercent,
|
|
21002
|
+
...resetsAt ? { resetsAt } : {}
|
|
21003
|
+
});
|
|
21004
|
+
}
|
|
21005
|
+
}
|
|
21006
|
+
if (limits.length === 0) {
|
|
21007
|
+
return {
|
|
21008
|
+
kind: "telemetry",
|
|
21009
|
+
name: "rate_limits",
|
|
21010
|
+
source: "codex_account_rate_limits_updated",
|
|
21011
|
+
quota: { status: "error", sourceEpoch, code: "invalid_response", retryable: true }
|
|
21012
|
+
};
|
|
21013
|
+
}
|
|
21014
|
+
return {
|
|
21015
|
+
kind: "telemetry",
|
|
21016
|
+
name: "rate_limits",
|
|
21017
|
+
source: "codex_account_rate_limits_updated",
|
|
21018
|
+
quota: {
|
|
21019
|
+
status: "available",
|
|
21020
|
+
sourceEpoch,
|
|
21021
|
+
...planName ? { planName } : {},
|
|
21022
|
+
freshForSeconds: 300,
|
|
21023
|
+
limits
|
|
21024
|
+
}
|
|
21025
|
+
};
|
|
21026
|
+
}
|
|
21027
|
+
function mapCodexTelemetry(method, params, sourceEpoch) {
|
|
20695
21028
|
if (method === "thread/tokenUsage/updated") {
|
|
20696
|
-
const u = params?.
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
|
|
20704
|
-
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
|
|
20708
|
-
|
|
20709
|
-
modelContextWindow: u.modelContextWindow ?? u.model_context_window,
|
|
20710
|
-
cachedInputRatio: u.cachedInputRatio,
|
|
20711
|
-
contextUtilization: u.contextUtilization
|
|
20712
|
-
}
|
|
21029
|
+
const u = params?.tokenUsage?.last ?? params?.token_usage?.last;
|
|
21030
|
+
if (!u)
|
|
21031
|
+
return [];
|
|
21032
|
+
const input = u.inputTokens ?? u.input_tokens;
|
|
21033
|
+
const cached2 = u.cachedInputTokens ?? u.cached_input_tokens;
|
|
21034
|
+
return [{
|
|
21035
|
+
kind: "telemetry",
|
|
21036
|
+
name: "token_usage",
|
|
21037
|
+
source: "codex_thread_token_usage_updated",
|
|
21038
|
+
usage: {
|
|
21039
|
+
input: nonCachedInput(input, cached2),
|
|
21040
|
+
output: metric(u.outputTokens ?? u.output_tokens),
|
|
21041
|
+
cache: metric(cached2)
|
|
20713
21042
|
}
|
|
20714
|
-
];
|
|
21043
|
+
}];
|
|
20715
21044
|
}
|
|
20716
21045
|
if (method === "account/rateLimits/updated") {
|
|
20717
|
-
|
|
20718
|
-
return [
|
|
20719
|
-
{
|
|
20720
|
-
kind: "telemetry",
|
|
20721
|
-
name: "rate_limits",
|
|
20722
|
-
source: "codex_account_rate_limits_updated",
|
|
20723
|
-
attrs: {
|
|
20724
|
-
limitId: r.limitId,
|
|
20725
|
-
planType: r.planType,
|
|
20726
|
-
usedPercent: r.usedPercent,
|
|
20727
|
-
windowDurationMins: r.windowDurationMins,
|
|
20728
|
-
resetsAt: r.resetsAt
|
|
20729
|
-
}
|
|
20730
|
-
}
|
|
20731
|
-
];
|
|
21046
|
+
return [mapCodexQuotaSnapshots([params?.rateLimits ?? params ?? {}], sourceEpoch)];
|
|
20732
21047
|
}
|
|
20733
21048
|
return [];
|
|
20734
21049
|
}
|
|
20735
21050
|
|
|
20736
21051
|
// agent-driver/dist/adapters/codex/normalizer.js
|
|
21052
|
+
import { randomBytes } from "node:crypto";
|
|
21053
|
+
var codexQuotaSourceEpoch = randomBytes(16).toString("base64url");
|
|
21054
|
+
var codexQuotaSourceGeneration = 0;
|
|
21055
|
+
var codexAccountFingerprint = null;
|
|
21056
|
+
function rotateCodexQuotaSource() {
|
|
21057
|
+
codexQuotaSourceEpoch = randomBytes(16).toString("base64url");
|
|
21058
|
+
codexQuotaSourceGeneration += 1;
|
|
21059
|
+
codexAccountFingerprint = null;
|
|
21060
|
+
}
|
|
21061
|
+
function observeCodexAccount(result) {
|
|
21062
|
+
const account2 = result?.account;
|
|
21063
|
+
const fingerprint = account2 && typeof account2 === "object" ? JSON.stringify([
|
|
21064
|
+
account2.type ?? "unknown",
|
|
21065
|
+
account2.email ?? null,
|
|
21066
|
+
account2.planType ?? account2.plan_type ?? null
|
|
21067
|
+
]) : "none";
|
|
21068
|
+
if (codexAccountFingerprint !== null && codexAccountFingerprint !== fingerprint) {
|
|
21069
|
+
rotateCodexQuotaSource();
|
|
21070
|
+
}
|
|
21071
|
+
codexAccountFingerprint = fingerprint;
|
|
21072
|
+
}
|
|
20737
21073
|
function normalizeFileChangeInput(item) {
|
|
20738
21074
|
const paths = [];
|
|
20739
21075
|
const seen = new Set;
|
|
@@ -20756,6 +21092,12 @@ function normalizeFileChangeInput(item) {
|
|
|
20756
21092
|
}
|
|
20757
21093
|
|
|
20758
21094
|
class CodexEventNormalizer {
|
|
21095
|
+
quotaReadRequestIds = new Set;
|
|
21096
|
+
accountReadRequestIds = new Set;
|
|
21097
|
+
rateLimitSnapshots = new Map;
|
|
21098
|
+
quotaSnapshotInitialized = false;
|
|
21099
|
+
quotaSourceGeneration = codexQuotaSourceGeneration;
|
|
21100
|
+
pendingTurnUsage = null;
|
|
20759
21101
|
threadId = null;
|
|
20760
21102
|
turnId = null;
|
|
20761
21103
|
terminalTurn = null;
|
|
@@ -20766,10 +21108,69 @@ class CodexEventNormalizer {
|
|
|
20766
21108
|
get currentTurnId() {
|
|
20767
21109
|
return this.turnId;
|
|
20768
21110
|
}
|
|
21111
|
+
registerQuotaReadRequest(requestId) {
|
|
21112
|
+
this.quotaReadRequestIds.add(requestId);
|
|
21113
|
+
}
|
|
21114
|
+
registerAccountReadRequest(requestId) {
|
|
21115
|
+
this.accountReadRequestIds.add(requestId);
|
|
21116
|
+
}
|
|
21117
|
+
syncQuotaSourceGeneration() {
|
|
21118
|
+
if (this.quotaSourceGeneration === codexQuotaSourceGeneration)
|
|
21119
|
+
return;
|
|
21120
|
+
this.quotaSourceGeneration = codexQuotaSourceGeneration;
|
|
21121
|
+
this.rateLimitSnapshots.clear();
|
|
21122
|
+
this.quotaSnapshotInitialized = false;
|
|
21123
|
+
}
|
|
21124
|
+
quotaSnapshots(value) {
|
|
21125
|
+
const byLimitId = value?.rateLimitsByLimitId ?? value?.rate_limits_by_limit_id;
|
|
21126
|
+
if (byLimitId && typeof byLimitId === "object" && !Array.isArray(byLimitId)) {
|
|
21127
|
+
return Object.entries(byLimitId).flatMap(([key2, snapshot2]) => {
|
|
21128
|
+
if (!snapshot2 || typeof snapshot2 !== "object" || Array.isArray(snapshot2))
|
|
21129
|
+
return [];
|
|
21130
|
+
return [[key2, {
|
|
21131
|
+
...snapshot2,
|
|
21132
|
+
limitId: snapshot2.limitId ?? snapshot2.limit_id ?? key2
|
|
21133
|
+
}]];
|
|
21134
|
+
});
|
|
21135
|
+
}
|
|
21136
|
+
const snapshot = value?.rateLimits ?? value?.rate_limits ?? value;
|
|
21137
|
+
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
21138
|
+
return [];
|
|
21139
|
+
const record2 = snapshot;
|
|
21140
|
+
const key = typeof record2.limitId === "string" ? record2.limitId : typeof record2.limit_id === "string" ? record2.limit_id : "codex";
|
|
21141
|
+
return [[key, { ...record2, limitId: key }]];
|
|
21142
|
+
}
|
|
21143
|
+
replaceQuotaSnapshots(value) {
|
|
21144
|
+
this.syncQuotaSourceGeneration();
|
|
21145
|
+
this.rateLimitSnapshots.clear();
|
|
21146
|
+
for (const [key, snapshot] of this.quotaSnapshots(value)) {
|
|
21147
|
+
this.rateLimitSnapshots.set(key, snapshot);
|
|
21148
|
+
}
|
|
21149
|
+
this.quotaSnapshotInitialized = true;
|
|
21150
|
+
return [mapCodexQuotaSnapshots([...this.rateLimitSnapshots.values()], codexQuotaSourceEpoch)];
|
|
21151
|
+
}
|
|
21152
|
+
mergeQuotaSnapshots(value) {
|
|
21153
|
+
this.syncQuotaSourceGeneration();
|
|
21154
|
+
for (const [key, update] of this.quotaSnapshots(value)) {
|
|
21155
|
+
const merged = {
|
|
21156
|
+
...this.rateLimitSnapshots.get(key) ?? {},
|
|
21157
|
+
limitId: key
|
|
21158
|
+
};
|
|
21159
|
+
for (const [field, fieldValue] of Object.entries(update)) {
|
|
21160
|
+
if (fieldValue !== undefined && fieldValue !== null)
|
|
21161
|
+
merged[field] = fieldValue;
|
|
21162
|
+
}
|
|
21163
|
+
this.rateLimitSnapshots.set(key, merged);
|
|
21164
|
+
}
|
|
21165
|
+
if (!this.quotaSnapshotInitialized)
|
|
21166
|
+
return [];
|
|
21167
|
+
return [mapCodexQuotaSnapshots([...this.rateLimitSnapshots.values()], codexQuotaSourceEpoch)];
|
|
21168
|
+
}
|
|
20769
21169
|
adoptThreadId(threadId) {
|
|
20770
21170
|
if (threadId !== this.threadId) {
|
|
20771
21171
|
this.turnId = null;
|
|
20772
21172
|
this.terminalTurn = null;
|
|
21173
|
+
this.pendingTurnUsage = null;
|
|
20773
21174
|
}
|
|
20774
21175
|
this.threadId = threadId;
|
|
20775
21176
|
}
|
|
@@ -20787,6 +21188,25 @@ class CodexEventNormalizer {
|
|
|
20787
21188
|
const msg = tryParseJsonLine(line);
|
|
20788
21189
|
if (!msg)
|
|
20789
21190
|
return [];
|
|
21191
|
+
if (msg?.id !== undefined && this.accountReadRequestIds.delete(msg.id)) {
|
|
21192
|
+
if (!msg.error) {
|
|
21193
|
+
observeCodexAccount(msg.result);
|
|
21194
|
+
this.syncQuotaSourceGeneration();
|
|
21195
|
+
}
|
|
21196
|
+
return [];
|
|
21197
|
+
}
|
|
21198
|
+
if (msg?.id !== undefined && this.quotaReadRequestIds.delete(msg.id)) {
|
|
21199
|
+
this.syncQuotaSourceGeneration();
|
|
21200
|
+
if (msg.error) {
|
|
21201
|
+
return [{
|
|
21202
|
+
kind: "telemetry",
|
|
21203
|
+
name: "rate_limits",
|
|
21204
|
+
source: "codex_account_rate_limits_read",
|
|
21205
|
+
quota: { status: "error", sourceEpoch: codexQuotaSourceEpoch, code: "provider_error", retryable: true }
|
|
21206
|
+
}];
|
|
21207
|
+
}
|
|
21208
|
+
return this.replaceQuotaSnapshots(msg.result ?? {});
|
|
21209
|
+
}
|
|
20790
21210
|
if (msg?.error && msg.id !== undefined) {
|
|
20791
21211
|
return [{ kind: "error", message: msg.error?.message ?? "Codex RPC error" }];
|
|
20792
21212
|
}
|
|
@@ -20811,6 +21231,7 @@ class CodexEventNormalizer {
|
|
|
20811
21231
|
return [];
|
|
20812
21232
|
this.turnId = params.turn.id;
|
|
20813
21233
|
this.terminalTurn = null;
|
|
21234
|
+
this.pendingTurnUsage = null;
|
|
20814
21235
|
return [
|
|
20815
21236
|
{
|
|
20816
21237
|
kind: "turn_owner",
|
|
@@ -20839,24 +21260,36 @@ class CodexEventNormalizer {
|
|
|
20839
21260
|
case "turn/completed":
|
|
20840
21261
|
if (!this.acceptRootTerminal(params))
|
|
20841
21262
|
return [];
|
|
21263
|
+
const usage = this.pendingTurnUsage;
|
|
21264
|
+
this.pendingTurnUsage = null;
|
|
20842
21265
|
if (params.turn.status === "failed") {
|
|
20843
21266
|
return [
|
|
21267
|
+
...usage ? [usage] : [],
|
|
20844
21268
|
{ kind: "error", message: "Codex turn failed" },
|
|
20845
21269
|
{ kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }
|
|
20846
21270
|
];
|
|
20847
21271
|
}
|
|
20848
21272
|
if (params.turn.status === "interrupted") {
|
|
20849
|
-
return [{ kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
|
|
21273
|
+
return [...usage ? [usage] : [], { kind: "error", message: "Codex turn interrupted" }, { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
|
|
20850
21274
|
}
|
|
20851
|
-
return [{ kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
|
|
21275
|
+
return [...usage ? [usage] : [], { kind: "turn_end", sessionId: this.threadId ?? undefined, turnOwner: this.turnReceipt(params.threadId, params.turn.id) }];
|
|
20852
21276
|
case "error":
|
|
20853
21277
|
if (params?.willRetry === true) {
|
|
20854
21278
|
return [{ kind: "runtime_recovery", stage: "retrying", source: "codex_stream" }];
|
|
20855
21279
|
}
|
|
20856
21280
|
return [{ kind: "error", message: params?.error?.message ?? params?.message ?? "Codex error" }];
|
|
20857
|
-
case "thread/tokenUsage/updated":
|
|
21281
|
+
case "thread/tokenUsage/updated": {
|
|
21282
|
+
const usage2 = mapCodexTelemetry(method, params, codexQuotaSourceEpoch)[0];
|
|
21283
|
+
if (usage2)
|
|
21284
|
+
this.pendingTurnUsage = usage2;
|
|
21285
|
+
return [];
|
|
21286
|
+
}
|
|
20858
21287
|
case "account/rateLimits/updated":
|
|
20859
|
-
return
|
|
21288
|
+
return this.mergeQuotaSnapshots(params);
|
|
21289
|
+
case "account/updated":
|
|
21290
|
+
rotateCodexQuotaSource();
|
|
21291
|
+
this.syncQuotaSourceGeneration();
|
|
21292
|
+
return [];
|
|
20860
21293
|
default:
|
|
20861
21294
|
return [];
|
|
20862
21295
|
}
|
|
@@ -20969,7 +21402,102 @@ function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
|
|
|
20969
21402
|
return path6.join(opts.defaultHomeDir ?? os.homedir(), ".codex");
|
|
20970
21403
|
}
|
|
20971
21404
|
|
|
21405
|
+
// agent-driver/dist/internal/errors.js
|
|
21406
|
+
var MAX_PUBLIC_ERROR_MESSAGE = 1000;
|
|
21407
|
+
var CREDENTIAL_NAME = String.raw`(?:[A-Za-z0-9]{1,32}[_-]){0,4}(?:api[_-]?key|access[_-]?key|secret(?:[_-]?access[_-]?key)?|client[_-]?secret|access[_-]?token|auth(?:orization)?|password|passwd|token|voucher)(?:[_-][A-Za-z0-9]{1,32}){0,4}`;
|
|
21408
|
+
var QUOTED_CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(["'])(${CREDENTIAL_NAME})\1\s*[:=]\s*(["'])[^"'\r\n]*\3`, "gi");
|
|
21409
|
+
var CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9_-])((?:${CREDENTIAL_NAME})\s*[:=]\s*)(?!\[redacted\])[^\s,;}\]]+`, "gi");
|
|
21410
|
+
function scrubDriverErrorMessage(value, fallback = "Runtime operation failed") {
|
|
21411
|
+
const text2 = value instanceof Error ? value.message : String(value ?? "");
|
|
21412
|
+
const scrubbed = text2.replace(/\b(?:cmk|cmt|crk)_[A-Za-z0-9_-]+\b/g, "[redacted-token]").replace(/(Authorization\s*:\s*)(?:Bearer|Basic)\s+[^\s,;]+/gi, "$1[redacted]").replace(/Bearer\s+[A-Za-z0-9._\-]+/gi, "Bearer [redacted]").replace(/\b(?:sk|sk-ant|sk-proj|xox[abprs])-[A-Za-z0-9._\-]+/gi, "[redacted-token]").replace(QUOTED_CREDENTIAL_ASSIGNMENT, "$1$2$1:$3[redacted]$3").replace(CREDENTIAL_ASSIGNMENT, "$1[redacted]").replace(/(?<![A-Za-z0-9._%+\-])[A-Za-z0-9._%+\-]{1,320}@[A-Za-z0-9.\-]{1,255}\.[A-Za-z]{2,63}/g, "[redacted-email]").replace(/([?&])([^=\s]+)=([^&\s]+)/g, "$1$2=[redacted]").replace(/\/(?:Users|home)\/[^\r\n,;]+/g, "[redacted-path]").replace(/[A-Za-z]:\\Users\\[^\r\n,;]+/gi, "[redacted-path]").replace(/\\\\[^\\\s]+\\[^\r\n,;]+/g, "[redacted-path]").replace(/(?:[A-Za-z]:\\|\/)(?:[^\s/:]+[\\/]){1,}[^\s:]*/g, "[redacted-path]").trim();
|
|
21413
|
+
return (scrubbed || fallback).slice(0, MAX_PUBLIC_ERROR_MESSAGE);
|
|
21414
|
+
}
|
|
21415
|
+
function scrubDriverError(error51) {
|
|
21416
|
+
return {
|
|
21417
|
+
...error51,
|
|
21418
|
+
code: stableErrorCode(error51.code, "runtime_error"),
|
|
21419
|
+
message: scrubDriverErrorMessage(error51.message),
|
|
21420
|
+
...error51.details ? { details: scrubDetails(error51.details) } : {}
|
|
21421
|
+
};
|
|
21422
|
+
}
|
|
21423
|
+
function scrubDetails(details) {
|
|
21424
|
+
const scrubValue = (value, key) => {
|
|
21425
|
+
if (key && /api[_-]?key|access[_-]?token|auth(?:orization)?|password|passwd|secret|token/i.test(key)) {
|
|
21426
|
+
return "[redacted]";
|
|
21427
|
+
}
|
|
21428
|
+
if (typeof value === "string")
|
|
21429
|
+
return scrubDriverErrorMessage(value, "[redacted]");
|
|
21430
|
+
if (Array.isArray(value))
|
|
21431
|
+
return value.map((item) => scrubValue(item));
|
|
21432
|
+
if (value && typeof value === "object") {
|
|
21433
|
+
return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
|
|
21434
|
+
childKey,
|
|
21435
|
+
scrubValue(child, childKey)
|
|
21436
|
+
]));
|
|
21437
|
+
}
|
|
21438
|
+
return value;
|
|
21439
|
+
};
|
|
21440
|
+
return scrubValue(details);
|
|
21441
|
+
}
|
|
21442
|
+
function stableErrorCode(value, fallback) {
|
|
21443
|
+
const code = String(value ?? "");
|
|
21444
|
+
return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
|
|
21445
|
+
}
|
|
21446
|
+
|
|
21447
|
+
// agent-driver/dist/internal/modelCatalog.js
|
|
21448
|
+
var RUNTIME_MODEL_CATALOG_MAX = 512;
|
|
21449
|
+
var RUNTIME_MODEL_ID_MAX = 100;
|
|
21450
|
+
function normalizeRuntimeModelId(value) {
|
|
21451
|
+
if (typeof value !== "string")
|
|
21452
|
+
return;
|
|
21453
|
+
const id = value.trim();
|
|
21454
|
+
if (!id || id.length > RUNTIME_MODEL_ID_MAX || /\s/.test(id))
|
|
21455
|
+
return;
|
|
21456
|
+
return id;
|
|
21457
|
+
}
|
|
21458
|
+
function catalogFromIds(ids) {
|
|
21459
|
+
const seen = new Set;
|
|
21460
|
+
const models = [];
|
|
21461
|
+
for (const rawId of ids) {
|
|
21462
|
+
const id = normalizeRuntimeModelId(rawId);
|
|
21463
|
+
if (!id || seen.has(id))
|
|
21464
|
+
continue;
|
|
21465
|
+
if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
|
|
21466
|
+
return;
|
|
21467
|
+
seen.add(id);
|
|
21468
|
+
models.push({ id, supportedReasoningEfforts: [] });
|
|
21469
|
+
}
|
|
21470
|
+
if (models.length === 0)
|
|
21471
|
+
return;
|
|
21472
|
+
return { updateMode: "unsupported", models };
|
|
21473
|
+
}
|
|
21474
|
+
function parseOpenCodeModelCatalog(output) {
|
|
21475
|
+
const ids = output.split(/\r?\n/).flatMap((line) => {
|
|
21476
|
+
const id = normalizeRuntimeModelId(line);
|
|
21477
|
+
return id && /^[^/]+\/.+$/.test(id) ? [id] : [];
|
|
21478
|
+
});
|
|
21479
|
+
return catalogFromIds(ids);
|
|
21480
|
+
}
|
|
21481
|
+
function parsePiModelCatalog(values) {
|
|
21482
|
+
if (!Array.isArray(values))
|
|
21483
|
+
return;
|
|
21484
|
+
const ids = values.flatMap((value) => {
|
|
21485
|
+
if (!value || typeof value !== "object")
|
|
21486
|
+
return [];
|
|
21487
|
+
const model = value;
|
|
21488
|
+
const provider = normalizeRuntimeModelId(model.provider);
|
|
21489
|
+
const id = normalizeRuntimeModelId(model.id);
|
|
21490
|
+
return provider && id && !provider.includes("/") ? [`${provider}/${id}`] : [];
|
|
21491
|
+
});
|
|
21492
|
+
return catalogFromIds(ids);
|
|
21493
|
+
}
|
|
21494
|
+
|
|
20972
21495
|
// agent-driver/dist/adapters/codex/index.js
|
|
21496
|
+
var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
|
|
21497
|
+
var MODEL_LIST_TIMEOUT_MS = 5000;
|
|
21498
|
+
var MODEL_LIST_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
21499
|
+
var MODEL_LIST_MAX = RUNTIME_MODEL_CATALOG_MAX;
|
|
21500
|
+
var MODEL_EFFORT_MAX = 16;
|
|
20973
21501
|
function isCodexMissingRolloutError(message2) {
|
|
20974
21502
|
return /\bno\s+rollout\s+found\b/i.test(message2) || /\bmissing\s+rollout\b/i.test(message2) || /\brollout\b.*\b(not found|missing)\b/i.test(message2) || /\b(not found|missing)\b.*\brollout\b/i.test(message2);
|
|
20975
21503
|
}
|
|
@@ -20990,19 +21518,173 @@ class CodexDriver {
|
|
|
20990
21518
|
}
|
|
20991
21519
|
};
|
|
20992
21520
|
eventNormalizer = new CodexEventNormalizer;
|
|
21521
|
+
pendingAccountReadRequestIds = new Set;
|
|
20993
21522
|
requestId = 0;
|
|
20994
21523
|
codexHomeRoot = null;
|
|
20995
21524
|
proc = null;
|
|
20996
21525
|
pendingInitialPrompt = null;
|
|
20997
21526
|
pendingResumeFallbackParams = null;
|
|
21527
|
+
pendingSettingsUpdates = new Map;
|
|
20998
21528
|
nextRequestId() {
|
|
20999
21529
|
return ++this.requestId;
|
|
21000
21530
|
}
|
|
21531
|
+
requestAccountQuotaSnapshot() {
|
|
21532
|
+
if (!this.proc?.stdin || this.proc.stdin.destroyed)
|
|
21533
|
+
return;
|
|
21534
|
+
const accountReadRequestId = this.nextRequestId();
|
|
21535
|
+
this.pendingAccountReadRequestIds.add(accountReadRequestId);
|
|
21536
|
+
this.eventNormalizer.registerAccountReadRequest(accountReadRequestId);
|
|
21537
|
+
this.proc.stdin.write(jsonRpcRequest("account/read", { refreshToken: false }, accountReadRequestId) + `
|
|
21538
|
+
`);
|
|
21539
|
+
}
|
|
21540
|
+
requestQuotaSnapshot() {
|
|
21541
|
+
if (!this.proc?.stdin || this.proc.stdin.destroyed)
|
|
21542
|
+
return;
|
|
21543
|
+
const quotaReadRequestId = this.nextRequestId();
|
|
21544
|
+
this.eventNormalizer.registerQuotaReadRequest(quotaReadRequestId);
|
|
21545
|
+
this.proc.stdin.write(jsonRpcRequest("account/rateLimits/read", {}, quotaReadRequestId) + `
|
|
21546
|
+
`);
|
|
21547
|
+
}
|
|
21001
21548
|
get codexHome() {
|
|
21002
21549
|
return this.codexHomeRoot;
|
|
21003
21550
|
}
|
|
21004
|
-
probe(command) {
|
|
21005
|
-
|
|
21551
|
+
async probe(command) {
|
|
21552
|
+
const result = await probeCliRuntime("codex", {}, command);
|
|
21553
|
+
if (result.status !== "healthy")
|
|
21554
|
+
return result;
|
|
21555
|
+
return {
|
|
21556
|
+
...result,
|
|
21557
|
+
reasoning: await this.probeReasoningCatalog(command)
|
|
21558
|
+
};
|
|
21559
|
+
}
|
|
21560
|
+
async probeReasoningCatalog(command) {
|
|
21561
|
+
const spec = resolveSpawnSpec("codex", ["app-server", "--listen", "stdio://"], command);
|
|
21562
|
+
let proc;
|
|
21563
|
+
try {
|
|
21564
|
+
proc = spawnAgentProcess(spec.command, spec.args, {
|
|
21565
|
+
cwd: process.cwd(),
|
|
21566
|
+
env: { ...process.env, CI: "1" },
|
|
21567
|
+
shell: spec.shell
|
|
21568
|
+
});
|
|
21569
|
+
} catch {
|
|
21570
|
+
return;
|
|
21571
|
+
}
|
|
21572
|
+
return new Promise((resolve2) => {
|
|
21573
|
+
let settled = false;
|
|
21574
|
+
let buffer = "";
|
|
21575
|
+
let outputBytes = 0;
|
|
21576
|
+
let nextId = 0;
|
|
21577
|
+
let initializeId = 0;
|
|
21578
|
+
let listId = 0;
|
|
21579
|
+
const models = [];
|
|
21580
|
+
const seenModels = new Set;
|
|
21581
|
+
let overflow = false;
|
|
21582
|
+
let defaultModelId;
|
|
21583
|
+
const finish = (catalog) => {
|
|
21584
|
+
if (settled)
|
|
21585
|
+
return;
|
|
21586
|
+
settled = true;
|
|
21587
|
+
clearTimeout(timer);
|
|
21588
|
+
const done = proc.pid ? killProcessTree(proc.pid, { graceMs: 250 }).catch(() => {}) : Promise.resolve().then(() => {
|
|
21589
|
+
proc.kill("SIGTERM");
|
|
21590
|
+
});
|
|
21591
|
+
done.finally(() => resolve2(catalog));
|
|
21592
|
+
};
|
|
21593
|
+
const requestModelPage = (cursor) => {
|
|
21594
|
+
listId = ++nextId;
|
|
21595
|
+
proc.stdin?.write(jsonRpcRequest("model/list", { limit: Math.min(MODEL_LIST_MAX - models.length, MODEL_LIST_MAX), includeHidden: false, ...cursor ? { cursor } : {} }, listId) + `
|
|
21596
|
+
`);
|
|
21597
|
+
};
|
|
21598
|
+
const consumeModel = (value) => {
|
|
21599
|
+
if (!value || typeof value !== "object")
|
|
21600
|
+
return;
|
|
21601
|
+
const model = value;
|
|
21602
|
+
const id = normalizeRuntimeModelId(model.id);
|
|
21603
|
+
if (!id || seenModels.has(id))
|
|
21604
|
+
return;
|
|
21605
|
+
if (models.length >= MODEL_LIST_MAX) {
|
|
21606
|
+
overflow = true;
|
|
21607
|
+
return;
|
|
21608
|
+
}
|
|
21609
|
+
const rawOptions = Array.isArray(model.supportedReasoningEfforts) ? model.supportedReasoningEfforts : [];
|
|
21610
|
+
const seenEfforts = new Set;
|
|
21611
|
+
const supportedReasoningEfforts = rawOptions.flatMap((raw) => {
|
|
21612
|
+
if (!raw || typeof raw !== "object")
|
|
21613
|
+
return [];
|
|
21614
|
+
const option = raw;
|
|
21615
|
+
const value2 = typeof option.reasoningEffort === "string" ? option.reasoningEffort.trim() : "";
|
|
21616
|
+
if (!value2 || value2.length > 32 || !/^[A-Za-z0-9._-]+$/.test(value2) || seenEfforts.has(value2))
|
|
21617
|
+
return [];
|
|
21618
|
+
seenEfforts.add(value2);
|
|
21619
|
+
const description = typeof option.description === "string" ? option.description.slice(0, 256) : undefined;
|
|
21620
|
+
return [{ value: value2, ...description ? { description } : {} }];
|
|
21621
|
+
}).slice(0, MODEL_EFFORT_MAX);
|
|
21622
|
+
const candidateDefault = typeof model.defaultReasoningEffort === "string" ? model.defaultReasoningEffort : undefined;
|
|
21623
|
+
seenModels.add(id);
|
|
21624
|
+
if (model.isDefault === true)
|
|
21625
|
+
defaultModelId = id;
|
|
21626
|
+
models.push({
|
|
21627
|
+
id,
|
|
21628
|
+
supportedReasoningEfforts,
|
|
21629
|
+
...candidateDefault && supportedReasoningEfforts.some((item) => item.value === candidateDefault) ? { defaultReasoningEffort: candidateDefault } : {}
|
|
21630
|
+
});
|
|
21631
|
+
};
|
|
21632
|
+
const onLine = (line) => {
|
|
21633
|
+
let message2;
|
|
21634
|
+
try {
|
|
21635
|
+
message2 = JSON.parse(line);
|
|
21636
|
+
} catch {
|
|
21637
|
+
return;
|
|
21638
|
+
}
|
|
21639
|
+
if (message2.id === initializeId) {
|
|
21640
|
+
if (message2.error)
|
|
21641
|
+
return finish();
|
|
21642
|
+
requestModelPage();
|
|
21643
|
+
return;
|
|
21644
|
+
}
|
|
21645
|
+
if (message2.id !== listId)
|
|
21646
|
+
return;
|
|
21647
|
+
if (message2.error || !message2.result || typeof message2.result !== "object")
|
|
21648
|
+
return finish();
|
|
21649
|
+
const result = message2.result;
|
|
21650
|
+
for (const model of Array.isArray(result.data) ? result.data : [])
|
|
21651
|
+
consumeModel(model);
|
|
21652
|
+
if (overflow)
|
|
21653
|
+
return finish();
|
|
21654
|
+
const cursor = typeof result.nextCursor === "string" ? result.nextCursor : undefined;
|
|
21655
|
+
if (cursor && models.length >= MODEL_LIST_MAX)
|
|
21656
|
+
return finish();
|
|
21657
|
+
if (cursor)
|
|
21658
|
+
return requestModelPage(cursor);
|
|
21659
|
+
if (models.length === 0)
|
|
21660
|
+
return finish();
|
|
21661
|
+
finish({
|
|
21662
|
+
updateMode: "live_next_turn",
|
|
21663
|
+
...defaultModelId ? { defaultModelId } : {},
|
|
21664
|
+
models
|
|
21665
|
+
});
|
|
21666
|
+
};
|
|
21667
|
+
const timer = setTimeout(() => finish(), MODEL_LIST_TIMEOUT_MS);
|
|
21668
|
+
timer.unref?.();
|
|
21669
|
+
proc.stdout?.on("data", (chunk2) => {
|
|
21670
|
+
const text2 = chunk2.toString();
|
|
21671
|
+
outputBytes += Buffer.byteLength(text2);
|
|
21672
|
+
if (outputBytes > MODEL_LIST_OUTPUT_MAX_BYTES)
|
|
21673
|
+
return finish();
|
|
21674
|
+
buffer += text2;
|
|
21675
|
+
const lines = buffer.split(`
|
|
21676
|
+
`);
|
|
21677
|
+
buffer = lines.pop() ?? "";
|
|
21678
|
+
for (const line of lines)
|
|
21679
|
+
if (line.trim())
|
|
21680
|
+
onLine(line);
|
|
21681
|
+
});
|
|
21682
|
+
proc.on("error", () => finish());
|
|
21683
|
+
proc.on("exit", () => finish());
|
|
21684
|
+
initializeId = ++nextId;
|
|
21685
|
+
proc.stdin?.write(jsonRpcRequest("initialize", { clientInfo: { name: "alook-agent-driver-probe", version: "0.1.24" }, capabilities: { experimentalApi: true } }, initializeId) + `
|
|
21686
|
+
`);
|
|
21687
|
+
});
|
|
21006
21688
|
}
|
|
21007
21689
|
async openLane(ctx, options) {
|
|
21008
21690
|
return createProcessLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
|
|
@@ -21018,6 +21700,9 @@ class CodexDriver {
|
|
|
21018
21700
|
shell: spec.shell
|
|
21019
21701
|
});
|
|
21020
21702
|
this.proc = proc;
|
|
21703
|
+
proc.once("exit", () => {
|
|
21704
|
+
this.failPendingSettingsUpdates("settings_process_exited", "Codex exited before acknowledging the settings update");
|
|
21705
|
+
});
|
|
21021
21706
|
const initialPrompt = ctx.prompt?.trim() ? ctx.prompt : null;
|
|
21022
21707
|
this.pendingInitialPrompt = initialPrompt;
|
|
21023
21708
|
queueMicrotask(() => {
|
|
@@ -21046,11 +21731,21 @@ class CodexDriver {
|
|
|
21046
21731
|
proc.stdin?.write(jsonRpcRequest("thread/start", freshParams, this.nextRequestId()) + `
|
|
21047
21732
|
`);
|
|
21048
21733
|
}
|
|
21734
|
+
this.requestAccountQuotaSnapshot();
|
|
21049
21735
|
});
|
|
21050
21736
|
return { process: proc };
|
|
21051
21737
|
}
|
|
21052
21738
|
normalizeLine(line) {
|
|
21739
|
+
const settingsResponse = this.consumeSettingsUpdateResponse(line);
|
|
21740
|
+
if (settingsResponse)
|
|
21741
|
+
return [];
|
|
21742
|
+
const parsed = tryParseJsonLine(line);
|
|
21053
21743
|
const events = this.eventNormalizer.normalizeLine(line);
|
|
21744
|
+
if (typeof parsed?.id === "number" && this.pendingAccountReadRequestIds.delete(parsed.id)) {
|
|
21745
|
+
this.requestQuotaSnapshot();
|
|
21746
|
+
}
|
|
21747
|
+
if (parsed?.method === "account/updated")
|
|
21748
|
+
this.requestAccountQuotaSnapshot();
|
|
21054
21749
|
if (this.pendingResumeFallbackParams && this.proc?.stdin && !this.proc.stdin.destroyed) {
|
|
21055
21750
|
const rolloutErr = events.find((e) => e.kind === "error" && isCodexMissingRolloutError(e.message));
|
|
21056
21751
|
if (rolloutErr) {
|
|
@@ -21074,6 +21769,90 @@ class CodexDriver {
|
|
|
21074
21769
|
}
|
|
21075
21770
|
return events;
|
|
21076
21771
|
}
|
|
21772
|
+
updateSettings(input) {
|
|
21773
|
+
const threadId = this.eventNormalizer.currentSessionId;
|
|
21774
|
+
const stdin = this.proc?.stdin;
|
|
21775
|
+
if (!threadId || !stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false) {
|
|
21776
|
+
return Promise.resolve({
|
|
21777
|
+
status: "failed",
|
|
21778
|
+
error: this.settingsError("process", "settings_thread_unavailable", "Codex thread is not available for a settings update", true)
|
|
21779
|
+
});
|
|
21780
|
+
}
|
|
21781
|
+
const id = this.nextRequestId();
|
|
21782
|
+
return new Promise((resolve2) => {
|
|
21783
|
+
const timer = setTimeout(() => {
|
|
21784
|
+
if (!this.pendingSettingsUpdates.delete(id))
|
|
21785
|
+
return;
|
|
21786
|
+
resolve2({
|
|
21787
|
+
status: "failed",
|
|
21788
|
+
error: this.settingsError("timeout", "settings_update_timeout", "Codex did not acknowledge the settings update before the deadline", true)
|
|
21789
|
+
});
|
|
21790
|
+
}, SETTINGS_UPDATE_TIMEOUT_MS);
|
|
21791
|
+
timer.unref?.();
|
|
21792
|
+
this.pendingSettingsUpdates.set(id, { resolve: resolve2, timer });
|
|
21793
|
+
try {
|
|
21794
|
+
stdin.write(jsonRpcRequest("thread/settings/update", { threadId, effort: input.reasoningEffort }, id) + `
|
|
21795
|
+
`);
|
|
21796
|
+
} catch (error51) {
|
|
21797
|
+
clearTimeout(timer);
|
|
21798
|
+
this.pendingSettingsUpdates.delete(id);
|
|
21799
|
+
resolve2({
|
|
21800
|
+
status: "failed",
|
|
21801
|
+
error: this.settingsError("process", "settings_update_write_failed", String(error51), true)
|
|
21802
|
+
});
|
|
21803
|
+
}
|
|
21804
|
+
});
|
|
21805
|
+
}
|
|
21806
|
+
consumeSettingsUpdateResponse(line) {
|
|
21807
|
+
let value;
|
|
21808
|
+
try {
|
|
21809
|
+
value = JSON.parse(line);
|
|
21810
|
+
} catch {
|
|
21811
|
+
return false;
|
|
21812
|
+
}
|
|
21813
|
+
if (!value || typeof value !== "object")
|
|
21814
|
+
return false;
|
|
21815
|
+
const record2 = value;
|
|
21816
|
+
if (typeof record2.id !== "number")
|
|
21817
|
+
return false;
|
|
21818
|
+
const pending = this.pendingSettingsUpdates.get(record2.id);
|
|
21819
|
+
if (!pending)
|
|
21820
|
+
return false;
|
|
21821
|
+
clearTimeout(pending.timer);
|
|
21822
|
+
this.pendingSettingsUpdates.delete(record2.id);
|
|
21823
|
+
const error51 = record2.error;
|
|
21824
|
+
if (!error51 || typeof error51 !== "object") {
|
|
21825
|
+
pending.resolve({ status: "applied" });
|
|
21826
|
+
return true;
|
|
21827
|
+
}
|
|
21828
|
+
const rpcError = error51;
|
|
21829
|
+
const message2 = typeof rpcError.message === "string" ? rpcError.message : "Codex rejected the settings update";
|
|
21830
|
+
if (rpcError.code === -32601 || /method\s+not\s+found/i.test(message2)) {
|
|
21831
|
+
pending.resolve({
|
|
21832
|
+
status: "unsupported",
|
|
21833
|
+
error: this.settingsError("protocol", "settings_update_unsupported", "Codex does not support live reasoning settings updates", false)
|
|
21834
|
+
});
|
|
21835
|
+
} else {
|
|
21836
|
+
pending.resolve({
|
|
21837
|
+
status: "failed",
|
|
21838
|
+
error: this.settingsError("protocol", "settings_update_rejected", message2, true)
|
|
21839
|
+
});
|
|
21840
|
+
}
|
|
21841
|
+
return true;
|
|
21842
|
+
}
|
|
21843
|
+
settingsError(category, code, message2, retryable) {
|
|
21844
|
+
return { category, code, message: scrubDriverErrorMessage(message2), retryable };
|
|
21845
|
+
}
|
|
21846
|
+
failPendingSettingsUpdates(code, message2) {
|
|
21847
|
+
for (const [id, pending] of this.pendingSettingsUpdates) {
|
|
21848
|
+
clearTimeout(pending.timer);
|
|
21849
|
+
this.pendingSettingsUpdates.delete(id);
|
|
21850
|
+
pending.resolve({
|
|
21851
|
+
status: "failed",
|
|
21852
|
+
error: this.settingsError("process", code, message2, true)
|
|
21853
|
+
});
|
|
21854
|
+
}
|
|
21855
|
+
}
|
|
21077
21856
|
get currentSessionId() {
|
|
21078
21857
|
return this.eventNormalizer.currentSessionId;
|
|
21079
21858
|
}
|
|
@@ -21095,9 +21874,189 @@ class CodexDriver {
|
|
|
21095
21874
|
|
|
21096
21875
|
// agent-driver/dist/adapters/cursor/acp-lane.js
|
|
21097
21876
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
21877
|
+
|
|
21878
|
+
// agent-driver/dist/adapters/cursor/catalog-probe.js
|
|
21098
21879
|
var ACP_PROTOCOL_VERSION = 1;
|
|
21099
|
-
var HANDSHAKE_TIMEOUT_MS = 15000;
|
|
21100
21880
|
var AUTH_METHOD_ID = "cursor_login";
|
|
21881
|
+
var CATALOG_PROBE_TIMEOUT_MS = 15000;
|
|
21882
|
+
var CATALOG_PROBE_OUTPUT_MAX_BYTES = 1024 * 1024;
|
|
21883
|
+
var MODEL_DISPLAY_NAME_MAX = 256;
|
|
21884
|
+
var MODEL_OPTION_NESTING_MAX = 16;
|
|
21885
|
+
function record2(value) {
|
|
21886
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
21887
|
+
}
|
|
21888
|
+
function normalizeDisplayName(value) {
|
|
21889
|
+
if (typeof value !== "string")
|
|
21890
|
+
return;
|
|
21891
|
+
const displayName = value.trim();
|
|
21892
|
+
return displayName && displayName.length <= MODEL_DISPLAY_NAME_MAX ? displayName : undefined;
|
|
21893
|
+
}
|
|
21894
|
+
function flattenCursorAcpSelectOptions(value, depth = 0) {
|
|
21895
|
+
if (!Array.isArray(value) || depth > MODEL_OPTION_NESTING_MAX)
|
|
21896
|
+
return [];
|
|
21897
|
+
const options = [];
|
|
21898
|
+
for (const item of value) {
|
|
21899
|
+
if (Array.isArray(item)) {
|
|
21900
|
+
options.push(...flattenCursorAcpSelectOptions(item, depth + 1));
|
|
21901
|
+
continue;
|
|
21902
|
+
}
|
|
21903
|
+
const candidate = record2(item);
|
|
21904
|
+
if (!candidate)
|
|
21905
|
+
continue;
|
|
21906
|
+
const exactValue = normalizeRuntimeModelId(candidate.value);
|
|
21907
|
+
if (exactValue) {
|
|
21908
|
+
const name = normalizeDisplayName(candidate.name);
|
|
21909
|
+
options.push({ value: exactValue, ...name ? { name } : {} });
|
|
21910
|
+
}
|
|
21911
|
+
if (Array.isArray(candidate.options)) {
|
|
21912
|
+
options.push(...flattenCursorAcpSelectOptions(candidate.options, depth + 1));
|
|
21913
|
+
}
|
|
21914
|
+
}
|
|
21915
|
+
return options;
|
|
21916
|
+
}
|
|
21917
|
+
function parseCursorAcpModelCatalog(session2) {
|
|
21918
|
+
const payload = record2(session2);
|
|
21919
|
+
const configOptions = Array.isArray(payload?.configOptions) ? payload.configOptions : [];
|
|
21920
|
+
const modelConfig = configOptions.map(record2).find((option) => option?.id === "model") ?? null;
|
|
21921
|
+
if (!modelConfig)
|
|
21922
|
+
return;
|
|
21923
|
+
const seen = new Set;
|
|
21924
|
+
const models = [];
|
|
21925
|
+
for (const option of flattenCursorAcpSelectOptions(modelConfig.options)) {
|
|
21926
|
+
if (option.value === "default[]" || seen.has(option.value))
|
|
21927
|
+
continue;
|
|
21928
|
+
if (models.length >= RUNTIME_MODEL_CATALOG_MAX)
|
|
21929
|
+
return;
|
|
21930
|
+
seen.add(option.value);
|
|
21931
|
+
models.push({
|
|
21932
|
+
id: option.value,
|
|
21933
|
+
...option.name ? { displayName: option.name } : {},
|
|
21934
|
+
supportedReasoningEfforts: []
|
|
21935
|
+
});
|
|
21936
|
+
}
|
|
21937
|
+
return models.length > 0 ? { updateMode: "unsupported", models } : undefined;
|
|
21938
|
+
}
|
|
21939
|
+
async function cleanupProbeProcess(process3) {
|
|
21940
|
+
if (process3.pid) {
|
|
21941
|
+
await killProcessTree(process3.pid, { graceMs: 250 }).catch(() => {});
|
|
21942
|
+
return;
|
|
21943
|
+
}
|
|
21944
|
+
if (process3.exitCode === null && process3.signalCode === null)
|
|
21945
|
+
process3.kill("SIGTERM");
|
|
21946
|
+
}
|
|
21947
|
+
async function probeCursorAcpCatalog(command, options = {}) {
|
|
21948
|
+
const cwd = options.cwd ?? process.cwd();
|
|
21949
|
+
const spec = resolveSpawnSpec("cursor-agent", ["acp"], command);
|
|
21950
|
+
let processHandle;
|
|
21951
|
+
try {
|
|
21952
|
+
processHandle = (options.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
|
|
21953
|
+
cwd,
|
|
21954
|
+
env: { ...process.env, CI: "1" },
|
|
21955
|
+
shell: spec.shell
|
|
21956
|
+
});
|
|
21957
|
+
} catch {
|
|
21958
|
+
return;
|
|
21959
|
+
}
|
|
21960
|
+
return new Promise((resolve2) => {
|
|
21961
|
+
let settled = false;
|
|
21962
|
+
let buffer = "";
|
|
21963
|
+
let outputBytes = 0;
|
|
21964
|
+
let requestId = 0;
|
|
21965
|
+
let expectedId = 0;
|
|
21966
|
+
let expectedMethod = "";
|
|
21967
|
+
const finish = (catalog) => {
|
|
21968
|
+
if (settled)
|
|
21969
|
+
return;
|
|
21970
|
+
settled = true;
|
|
21971
|
+
clearTimeout(timer);
|
|
21972
|
+
const cleanup = options.cleanup ?? cleanupProbeProcess;
|
|
21973
|
+
Promise.resolve().then(() => cleanup(processHandle)).catch(() => {}).finally(() => resolve2(catalog));
|
|
21974
|
+
};
|
|
21975
|
+
const request = (method, params) => {
|
|
21976
|
+
if (settled)
|
|
21977
|
+
return;
|
|
21978
|
+
const stdin = processHandle.stdin;
|
|
21979
|
+
if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false)
|
|
21980
|
+
return finish();
|
|
21981
|
+
expectedId = ++requestId;
|
|
21982
|
+
expectedMethod = method;
|
|
21983
|
+
try {
|
|
21984
|
+
stdin.write(`${jsonRpcRequest(method, params, expectedId)}
|
|
21985
|
+
`);
|
|
21986
|
+
} catch {
|
|
21987
|
+
finish();
|
|
21988
|
+
}
|
|
21989
|
+
};
|
|
21990
|
+
const onLine = (line) => {
|
|
21991
|
+
const parsed = tryParseJsonLine(line);
|
|
21992
|
+
const message2 = record2(parsed);
|
|
21993
|
+
if (!message2)
|
|
21994
|
+
return finish();
|
|
21995
|
+
if (message2.id !== expectedId)
|
|
21996
|
+
return;
|
|
21997
|
+
if (message2.error !== undefined)
|
|
21998
|
+
return finish();
|
|
21999
|
+
if (!Object.prototype.hasOwnProperty.call(message2, "result"))
|
|
22000
|
+
return finish();
|
|
22001
|
+
if (expectedMethod === "authenticate") {
|
|
22002
|
+
request("session/new", { cwd, mcpServers: [] });
|
|
22003
|
+
return;
|
|
22004
|
+
}
|
|
22005
|
+
const result = record2(message2.result);
|
|
22006
|
+
if (!result)
|
|
22007
|
+
return finish();
|
|
22008
|
+
if (expectedMethod === "initialize") {
|
|
22009
|
+
const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
|
|
22010
|
+
if (result.protocolVersion !== ACP_PROTOCOL_VERSION || !authMethods.some((method) => record2(method)?.id === AUTH_METHOD_ID))
|
|
22011
|
+
return finish();
|
|
22012
|
+
request("authenticate", { methodId: AUTH_METHOD_ID });
|
|
22013
|
+
return;
|
|
22014
|
+
}
|
|
22015
|
+
if (expectedMethod !== "session/new" || typeof result.sessionId !== "string" || !result.sessionId.trim())
|
|
22016
|
+
return finish();
|
|
22017
|
+
finish(parseCursorAcpModelCatalog(result));
|
|
22018
|
+
};
|
|
22019
|
+
const timer = setTimeout(() => finish(), options.timeoutMs ?? CATALOG_PROBE_TIMEOUT_MS);
|
|
22020
|
+
timer.unref?.();
|
|
22021
|
+
processHandle.stdout?.on("data", (chunk2) => {
|
|
22022
|
+
if (settled)
|
|
22023
|
+
return;
|
|
22024
|
+
const text2 = chunk2.toString();
|
|
22025
|
+
outputBytes += Buffer.byteLength(text2);
|
|
22026
|
+
if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
|
|
22027
|
+
return finish();
|
|
22028
|
+
buffer += text2;
|
|
22029
|
+
const lines = buffer.split(`
|
|
22030
|
+
`);
|
|
22031
|
+
buffer = lines.pop() ?? "";
|
|
22032
|
+
for (const line of lines)
|
|
22033
|
+
if (line.trim())
|
|
22034
|
+
onLine(line);
|
|
22035
|
+
});
|
|
22036
|
+
processHandle.stderr?.on("data", (chunk2) => {
|
|
22037
|
+
if (settled)
|
|
22038
|
+
return;
|
|
22039
|
+
outputBytes += Buffer.byteLength(chunk2.toString());
|
|
22040
|
+
if (outputBytes > (options.outputMaxBytes ?? CATALOG_PROBE_OUTPUT_MAX_BYTES))
|
|
22041
|
+
finish();
|
|
22042
|
+
});
|
|
22043
|
+
processHandle.on("error", () => finish());
|
|
22044
|
+
processHandle.on("exit", () => finish());
|
|
22045
|
+
request("initialize", {
|
|
22046
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
22047
|
+
clientCapabilities: {
|
|
22048
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
22049
|
+
terminal: false
|
|
22050
|
+
},
|
|
22051
|
+
clientInfo: { name: "alook-agent-driver-probe", version: "0.1.25" }
|
|
22052
|
+
});
|
|
22053
|
+
});
|
|
22054
|
+
}
|
|
22055
|
+
|
|
22056
|
+
// agent-driver/dist/adapters/cursor/acp-lane.js
|
|
22057
|
+
var ACP_PROTOCOL_VERSION2 = 1;
|
|
22058
|
+
var HANDSHAKE_TIMEOUT_MS = 15000;
|
|
22059
|
+
var AUTH_METHOD_ID2 = "cursor_login";
|
|
21101
22060
|
var PROMPT_STOP_REASONS = new Set([
|
|
21102
22061
|
"end_turn",
|
|
21103
22062
|
"max_tokens",
|
|
@@ -21121,16 +22080,16 @@ class CursorAcpRpcError extends Error {
|
|
|
21121
22080
|
this.code = code;
|
|
21122
22081
|
}
|
|
21123
22082
|
}
|
|
21124
|
-
function
|
|
22083
|
+
function record3(value) {
|
|
21125
22084
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
21126
22085
|
}
|
|
21127
22086
|
function safeLabel(value) {
|
|
21128
22087
|
return typeof value === "string" && /^[a-z0-9_-]{1,64}$/i.test(value) ? value : "unknown";
|
|
21129
22088
|
}
|
|
21130
22089
|
function rpcErrorMessage(error51) {
|
|
21131
|
-
const payload =
|
|
22090
|
+
const payload = record3(error51);
|
|
21132
22091
|
const message2 = typeof payload?.message === "string" && payload.message.trim() ? payload.message : "Cursor ACP request failed";
|
|
21133
|
-
const data =
|
|
22092
|
+
const data = record3(payload?.data);
|
|
21134
22093
|
const detail = typeof data?.message === "string" && data.message.trim() ? data.message : undefined;
|
|
21135
22094
|
return detail ? `${message2}: ${detail}` : message2;
|
|
21136
22095
|
}
|
|
@@ -21138,26 +22097,6 @@ function isMissingSessionError(error51) {
|
|
|
21138
22097
|
const message2 = error51 instanceof Error ? error51.message : String(error51);
|
|
21139
22098
|
return /\bsession\b.*\b(not found|missing|unknown|invalid)\b/i.test(message2) || /\b(not found|missing|unknown|invalid)\b.*\bsession\b/i.test(message2);
|
|
21140
22099
|
}
|
|
21141
|
-
function flattenSelectOptions(value) {
|
|
21142
|
-
if (!Array.isArray(value))
|
|
21143
|
-
return [];
|
|
21144
|
-
const out = [];
|
|
21145
|
-
for (const item of value) {
|
|
21146
|
-
if (Array.isArray(item)) {
|
|
21147
|
-
out.push(...flattenSelectOptions(item));
|
|
21148
|
-
continue;
|
|
21149
|
-
}
|
|
21150
|
-
const candidate = record2(item);
|
|
21151
|
-
if (!candidate)
|
|
21152
|
-
continue;
|
|
21153
|
-
if (typeof candidate.value === "string") {
|
|
21154
|
-
out.push({ value: candidate.value, ...typeof candidate.name === "string" ? { name: candidate.name } : {} });
|
|
21155
|
-
}
|
|
21156
|
-
if (Array.isArray(candidate.options))
|
|
21157
|
-
out.push(...flattenSelectOptions(candidate.options));
|
|
21158
|
-
}
|
|
21159
|
-
return out;
|
|
21160
|
-
}
|
|
21161
22100
|
|
|
21162
22101
|
class CursorAcpLane {
|
|
21163
22102
|
factory;
|
|
@@ -21281,30 +22220,30 @@ class CursorAcpLane {
|
|
|
21281
22220
|
}
|
|
21282
22221
|
}
|
|
21283
22222
|
async handshake(ctx) {
|
|
21284
|
-
const initialize =
|
|
21285
|
-
protocolVersion:
|
|
22223
|
+
const initialize = record3(await this.call("initialize", {
|
|
22224
|
+
protocolVersion: ACP_PROTOCOL_VERSION2,
|
|
21286
22225
|
clientCapabilities: {
|
|
21287
22226
|
fs: { readTextFile: false, writeTextFile: false },
|
|
21288
22227
|
terminal: false
|
|
21289
22228
|
},
|
|
21290
22229
|
clientInfo: { name: "alook-agent-driver", version: "0.1.14" }
|
|
21291
22230
|
}));
|
|
21292
|
-
if (initialize?.protocolVersion !==
|
|
22231
|
+
if (initialize?.protocolVersion !== ACP_PROTOCOL_VERSION2) {
|
|
21293
22232
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support protocol version 1");
|
|
21294
22233
|
}
|
|
21295
|
-
const capabilities =
|
|
22234
|
+
const capabilities = record3(initialize.agentCapabilities);
|
|
21296
22235
|
if (capabilities?.loadSession !== true) {
|
|
21297
22236
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support persistent session loading");
|
|
21298
22237
|
}
|
|
21299
22238
|
const authMethods = Array.isArray(initialize.authMethods) ? initialize.authMethods : [];
|
|
21300
|
-
if (!authMethods.some((method) =>
|
|
22239
|
+
if (!authMethods.some((method) => record3(method)?.id === AUTH_METHOD_ID2)) {
|
|
21301
22240
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not expose Cursor login authentication");
|
|
21302
22241
|
}
|
|
21303
|
-
await this.call("authenticate", { methodId:
|
|
22242
|
+
await this.call("authenticate", { methodId: AUTH_METHOD_ID2 });
|
|
21304
22243
|
let session2;
|
|
21305
22244
|
if (ctx.config.sessionId) {
|
|
21306
22245
|
try {
|
|
21307
|
-
session2 =
|
|
22246
|
+
session2 = record3(await this.call("session/load", {
|
|
21308
22247
|
sessionId: ctx.config.sessionId,
|
|
21309
22248
|
cwd: ctx.workingDirectory,
|
|
21310
22249
|
mcpServers: []
|
|
@@ -21316,15 +22255,25 @@ class CursorAcpLane {
|
|
|
21316
22255
|
throw error51;
|
|
21317
22256
|
}
|
|
21318
22257
|
} else {
|
|
21319
|
-
session2 =
|
|
22258
|
+
session2 = record3(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
|
|
21320
22259
|
}
|
|
21321
|
-
if (!session2
|
|
22260
|
+
if (!session2)
|
|
22261
|
+
throw new Error("Cursor ACP did not return a valid session response");
|
|
22262
|
+
const returnedSessionId = session2.sessionId;
|
|
22263
|
+
if (returnedSessionId !== undefined && (typeof returnedSessionId !== "string" || !returnedSessionId.trim())) {
|
|
21322
22264
|
throw new Error("Cursor ACP did not return a valid session id");
|
|
21323
22265
|
}
|
|
21324
|
-
if (ctx.config.sessionId
|
|
21325
|
-
|
|
22266
|
+
if (ctx.config.sessionId) {
|
|
22267
|
+
if (returnedSessionId !== undefined && returnedSessionId !== ctx.config.sessionId) {
|
|
22268
|
+
throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
|
|
22269
|
+
}
|
|
22270
|
+
this.sessionId = ctx.config.sessionId;
|
|
22271
|
+
} else {
|
|
22272
|
+
if (typeof returnedSessionId !== "string") {
|
|
22273
|
+
throw new Error("Cursor ACP did not return a valid session id");
|
|
22274
|
+
}
|
|
22275
|
+
this.sessionId = returnedSessionId;
|
|
21326
22276
|
}
|
|
21327
|
-
this.sessionId = session2.sessionId;
|
|
21328
22277
|
await this.configureModel(session2, ctx);
|
|
21329
22278
|
}
|
|
21330
22279
|
async configureModel(session2, ctx) {
|
|
@@ -21332,24 +22281,30 @@ class CursorAcpLane {
|
|
|
21332
22281
|
if (!requestedModel)
|
|
21333
22282
|
return;
|
|
21334
22283
|
const configOptions = Array.isArray(session2.configOptions) ? session2.configOptions : [];
|
|
21335
|
-
const modelConfig = configOptions.map(
|
|
22284
|
+
const modelConfig = configOptions.map(record3).find((option) => option?.id === "model") ?? null;
|
|
21336
22285
|
if (!modelConfig) {
|
|
21337
22286
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP does not support model configuration");
|
|
21338
22287
|
}
|
|
21339
|
-
const options =
|
|
21340
|
-
const match = options.find((option) => option.value === requestedModel)
|
|
22288
|
+
const options = flattenCursorAcpSelectOptions(modelConfig.options);
|
|
22289
|
+
const match = options.find((option) => option.value === requestedModel);
|
|
21341
22290
|
if (!match) {
|
|
21342
22291
|
throw new CursorAcpIncompatibleError(`Configured Cursor model is unavailable through ACP: ${requestedModel}`);
|
|
21343
22292
|
}
|
|
22293
|
+
let response;
|
|
21344
22294
|
try {
|
|
21345
|
-
await this.call("session/set_config_option", {
|
|
22295
|
+
response = record3(await this.call("session/set_config_option", {
|
|
21346
22296
|
sessionId: this.sessionId,
|
|
21347
22297
|
configId: "model",
|
|
21348
22298
|
value: match.value
|
|
21349
|
-
});
|
|
22299
|
+
}));
|
|
21350
22300
|
} catch {
|
|
21351
22301
|
throw new CursorAcpIncompatibleError("Installed Cursor ACP rejected model configuration");
|
|
21352
22302
|
}
|
|
22303
|
+
const confirmedOptions = Array.isArray(response?.configOptions) ? response.configOptions : [];
|
|
22304
|
+
const confirmedModel = confirmedOptions.map(record3).find((option) => option?.id === "model") ?? null;
|
|
22305
|
+
if (confirmedModel?.currentValue !== match.value) {
|
|
22306
|
+
throw new CursorAcpIncompatibleError("Cursor ACP did not confirm the exact configured model");
|
|
22307
|
+
}
|
|
21353
22308
|
}
|
|
21354
22309
|
admitPrompt(text2) {
|
|
21355
22310
|
if (!this.sessionId)
|
|
@@ -21385,22 +22340,13 @@ class CursorAcpLane {
|
|
|
21385
22340
|
completePrompt(active, value) {
|
|
21386
22341
|
if (this.activePrompt?.requestId !== active.requestId)
|
|
21387
22342
|
return;
|
|
21388
|
-
const result =
|
|
22343
|
+
const result = record3(value);
|
|
21389
22344
|
if (!result || typeof result.stopReason !== "string" || !PROMPT_STOP_REASONS.has(result.stopReason)) {
|
|
21390
22345
|
this.failPrompt(active, new Error("Cursor ACP prompt response did not contain a supported stopReason"));
|
|
21391
22346
|
return;
|
|
21392
22347
|
}
|
|
21393
22348
|
this.activePrompt = null;
|
|
21394
22349
|
this.openToolCalls.clear();
|
|
21395
|
-
const usage = record2(result.usage);
|
|
21396
|
-
if (usage) {
|
|
21397
|
-
this.events.emit("runtime_event", {
|
|
21398
|
-
kind: "telemetry",
|
|
21399
|
-
name: "token_usage",
|
|
21400
|
-
source: "cursor.acp",
|
|
21401
|
-
attrs: usage
|
|
21402
|
-
});
|
|
21403
|
-
}
|
|
21404
22350
|
this.events.emit("runtime_event", {
|
|
21405
22351
|
kind: "turn_end",
|
|
21406
22352
|
sessionId: this.sessionId ?? undefined,
|
|
@@ -21536,7 +22482,7 @@ class CursorAcpLane {
|
|
|
21536
22482
|
});
|
|
21537
22483
|
}
|
|
21538
22484
|
handleMessage(value) {
|
|
21539
|
-
const message2 =
|
|
22485
|
+
const message2 = record3(value);
|
|
21540
22486
|
if (!message2 || message2.jsonrpc !== "2.0") {
|
|
21541
22487
|
this.protocolFailure("Cursor ACP emitted an invalid JSON-RPC message");
|
|
21542
22488
|
return;
|
|
@@ -21564,7 +22510,7 @@ class CursorAcpLane {
|
|
|
21564
22510
|
if (pending.kind === "prompt") {
|
|
21565
22511
|
this.pending.delete(id);
|
|
21566
22512
|
if (message2.error !== undefined) {
|
|
21567
|
-
const payload =
|
|
22513
|
+
const payload = record3(message2.error);
|
|
21568
22514
|
this.failPrompt(pending.active, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message2.error)));
|
|
21569
22515
|
} else if (!("result" in message2)) {
|
|
21570
22516
|
this.failPrompt(pending.active, new Error("Cursor ACP response omitted result"));
|
|
@@ -21574,7 +22520,7 @@ class CursorAcpLane {
|
|
|
21574
22520
|
return;
|
|
21575
22521
|
}
|
|
21576
22522
|
if (message2.error !== undefined) {
|
|
21577
|
-
const payload =
|
|
22523
|
+
const payload = record3(message2.error);
|
|
21578
22524
|
this.settleRequest(id, false, new CursorAcpRpcError(pending.method, typeof payload?.code === "number" ? payload.code : undefined, rpcErrorMessage(message2.error)));
|
|
21579
22525
|
return;
|
|
21580
22526
|
}
|
|
@@ -21606,9 +22552,9 @@ class CursorAcpLane {
|
|
|
21606
22552
|
this.diagnostic("warning", `Unsupported Cursor ACP client request: ${safeLabel(method)}`);
|
|
21607
22553
|
return;
|
|
21608
22554
|
}
|
|
21609
|
-
const payload =
|
|
22555
|
+
const payload = record3(params);
|
|
21610
22556
|
const sameSession = payload?.sessionId === this.sessionId;
|
|
21611
|
-
const options = Array.isArray(payload?.options) ? payload.options.map(
|
|
22557
|
+
const options = Array.isArray(payload?.options) ? payload.options.map(record3).filter(Boolean) : [];
|
|
21612
22558
|
const allowOnce = options.find((option) => option.kind === "allow_once" && typeof option.optionId === "string" && option.optionId.trim().length > 0);
|
|
21613
22559
|
if (!this.ready || !this.activePrompt || !sameSession || !allowOnce) {
|
|
21614
22560
|
this.write({ jsonrpc: "2.0", id, result: { outcome: { outcome: "cancelled" } } });
|
|
@@ -21629,7 +22575,7 @@ class CursorAcpLane {
|
|
|
21629
22575
|
this.diagnostic("warning", `Unsupported Cursor ACP notification: ${safeLabel(method)}`);
|
|
21630
22576
|
}
|
|
21631
22577
|
handleSessionUpdate(params) {
|
|
21632
|
-
const payload =
|
|
22578
|
+
const payload = record3(params);
|
|
21633
22579
|
if (!payload || payload.sessionId !== this.sessionId) {
|
|
21634
22580
|
this.diagnostic("warning", "Cursor ACP emitted an update for a different session");
|
|
21635
22581
|
return;
|
|
@@ -21638,18 +22584,18 @@ class CursorAcpLane {
|
|
|
21638
22584
|
this.diagnostic("warning", "Cursor ACP emitted a session update without an active prompt");
|
|
21639
22585
|
return;
|
|
21640
22586
|
}
|
|
21641
|
-
const update =
|
|
22587
|
+
const update = record3(payload.update) ?? {};
|
|
21642
22588
|
const updateType = update?.sessionUpdate;
|
|
21643
22589
|
switch (updateType) {
|
|
21644
22590
|
case "agent_message_chunk": {
|
|
21645
|
-
const content =
|
|
22591
|
+
const content = record3(update.content);
|
|
21646
22592
|
if (content?.type === "text" && typeof content.text === "string") {
|
|
21647
22593
|
this.events.emit("runtime_event", { kind: "assistant_message_delta", text: content.text });
|
|
21648
22594
|
}
|
|
21649
22595
|
return;
|
|
21650
22596
|
}
|
|
21651
22597
|
case "agent_thought_chunk": {
|
|
21652
|
-
const content =
|
|
22598
|
+
const content = record3(update.content);
|
|
21653
22599
|
if (content?.type === "text" && typeof content.text === "string") {
|
|
21654
22600
|
this.events.emit("runtime_event", { kind: "assistant_reasoning_delta", text: content.text });
|
|
21655
22601
|
}
|
|
@@ -21721,6 +22667,7 @@ class CursorAcpLane {
|
|
|
21721
22667
|
|
|
21722
22668
|
// agent-driver/dist/adapters/cursor/index.js
|
|
21723
22669
|
class CursorDriver {
|
|
22670
|
+
catalogProbe;
|
|
21724
22671
|
id = "cursor";
|
|
21725
22672
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
21726
22673
|
execution = {
|
|
@@ -21729,8 +22676,23 @@ class CursorDriver {
|
|
|
21729
22676
|
wakeStart: "immediate",
|
|
21730
22677
|
terminalOwnership: "transport_request"
|
|
21731
22678
|
};
|
|
21732
|
-
|
|
21733
|
-
|
|
22679
|
+
constructor(catalogProbe = probeCursorAcpCatalog) {
|
|
22680
|
+
this.catalogProbe = catalogProbe;
|
|
22681
|
+
}
|
|
22682
|
+
async probe(command) {
|
|
22683
|
+
const result = probeCliRuntime("cursor-agent", {}, command);
|
|
22684
|
+
if (result.status !== "healthy")
|
|
22685
|
+
return result;
|
|
22686
|
+
let reasoning;
|
|
22687
|
+
try {
|
|
22688
|
+
reasoning = await this.catalogProbe(command);
|
|
22689
|
+
} catch {
|
|
22690
|
+
reasoning = undefined;
|
|
22691
|
+
}
|
|
22692
|
+
return {
|
|
22693
|
+
...result,
|
|
22694
|
+
reasoning
|
|
22695
|
+
};
|
|
21734
22696
|
}
|
|
21735
22697
|
async openLane(ctx, options) {
|
|
21736
22698
|
return new CursorAcpLane(this, ctx, { onRawStdoutLine: options?.onRawStdoutLine });
|
|
@@ -21750,10 +22712,10 @@ class CursorDriver {
|
|
|
21750
22712
|
}
|
|
21751
22713
|
|
|
21752
22714
|
// agent-driver/dist/adapters/opencode/index.js
|
|
21753
|
-
import { randomBytes as
|
|
22715
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
21754
22716
|
|
|
21755
22717
|
// agent-driver/dist/adapters/opencode/service-lane.js
|
|
21756
|
-
import { randomBytes } from "node:crypto";
|
|
22718
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
21757
22719
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
21758
22720
|
import { createServer as createServer2 } from "node:net";
|
|
21759
22721
|
var SUPPORTED_VERSION = "1.17.20";
|
|
@@ -21787,7 +22749,7 @@ class OpenCodeHttpError extends Error {
|
|
|
21787
22749
|
this.status = status;
|
|
21788
22750
|
}
|
|
21789
22751
|
}
|
|
21790
|
-
function
|
|
22752
|
+
function record4(value) {
|
|
21791
22753
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
21792
22754
|
}
|
|
21793
22755
|
function safeLabel2(value) {
|
|
@@ -21834,7 +22796,7 @@ function parseModelRef(model) {
|
|
|
21834
22796
|
return { providerID: model.slice(0, slash), id: model.slice(slash + 1) };
|
|
21835
22797
|
}
|
|
21836
22798
|
function messageFromError(value) {
|
|
21837
|
-
const payload =
|
|
22799
|
+
const payload = record4(value);
|
|
21838
22800
|
const message2 = typeof payload?.message === "string" && payload.message.trim() ? payload.message : undefined;
|
|
21839
22801
|
return message2 ? "OpenCode turn failed" : "OpenCode reported an inconsistent turn outcome";
|
|
21840
22802
|
}
|
|
@@ -21892,7 +22854,7 @@ class OpenCodeServiceLane {
|
|
|
21892
22854
|
this.ctx = ctx;
|
|
21893
22855
|
this.options = options;
|
|
21894
22856
|
this.fetchFn = options.fetch ?? fetch;
|
|
21895
|
-
this.password = options.password ??
|
|
22857
|
+
this.password = options.password ?? randomBytes2(32).toString("base64url");
|
|
21896
22858
|
}
|
|
21897
22859
|
get currentSessionId() {
|
|
21898
22860
|
return this.sessionId;
|
|
@@ -22168,7 +23130,7 @@ class OpenCodeServiceLane {
|
|
|
22168
23130
|
const healthTimeoutMs = Math.max(1, Math.min(1000, deadline - Date.now(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS));
|
|
22169
23131
|
const { response, body } = await this.fetchJsonWithTimeout("/global/health", { method: "GET" }, "health", healthTimeoutMs);
|
|
22170
23132
|
if (response.ok) {
|
|
22171
|
-
const health =
|
|
23133
|
+
const health = record4(body);
|
|
22172
23134
|
if (health?.healthy !== true || health.version !== SUPPORTED_VERSION) {
|
|
22173
23135
|
throw new OpenCodeIncompatibleError(`Installed OpenCode service must be version ${SUPPORTED_VERSION}`);
|
|
22174
23136
|
}
|
|
@@ -22189,8 +23151,8 @@ class OpenCodeServiceLane {
|
|
|
22189
23151
|
const { response, body } = await this.fetchJsonWithTimeout("/doc", { method: "GET" }, "OpenAPI");
|
|
22190
23152
|
if (!response.ok)
|
|
22191
23153
|
throw new OpenCodeIncompatibleError("Installed OpenCode service does not expose its OpenAPI document");
|
|
22192
|
-
const document =
|
|
22193
|
-
const paths =
|
|
23154
|
+
const document = record4(body);
|
|
23155
|
+
const paths = record4(document?.paths);
|
|
22194
23156
|
const required2 = [
|
|
22195
23157
|
"/api/session",
|
|
22196
23158
|
"/api/session/active",
|
|
@@ -22203,7 +23165,7 @@ class OpenCodeServiceLane {
|
|
|
22203
23165
|
"/api/session/{sessionID}/permission/{requestID}/reply",
|
|
22204
23166
|
"/api/event"
|
|
22205
23167
|
];
|
|
22206
|
-
if (!paths || required2.some((path7) => !
|
|
23168
|
+
if (!paths || required2.some((path7) => !record4(paths[path7]))) {
|
|
22207
23169
|
throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
|
|
22208
23170
|
}
|
|
22209
23171
|
}
|
|
@@ -22216,7 +23178,7 @@ class OpenCodeServiceLane {
|
|
|
22216
23178
|
}
|
|
22217
23179
|
if (!response2.ok)
|
|
22218
23180
|
throw new OpenCodeHttpError(response2.status, "session resume");
|
|
22219
|
-
const session3 =
|
|
23181
|
+
const session3 = record4(record4(body2)?.data);
|
|
22220
23182
|
if (session3?.id !== resumeId) {
|
|
22221
23183
|
throw new OpenCodeResetRequiredError("OpenCode v2 returned a different resumed session; reset this agent before continuing");
|
|
22222
23184
|
}
|
|
@@ -22237,8 +23199,8 @@ class OpenCodeServiceLane {
|
|
|
22237
23199
|
}, "session create");
|
|
22238
23200
|
if (!response.ok)
|
|
22239
23201
|
throw new OpenCodeHttpError(response.status, "session create");
|
|
22240
|
-
const payload =
|
|
22241
|
-
const session2 =
|
|
23202
|
+
const payload = record4(responseBody);
|
|
23203
|
+
const session2 = record4(payload?.data);
|
|
22242
23204
|
if (typeof session2?.id !== "string" || !/^ses/.test(session2.id)) {
|
|
22243
23205
|
throw new Error("OpenCode v2 did not return a valid session id");
|
|
22244
23206
|
}
|
|
@@ -22395,7 +23357,7 @@ class OpenCodeServiceLane {
|
|
|
22395
23357
|
const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/history?after=${historyCursor}&limit=${HISTORY_PAGE_LIMIT}`, { method: "GET" }, "session history");
|
|
22396
23358
|
if (!response.ok)
|
|
22397
23359
|
throw new OpenCodeHttpError(response.status, "session history");
|
|
22398
|
-
const body =
|
|
23360
|
+
const body = record4(responseBody);
|
|
22399
23361
|
if (!Array.isArray(body?.data) || typeof body.hasMore !== "boolean") {
|
|
22400
23362
|
throw new OpenCodeProtocolError("OpenCode session history returned an invalid page");
|
|
22401
23363
|
}
|
|
@@ -22413,9 +23375,9 @@ class OpenCodeServiceLane {
|
|
|
22413
23375
|
return run;
|
|
22414
23376
|
}
|
|
22415
23377
|
async handleDurableEvent(value, project) {
|
|
22416
|
-
const event =
|
|
22417
|
-
const durable =
|
|
22418
|
-
const data =
|
|
23378
|
+
const event = record4(value);
|
|
23379
|
+
const durable = record4(event?.durable);
|
|
23380
|
+
const data = record4(event?.data);
|
|
22419
23381
|
if (!event || typeof event.id !== "string" || typeof event.type !== "string" || !durable || durable.aggregateID !== this.sessionId || !Number.isInteger(durable.seq) || Number(durable.seq) < 0 || data?.sessionID !== this.sessionId) {
|
|
22420
23382
|
throw new OpenCodeProtocolError("OpenCode session stream emitted an invalid durable event");
|
|
22421
23383
|
}
|
|
@@ -22505,13 +23467,21 @@ class OpenCodeServiceLane {
|
|
|
22505
23467
|
...!successful ? { message: "OpenCode reported an unsupported final step outcome" } : {}
|
|
22506
23468
|
});
|
|
22507
23469
|
}
|
|
22508
|
-
const tokens =
|
|
22509
|
-
if (tokens) {
|
|
23470
|
+
const tokens = record4(data.tokens);
|
|
23471
|
+
if (tokens && data.finish !== "tool-calls") {
|
|
23472
|
+
const cache = record4(tokens.cache);
|
|
23473
|
+
const metric2 = (value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0 ? value2 : null;
|
|
23474
|
+
const cacheParts = [cache?.read, cache?.write].filter((value2) => typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0);
|
|
23475
|
+
const cacheTotal = cacheParts.reduce((sum, value2) => sum + value2, 0);
|
|
22510
23476
|
this.events.emit("runtime_event", {
|
|
22511
23477
|
kind: "telemetry",
|
|
22512
23478
|
name: "token_usage",
|
|
22513
23479
|
source: "opencode.v2",
|
|
22514
|
-
|
|
23480
|
+
usage: {
|
|
23481
|
+
input: metric2(tokens.input),
|
|
23482
|
+
output: metric2(tokens.output),
|
|
23483
|
+
cache: cacheParts.length > 0 && Number.isSafeInteger(cacheTotal) ? cacheTotal : null
|
|
23484
|
+
}
|
|
22515
23485
|
});
|
|
22516
23486
|
}
|
|
22517
23487
|
break;
|
|
@@ -22526,8 +23496,8 @@ class OpenCodeServiceLane {
|
|
|
22526
23496
|
return seq;
|
|
22527
23497
|
}
|
|
22528
23498
|
async handleLiveEvent(value) {
|
|
22529
|
-
const event =
|
|
22530
|
-
const data =
|
|
23499
|
+
const event = record4(value);
|
|
23500
|
+
const data = record4(event?.data);
|
|
22531
23501
|
if (event?.type !== "permission.v2.asked" || data?.sessionID !== this.sessionId)
|
|
22532
23502
|
return;
|
|
22533
23503
|
if (typeof data.id !== "string" || !/^per/.test(data.id)) {
|
|
@@ -22541,11 +23511,11 @@ class OpenCodeServiceLane {
|
|
|
22541
23511
|
const { response, body: responseBody } = await this.fetchJsonWithTimeout(`/api/session/${encodeURIComponent(this.sessionId)}/permission`, { method: "GET" }, "permission list");
|
|
22542
23512
|
if (!response.ok)
|
|
22543
23513
|
throw new OpenCodeHttpError(response.status, "permission list");
|
|
22544
|
-
const body =
|
|
23514
|
+
const body = record4(responseBody);
|
|
22545
23515
|
if (!Array.isArray(body?.data))
|
|
22546
23516
|
throw new OpenCodeProtocolError("OpenCode permission list returned invalid data");
|
|
22547
23517
|
for (const item of body.data) {
|
|
22548
|
-
const permission =
|
|
23518
|
+
const permission = record4(item);
|
|
22549
23519
|
if (permission?.sessionID === this.sessionId && typeof permission.id === "string") {
|
|
22550
23520
|
await this.replyPermission(permission.id);
|
|
22551
23521
|
}
|
|
@@ -22612,8 +23582,8 @@ class OpenCodeServiceLane {
|
|
|
22612
23582
|
}, "prompt admission");
|
|
22613
23583
|
if (!response.ok)
|
|
22614
23584
|
throw new OpenCodeHttpError(response.status, "prompt admission");
|
|
22615
|
-
const body =
|
|
22616
|
-
const admitted =
|
|
23585
|
+
const body = record4(responseBody);
|
|
23586
|
+
const admitted = record4(body?.data);
|
|
22617
23587
|
if (admitted?.id !== messageId || admitted.sessionID !== this.sessionId || admitted.delivery !== delivery || !Number.isInteger(admitted.admittedSeq) || Number(admitted.admittedSeq) < 0) {
|
|
22618
23588
|
throw new OpenCodeProtocolError("OpenCode prompt admission returned an invalid receipt");
|
|
22619
23589
|
}
|
|
@@ -22680,8 +23650,8 @@ class OpenCodeServiceLane {
|
|
|
22680
23650
|
const { response, body: responseBody } = await this.fetchJsonWithTimeout("/api/session/active", { method: "GET" }, "active session query");
|
|
22681
23651
|
if (!response.ok)
|
|
22682
23652
|
throw new OpenCodeHttpError(response.status, "active session query");
|
|
22683
|
-
const body =
|
|
22684
|
-
const active =
|
|
23653
|
+
const body = record4(responseBody);
|
|
23654
|
+
const active = record4(body?.data);
|
|
22685
23655
|
if (!active)
|
|
22686
23656
|
throw new OpenCodeProtocolError("OpenCode active session query returned invalid data");
|
|
22687
23657
|
if (!this.barrierStillCurrent(root, identity, generation))
|
|
@@ -22817,7 +23787,7 @@ class OpenCodeServiceLane {
|
|
|
22817
23787
|
return headers;
|
|
22818
23788
|
}
|
|
22819
23789
|
newMessageId() {
|
|
22820
|
-
return `msg_${
|
|
23790
|
+
return `msg_${randomBytes2(16).toString("hex")}`;
|
|
22821
23791
|
}
|
|
22822
23792
|
diagnostic(severity, message2) {
|
|
22823
23793
|
this.events.emit("runtime_event", {
|
|
@@ -22882,10 +23852,11 @@ class OpenCodeServiceLane {
|
|
|
22882
23852
|
|
|
22883
23853
|
// agent-driver/dist/adapters/opencode/index.js
|
|
22884
23854
|
function createOpenCodeMessageId() {
|
|
22885
|
-
return `msg_${
|
|
23855
|
+
return `msg_${randomBytes3(16).toString("hex")}`;
|
|
22886
23856
|
}
|
|
22887
23857
|
|
|
22888
23858
|
class OpenCodeDriver {
|
|
23859
|
+
outputProbe;
|
|
22889
23860
|
id = "opencode";
|
|
22890
23861
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
22891
23862
|
execution = {
|
|
@@ -22894,8 +23865,19 @@ class OpenCodeDriver {
|
|
|
22894
23865
|
wakeStart: "immediate",
|
|
22895
23866
|
terminalOwnership: "transport_request"
|
|
22896
23867
|
};
|
|
23868
|
+
constructor(outputProbe = probeCommandOutput) {
|
|
23869
|
+
this.outputProbe = outputProbe;
|
|
23870
|
+
}
|
|
22897
23871
|
probe(command) {
|
|
22898
|
-
|
|
23872
|
+
const result = probeCliRuntime("opencode", {}, command);
|
|
23873
|
+
if (result.status !== "healthy")
|
|
23874
|
+
return result;
|
|
23875
|
+
const spec = resolveSpawnSpec("opencode", ["models", "--pure"], command);
|
|
23876
|
+
const output = this.outputProbe(spec.command, spec.args);
|
|
23877
|
+
return {
|
|
23878
|
+
...result,
|
|
23879
|
+
reasoning: output.ok ? parseOpenCodeModelCatalog(output.output) : undefined
|
|
23880
|
+
};
|
|
22899
23881
|
}
|
|
22900
23882
|
beginTurn() {
|
|
22901
23883
|
return createOpenCodeMessageId();
|
|
@@ -23149,6 +24131,7 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
|
|
|
23149
24131
|
|
|
23150
24132
|
// agent-driver/dist/adapters/pi/index.js
|
|
23151
24133
|
var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
|
|
24134
|
+
var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
|
|
23152
24135
|
function isPiSdkPackageJson(pkgJsonPath) {
|
|
23153
24136
|
if (!existsSync3(pkgJsonPath))
|
|
23154
24137
|
return false;
|
|
@@ -23257,6 +24240,8 @@ function mapPiSdkEvent(event, sessionId, state) {
|
|
|
23257
24240
|
|
|
23258
24241
|
class PiDriver {
|
|
23259
24242
|
dependenciesFor;
|
|
24243
|
+
loadSdk;
|
|
24244
|
+
readVersion;
|
|
23260
24245
|
id = "pi";
|
|
23261
24246
|
instructionDelivery = { kind: "workspace_file", canonical: "AGENTS.md", aliases: ["CLAUDE.md"] };
|
|
23262
24247
|
execution = {
|
|
@@ -23267,15 +24252,36 @@ class PiDriver {
|
|
|
23267
24252
|
};
|
|
23268
24253
|
sessionId = null;
|
|
23269
24254
|
terminalSequence = 0;
|
|
23270
|
-
constructor(dependenciesFor = createPiSessionDependencies) {
|
|
24255
|
+
constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
|
|
23271
24256
|
this.dependenciesFor = dependenciesFor;
|
|
24257
|
+
this.loadSdk = loadSdk;
|
|
24258
|
+
this.readVersion = readVersion;
|
|
23272
24259
|
}
|
|
23273
|
-
probe() {
|
|
23274
|
-
const version3 =
|
|
24260
|
+
async probe() {
|
|
24261
|
+
const version3 = this.readVersion();
|
|
23275
24262
|
if (!version3) {
|
|
23276
24263
|
return { status: "unhealthy", lastError: "sdk_not_installed" };
|
|
23277
24264
|
}
|
|
23278
|
-
|
|
24265
|
+
let timer;
|
|
24266
|
+
try {
|
|
24267
|
+
const reasoning = await Promise.race([
|
|
24268
|
+
this.loadSdk().then(async (sdk) => {
|
|
24269
|
+
const authStorage = sdk.AuthStorage.create();
|
|
24270
|
+
const registry2 = sdk.ModelRegistry.create(authStorage);
|
|
24271
|
+
return parsePiModelCatalog(await registry2.getAvailable());
|
|
24272
|
+
}),
|
|
24273
|
+
new Promise((resolve3) => {
|
|
24274
|
+
timer = setTimeout(() => resolve3(undefined), PI_MODEL_PROBE_TIMEOUT_MS);
|
|
24275
|
+
timer.unref?.();
|
|
24276
|
+
})
|
|
24277
|
+
]);
|
|
24278
|
+
return { status: "healthy", version: version3, reasoning };
|
|
24279
|
+
} catch {
|
|
24280
|
+
return { status: "healthy", version: version3, reasoning: undefined };
|
|
24281
|
+
} finally {
|
|
24282
|
+
if (timer)
|
|
24283
|
+
clearTimeout(timer);
|
|
24284
|
+
}
|
|
23279
24285
|
}
|
|
23280
24286
|
async openLane(ctx) {
|
|
23281
24287
|
const deps = this.dependenciesFor(ctx);
|
|
@@ -23660,48 +24666,6 @@ function assertInstructionFileName(name) {
|
|
|
23660
24666
|
}
|
|
23661
24667
|
}
|
|
23662
24668
|
|
|
23663
|
-
// agent-driver/dist/internal/errors.js
|
|
23664
|
-
var MAX_PUBLIC_ERROR_MESSAGE = 1000;
|
|
23665
|
-
var CREDENTIAL_NAME = String.raw`(?:[A-Za-z0-9]{1,32}[_-]){0,4}(?:api[_-]?key|access[_-]?key|secret(?:[_-]?access[_-]?key)?|client[_-]?secret|access[_-]?token|auth(?:orization)?|password|passwd|token|voucher)(?:[_-][A-Za-z0-9]{1,32}){0,4}`;
|
|
23666
|
-
var QUOTED_CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(["'])(${CREDENTIAL_NAME})\1\s*[:=]\s*(["'])[^"'\r\n]*\3`, "gi");
|
|
23667
|
-
var CREDENTIAL_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9_-])((?:${CREDENTIAL_NAME})\s*[:=]\s*)(?!\[redacted\])[^\s,;}\]]+`, "gi");
|
|
23668
|
-
function scrubDriverErrorMessage(value, fallback = "Runtime operation failed") {
|
|
23669
|
-
const text2 = value instanceof Error ? value.message : String(value ?? "");
|
|
23670
|
-
const scrubbed = text2.replace(/\b(?:cmk|cmt|crk)_[A-Za-z0-9_-]+\b/g, "[redacted-token]").replace(/(Authorization\s*:\s*)(?:Bearer|Basic)\s+[^\s,;]+/gi, "$1[redacted]").replace(/Bearer\s+[A-Za-z0-9._\-]+/gi, "Bearer [redacted]").replace(/\b(?:sk|sk-ant|sk-proj|xox[abprs])-[A-Za-z0-9._\-]+/gi, "[redacted-token]").replace(QUOTED_CREDENTIAL_ASSIGNMENT, "$1$2$1:$3[redacted]$3").replace(CREDENTIAL_ASSIGNMENT, "$1[redacted]").replace(/(?<![A-Za-z0-9._%+\-])[A-Za-z0-9._%+\-]{1,320}@[A-Za-z0-9.\-]{1,255}\.[A-Za-z]{2,63}/g, "[redacted-email]").replace(/([?&])([^=\s]+)=([^&\s]+)/g, "$1$2=[redacted]").replace(/\/(?:Users|home)\/[^\r\n,;]+/g, "[redacted-path]").replace(/[A-Za-z]:\\Users\\[^\r\n,;]+/gi, "[redacted-path]").replace(/\\\\[^\\\s]+\\[^\r\n,;]+/g, "[redacted-path]").replace(/(?:[A-Za-z]:\\|\/)(?:[^\s/:]+[\\/]){1,}[^\s:]*/g, "[redacted-path]").trim();
|
|
23671
|
-
return (scrubbed || fallback).slice(0, MAX_PUBLIC_ERROR_MESSAGE);
|
|
23672
|
-
}
|
|
23673
|
-
function scrubDriverError(error51) {
|
|
23674
|
-
return {
|
|
23675
|
-
...error51,
|
|
23676
|
-
code: stableErrorCode(error51.code, "runtime_error"),
|
|
23677
|
-
message: scrubDriverErrorMessage(error51.message),
|
|
23678
|
-
...error51.details ? { details: scrubDetails(error51.details) } : {}
|
|
23679
|
-
};
|
|
23680
|
-
}
|
|
23681
|
-
function scrubDetails(details) {
|
|
23682
|
-
const scrubValue = (value, key) => {
|
|
23683
|
-
if (key && /api[_-]?key|access[_-]?token|auth(?:orization)?|password|passwd|secret|token/i.test(key)) {
|
|
23684
|
-
return "[redacted]";
|
|
23685
|
-
}
|
|
23686
|
-
if (typeof value === "string")
|
|
23687
|
-
return scrubDriverErrorMessage(value, "[redacted]");
|
|
23688
|
-
if (Array.isArray(value))
|
|
23689
|
-
return value.map((item) => scrubValue(item));
|
|
23690
|
-
if (value && typeof value === "object") {
|
|
23691
|
-
return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
|
|
23692
|
-
childKey,
|
|
23693
|
-
scrubValue(child, childKey)
|
|
23694
|
-
]));
|
|
23695
|
-
}
|
|
23696
|
-
return value;
|
|
23697
|
-
};
|
|
23698
|
-
return scrubValue(details);
|
|
23699
|
-
}
|
|
23700
|
-
function stableErrorCode(value, fallback) {
|
|
23701
|
-
const code = String(value ?? "");
|
|
23702
|
-
return /^[A-Za-z0-9_.-]{1,100}$/.test(code) ? code : fallback;
|
|
23703
|
-
}
|
|
23704
|
-
|
|
23705
24669
|
// agent-driver/dist/controller/logical-session.js
|
|
23706
24670
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
23707
24671
|
var SEMANTIC_ASSEMBLER_MAX_BYTES = 1048576;
|
|
@@ -23819,6 +24783,8 @@ class LogicalAgentSession {
|
|
|
23819
24783
|
toolBoundaryFlushDisabled = false;
|
|
23820
24784
|
safeBoundaryFlush;
|
|
23821
24785
|
safeBoundaryDelivery;
|
|
24786
|
+
settingsUpdateTail = Promise.resolve();
|
|
24787
|
+
settingsUpdatePending = false;
|
|
23822
24788
|
turnAdmission;
|
|
23823
24789
|
instructionsMaterialized = false;
|
|
23824
24790
|
lifecycleGeneration = 0;
|
|
@@ -23874,6 +24840,35 @@ class LogicalAgentSession {
|
|
|
23874
24840
|
send(message2) {
|
|
23875
24841
|
return this.admit("send", message2);
|
|
23876
24842
|
}
|
|
24843
|
+
updateSettings(input) {
|
|
24844
|
+
if (this.state === "closed" || this.state === "stopping" || this.finishing) {
|
|
24845
|
+
return Promise.resolve({
|
|
24846
|
+
status: "failed",
|
|
24847
|
+
error: driverError("process", "settings_session_closed", "Runtime session is closed", true)
|
|
24848
|
+
});
|
|
24849
|
+
}
|
|
24850
|
+
this.settingsUpdatePending = true;
|
|
24851
|
+
const operation = this.settingsUpdateTail.then(async () => {
|
|
24852
|
+
if (!this.lane?.updateSettings)
|
|
24853
|
+
return { status: "unsupported" };
|
|
24854
|
+
try {
|
|
24855
|
+
return await this.lane.updateSettings(input);
|
|
24856
|
+
} catch (error51) {
|
|
24857
|
+
return {
|
|
24858
|
+
status: "failed",
|
|
24859
|
+
error: driverError("process", "settings_update_failed", String(error51), true)
|
|
24860
|
+
};
|
|
24861
|
+
}
|
|
24862
|
+
});
|
|
24863
|
+
this.settingsUpdateTail = operation.then((result) => {
|
|
24864
|
+
if (result.status === "applied") {
|
|
24865
|
+
this.settingsUpdatePending = false;
|
|
24866
|
+
return;
|
|
24867
|
+
}
|
|
24868
|
+
return new Promise(() => {});
|
|
24869
|
+
});
|
|
24870
|
+
return operation;
|
|
24871
|
+
}
|
|
23877
24872
|
async interrupt(input) {
|
|
23878
24873
|
if (this.state === "closed" || this.state === "stopping" || this.finishing)
|
|
23879
24874
|
return { status: "closed" };
|
|
@@ -24035,7 +25030,7 @@ class LogicalAgentSession {
|
|
|
24035
25030
|
}
|
|
24036
25031
|
return receipt;
|
|
24037
25032
|
}
|
|
24038
|
-
if (this.state === "idle" && (this.queued.length > 0 || this.safeBoundaryFlush !== undefined || this.safeBoundaryDelivery !== undefined)) {
|
|
25033
|
+
if (this.state === "idle" && (this.queued.length > 0 || this.safeBoundaryFlush !== undefined || this.safeBoundaryDelivery !== undefined || this.settingsUpdatePending)) {
|
|
24039
25034
|
return this.queue(message2, "runtime_busy");
|
|
24040
25035
|
}
|
|
24041
25036
|
return this.startTurn([message2], "prompt");
|
|
@@ -24343,11 +25338,10 @@ class LogicalAgentSession {
|
|
|
24343
25338
|
}
|
|
24344
25339
|
return;
|
|
24345
25340
|
case "telemetry": {
|
|
24346
|
-
const details = jsonValue(event.attrs);
|
|
24347
25341
|
if (event.name === "token_usage") {
|
|
24348
|
-
this.emit({ type: "token_usage", turnId, source: event.source, usage:
|
|
25342
|
+
this.emit({ type: "token_usage", turnId, source: event.source, usage: event.usage });
|
|
24349
25343
|
} else {
|
|
24350
|
-
this.emit({ type: "rate_limits", turnId, source: event.source,
|
|
25344
|
+
this.emit({ type: "rate_limits", turnId, source: event.source, quota: event.quota });
|
|
24351
25345
|
}
|
|
24352
25346
|
return;
|
|
24353
25347
|
}
|
|
@@ -24448,7 +25442,7 @@ class LogicalAgentSession {
|
|
|
24448
25442
|
if (this.adapter.execution.lifetime === "turn") {
|
|
24449
25443
|
this.processTurnEnded = true;
|
|
24450
25444
|
} else {
|
|
24451
|
-
Promise.resolve().then(() => this.safeBoundaryFlush).then(() => this.startNextQueued());
|
|
25445
|
+
Promise.resolve().then(() => this.safeBoundaryFlush).then(() => this.settingsUpdateTail).then(() => this.startNextQueued());
|
|
24452
25446
|
}
|
|
24453
25447
|
}
|
|
24454
25448
|
flushSafeBoundaryQueue() {
|
|
@@ -24948,8 +25942,14 @@ function createAgentDriverSdkWithRegistry(options) {
|
|
|
24948
25942
|
assertAdapterCompatibility(String(registration.id), registration.capabilities, adapter);
|
|
24949
25943
|
const command = capabilities2.commandOverride ? input.command : undefined;
|
|
24950
25944
|
const result = await adapter.probe(command);
|
|
24951
|
-
if (result.status === "healthy")
|
|
24952
|
-
return {
|
|
25945
|
+
if (result.status === "healthy") {
|
|
25946
|
+
return {
|
|
25947
|
+
status: "healthy",
|
|
25948
|
+
version: result.version,
|
|
25949
|
+
capabilities: capabilities2,
|
|
25950
|
+
reasoning: result.reasoning
|
|
25951
|
+
};
|
|
25952
|
+
}
|
|
24953
25953
|
return {
|
|
24954
25954
|
status: "unhealthy",
|
|
24955
25955
|
error: {
|
|
@@ -24958,7 +25958,8 @@ function createAgentDriverSdkWithRegistry(options) {
|
|
|
24958
25958
|
message: `Backend ${input.backend} is unavailable`,
|
|
24959
25959
|
retryable: true
|
|
24960
25960
|
},
|
|
24961
|
-
capabilities: capabilities2
|
|
25961
|
+
capabilities: capabilities2,
|
|
25962
|
+
reasoning: result.reasoning
|
|
24962
25963
|
};
|
|
24963
25964
|
} catch (error51) {
|
|
24964
25965
|
const contractInvalid = error51 instanceof Error && (error51.message.startsWith("Adapter ") || error51.message.startsWith("Agent backend registration "));
|
|
@@ -25110,7 +26111,12 @@ async function detectRuntimes() {
|
|
|
25110
26111
|
const driver = getDriver(id);
|
|
25111
26112
|
const probe = await driver.probe();
|
|
25112
26113
|
if (probe.status === "healthy") {
|
|
25113
|
-
results.push({
|
|
26114
|
+
results.push({
|
|
26115
|
+
id,
|
|
26116
|
+
status: "healthy",
|
|
26117
|
+
version: probe.version,
|
|
26118
|
+
reasoning: probe.reasoning
|
|
26119
|
+
});
|
|
25114
26120
|
} else {
|
|
25115
26121
|
results.push({
|
|
25116
26122
|
id,
|
|
@@ -25183,14 +26189,14 @@ function createLogger2(options = {}) {
|
|
|
25183
26189
|
`));
|
|
25184
26190
|
const err = options.err ?? ((line) => process.stderr.write(line + `
|
|
25185
26191
|
`));
|
|
25186
|
-
const
|
|
26192
|
+
const record5 = options.record;
|
|
25187
26193
|
const emit = (level, message2, data) => {
|
|
25188
26194
|
if (LEVEL_RANK[level] < minRank)
|
|
25189
26195
|
return;
|
|
25190
26196
|
const time3 = now();
|
|
25191
26197
|
const line = `${time3} ${header} ${level.toUpperCase().padEnd(5)} ${message2}${formatData(data)}`;
|
|
25192
26198
|
try {
|
|
25193
|
-
|
|
26199
|
+
record5?.({ time: time3, header, level, message: message2, fields: recordFields(data) });
|
|
25194
26200
|
} catch {}
|
|
25195
26201
|
(level === "warn" || level === "error" ? err : out)(line);
|
|
25196
26202
|
};
|
|
@@ -25226,7 +26232,7 @@ import * as path12 from "node:path";
|
|
|
25226
26232
|
import { WebSocket } from "ws";
|
|
25227
26233
|
|
|
25228
26234
|
// src/daemon/createDaemon.ts
|
|
25229
|
-
import { homedir as
|
|
26235
|
+
import { homedir as homedir5 } from "os";
|
|
25230
26236
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "node:fs";
|
|
25231
26237
|
|
|
25232
26238
|
// src/util/rotatingFileSink.ts
|
|
@@ -25914,23 +26920,43 @@ class WsControlChannel {
|
|
|
25914
26920
|
this.ws.send(JSON.stringify(frame));
|
|
25915
26921
|
}
|
|
25916
26922
|
resyncOnConnect() {
|
|
26923
|
+
const sendActivities = (activities, counts) => {
|
|
26924
|
+
for (const activity of activities) {
|
|
26925
|
+
this.sendFrame({ type: "agent_activity", ...activity });
|
|
26926
|
+
}
|
|
26927
|
+
this.log.info("resync sent", {
|
|
26928
|
+
ready: counts.ready,
|
|
26929
|
+
sessions: counts.sessions,
|
|
26930
|
+
activities: activities.length,
|
|
26931
|
+
pendingAuditEvents: this.pendingBotAuditEvents.size
|
|
26932
|
+
});
|
|
26933
|
+
};
|
|
25917
26934
|
if (this.resyncProvider) {
|
|
25918
|
-
const
|
|
25919
|
-
this.
|
|
25920
|
-
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
this.sendFrame({ type: "agent_activity", ...a });
|
|
26935
|
+
const socketAtStart = this.ws;
|
|
26936
|
+
const snapshot = this.resyncProvider();
|
|
26937
|
+
this.sendFrame({ type: "ready", ...snapshot.ready });
|
|
26938
|
+
for (const session2 of snapshot.sessions) {
|
|
26939
|
+
this.sendFrame({ type: "agent_session", ...session2 });
|
|
26940
|
+
}
|
|
25925
26941
|
for (const frame of this.pendingBotAuditEvents.values())
|
|
25926
26942
|
this.sendFrame(frame);
|
|
25927
26943
|
this.scheduleAuditRetry();
|
|
25928
|
-
|
|
25929
|
-
|
|
25930
|
-
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
26944
|
+
const activities = snapshot.activities ?? [];
|
|
26945
|
+
const counts = {
|
|
26946
|
+
ready: snapshot.ready.runtimeReport.length,
|
|
26947
|
+
sessions: snapshot.sessions.length
|
|
26948
|
+
};
|
|
26949
|
+
if (activities instanceof Promise) {
|
|
26950
|
+
activities.then((resolved) => {
|
|
26951
|
+
if (this.ws === socketAtStart && this.statusValue === "open") {
|
|
26952
|
+
sendActivities(resolved, counts);
|
|
26953
|
+
}
|
|
26954
|
+
}).catch((err) => {
|
|
26955
|
+
this.log.warn("resync provider failed", { err: describeErr(err) });
|
|
26956
|
+
});
|
|
26957
|
+
} else {
|
|
26958
|
+
sendActivities(activities, counts);
|
|
26959
|
+
}
|
|
25934
26960
|
}
|
|
25935
26961
|
for (const hook of this.resyncHooks) {
|
|
25936
26962
|
try {
|
|
@@ -26314,20 +27340,20 @@ function parseLocalMessageReminderBody(body, agentId) {
|
|
|
26314
27340
|
}
|
|
26315
27341
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
26316
27342
|
return null;
|
|
26317
|
-
const
|
|
26318
|
-
if (Object.keys(
|
|
27343
|
+
const record5 = value;
|
|
27344
|
+
if (Object.keys(record5).sort().join(",") !== "channel,remindAfterMs,sentSeq")
|
|
26319
27345
|
return null;
|
|
26320
|
-
if (typeof
|
|
27346
|
+
if (typeof record5.channel !== "string" || !isCanonicalChannelScope(record5.channel))
|
|
26321
27347
|
return null;
|
|
26322
|
-
if (!Number.isSafeInteger(
|
|
27348
|
+
if (!Number.isSafeInteger(record5.sentSeq) || record5.sentSeq < 1)
|
|
26323
27349
|
return null;
|
|
26324
|
-
if (!Number.isSafeInteger(
|
|
27350
|
+
if (!Number.isSafeInteger(record5.remindAfterMs) || record5.remindAfterMs !== 0 && record5.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record5.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
|
|
26325
27351
|
return null;
|
|
26326
27352
|
return {
|
|
26327
27353
|
agentId,
|
|
26328
|
-
channel:
|
|
26329
|
-
sentSeq:
|
|
26330
|
-
remindAfterMs:
|
|
27354
|
+
channel: record5.channel,
|
|
27355
|
+
sentSeq: record5.sentSeq,
|
|
27356
|
+
remindAfterMs: record5.remindAfterMs
|
|
26331
27357
|
};
|
|
26332
27358
|
}
|
|
26333
27359
|
async function handleLocalMessageReminder(req, res, agentId, onArm) {
|
|
@@ -26673,13 +27699,13 @@ function reduceManager(state, event) {
|
|
|
26673
27699
|
const existing = state.agents[event.agentId];
|
|
26674
27700
|
if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
|
|
26675
27701
|
return { state, effects: [] };
|
|
26676
|
-
const
|
|
26677
|
-
if (!
|
|
27702
|
+
const record5 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
|
|
27703
|
+
if (!record5)
|
|
26678
27704
|
return { state, effects: [] };
|
|
26679
27705
|
const agent2 = clone2(existing);
|
|
26680
27706
|
agent2.pendingAdmissions = agent2.pendingAdmissions.filter((entry) => entry.sessionInstanceId !== event.sessionInstanceId || entry.commandId !== event.commandId);
|
|
26681
27707
|
syncExecutionProjection(agent2);
|
|
26682
|
-
return commit(state, agent2, event.outcome === "failed" ? recoveryEffects(agent2, [
|
|
27708
|
+
return commit(state, agent2, event.outcome === "failed" ? recoveryEffects(agent2, [record5]) : []);
|
|
26683
27709
|
}
|
|
26684
27710
|
case "admission_acknowledged": {
|
|
26685
27711
|
const existing = state.agents[event.agentId];
|
|
@@ -26736,6 +27762,29 @@ function reduceManager(state, event) {
|
|
|
26736
27762
|
a.inbox = [...a.inbox, event.message];
|
|
26737
27763
|
a.idleSince = null;
|
|
26738
27764
|
});
|
|
27765
|
+
case "runtime_config_queued":
|
|
27766
|
+
return mutate(state, event.agentId, (a) => {
|
|
27767
|
+
if (!a.inbox.some((message2) => message2.id === event.message.id)) {
|
|
27768
|
+
a.inbox = [...a.inbox, event.message];
|
|
27769
|
+
}
|
|
27770
|
+
syncExecutionProjection(a);
|
|
27771
|
+
a.idleSince = null;
|
|
27772
|
+
});
|
|
27773
|
+
case "runtime_config_applied": {
|
|
27774
|
+
const existing = state.agents[event.agentId];
|
|
27775
|
+
if (!existing)
|
|
27776
|
+
return { state, effects: [] };
|
|
27777
|
+
const agent2 = clone2(existing);
|
|
27778
|
+
if (agent2.status !== "running" || leaseIsWorking(agent2.execution.lease) || agent2.pendingAdmissions.length > 0 || agent2.inbox.length === 0)
|
|
27779
|
+
return { state, effects: [] };
|
|
27780
|
+
const messages = drainInbox(agent2);
|
|
27781
|
+
return commit(state, agent2, messages.map((message2) => ({
|
|
27782
|
+
type: "send",
|
|
27783
|
+
agentId: event.agentId,
|
|
27784
|
+
message: message2,
|
|
27785
|
+
mode: "idle"
|
|
27786
|
+
})));
|
|
27787
|
+
}
|
|
26739
27788
|
case "turn_started": {
|
|
26740
27789
|
const existing = state.agents[event.agentId];
|
|
26741
27790
|
if (!existing || existing.execution.sessionInstanceId !== event.sessionInstanceId)
|
|
@@ -26943,7 +27992,7 @@ function onTurnCompleted(state, agentId, sessionInstanceId, nowMs, turnId, endRe
|
|
|
26943
27992
|
agent2.stalledSessionId = null;
|
|
26944
27993
|
syncExecutionProjection(agent2);
|
|
26945
27994
|
const clearEffects = clearedStallSessionId === null ? [] : [{ type: "clear_stall_recovery", agentId, sessionId: clearedStallSessionId }];
|
|
26946
|
-
if (agent2.inbox.length > 0) {
|
|
27995
|
+
if (agent2.inbox.length > 0 && !agent2.resetting) {
|
|
26947
27996
|
const messages = drainInbox(agent2);
|
|
26948
27997
|
return commit(state, agent2, [
|
|
26949
27998
|
...clearEffects,
|
|
@@ -27174,11 +28223,11 @@ function syncExecutionProjection(agent2) {
|
|
|
27174
28223
|
agent2.lastDeliverAt = agent2.pendingAdmissions.length > 0 ? Math.max(...agent2.pendingAdmissions.map((entry) => entry.admittedAt)) : null;
|
|
27175
28224
|
}
|
|
27176
28225
|
function recoveryEffects(agent2, records) {
|
|
27177
|
-
return records.filter((
|
|
28226
|
+
return records.filter((record5) => record5.requeueOnFailure).map((record5) => ({
|
|
27178
28227
|
type: "requeue_delivery",
|
|
27179
28228
|
agentId: agent2.agentId,
|
|
27180
|
-
message:
|
|
27181
|
-
mode:
|
|
28229
|
+
message: record5.exactAgentMsg,
|
|
28230
|
+
mode: record5.mode
|
|
27182
28231
|
}));
|
|
27183
28232
|
}
|
|
27184
28233
|
function commit(state, agent2, effects) {
|
|
@@ -27203,6 +28252,144 @@ function toAgentBackendSelection(config2) {
|
|
|
27203
28252
|
function runtimeModelName(config2) {
|
|
27204
28253
|
return config2?.model.kind === "default" ? undefined : config2?.model.name;
|
|
27205
28254
|
}
|
|
28255
|
+
// agent-driver/dist/provider-quota.js
|
|
28256
|
+
import { execFile } from "node:child_process";
|
|
28257
|
+
import { readFile } from "node:fs/promises";
|
|
28258
|
+
import { homedir as homedir3 } from "node:os";
|
|
28259
|
+
import { join as join11 } from "node:path";
|
|
28260
|
+
import { promisify } from "node:util";
|
|
28261
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
28262
|
+
var execFileAsync = promisify(execFile);
|
|
28263
|
+
var claudeAccessToken = null;
|
|
28264
|
+
var claudeSourceEpoch = randomBytes5(16).toString("base64url");
|
|
28265
|
+
function parseCredentials(value) {
|
|
28266
|
+
try {
|
|
28267
|
+
const parsed = JSON.parse(value);
|
|
28268
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
28269
|
+
} catch {
|
|
28270
|
+
return null;
|
|
28271
|
+
}
|
|
28272
|
+
}
|
|
28273
|
+
async function claudeCredentials(options) {
|
|
28274
|
+
const platform = options.platform ?? process.platform;
|
|
28275
|
+
if (platform === "darwin") {
|
|
28276
|
+
try {
|
|
28277
|
+
const value = options.readKeychain ? await options.readKeychain() : (await execFileAsync("security", ["find-generic-password", "-s", "Claude Code-credentials", "-w"], {
|
|
28278
|
+
timeout: 3000,
|
|
28279
|
+
maxBuffer: 256 * 1024
|
|
28280
|
+
})).stdout;
|
|
28281
|
+
const parsed = parseCredentials(value.trim());
|
|
28282
|
+
if (parsed)
|
|
28283
|
+
return parsed;
|
|
28284
|
+
} catch {}
|
|
28285
|
+
}
|
|
28286
|
+
const env = options.env ?? process.env;
|
|
28287
|
+
const root = env.CLAUDE_CONFIG_DIR || join11(options.home ?? homedir3(), ".claude");
|
|
28288
|
+
try {
|
|
28289
|
+
const value = options.readCredentialsFile ? await options.readCredentialsFile(join11(root, ".credentials.json")) : await readFile(join11(root, ".credentials.json"), "utf8");
|
|
28290
|
+
return parseCredentials(value);
|
|
28291
|
+
} catch {
|
|
28292
|
+
return null;
|
|
28293
|
+
}
|
|
28294
|
+
}
|
|
28295
|
+
function mappedPlanName2(value) {
|
|
28296
|
+
switch (value) {
|
|
28297
|
+
case "free":
|
|
28298
|
+
return "Free";
|
|
28299
|
+
case "pro":
|
|
28300
|
+
return "Pro";
|
|
28301
|
+
case "max":
|
|
28302
|
+
return "Max";
|
|
28303
|
+
case "team":
|
|
28304
|
+
return "Team";
|
|
28305
|
+
case "enterprise":
|
|
28306
|
+
return "Enterprise";
|
|
28307
|
+
default:
|
|
28308
|
+
return;
|
|
28309
|
+
}
|
|
28310
|
+
}
|
|
28311
|
+
function resetIso2(value) {
|
|
28312
|
+
if (typeof value !== "string")
|
|
28313
|
+
return;
|
|
28314
|
+
const date5 = new Date(value);
|
|
28315
|
+
return Number.isNaN(date5.getTime()) ? undefined : date5.toISOString();
|
|
28316
|
+
}
|
|
28317
|
+
function claudeLimit(key, value) {
|
|
28318
|
+
if (!value || typeof value !== "object")
|
|
28319
|
+
return null;
|
|
28320
|
+
const row = value;
|
|
28321
|
+
if (typeof row.utilization !== "number" || !Number.isFinite(row.utilization) || row.utilization < 0 || row.utilization > 100)
|
|
28322
|
+
return null;
|
|
28323
|
+
const model = key.includes("sonnet") ? { kind: "reported", id: "claude-sonnet" } : key.includes("opus") ? { kind: "reported", id: "claude-opus" } : { kind: "not_applicable" };
|
|
28324
|
+
const window2 = key === "five_hour" ? { kind: "rolling", durationSeconds: 18000, displayName: "5 hour usage limit" } : { kind: "rolling", durationSeconds: 604800, displayName: "7 day usage limit" };
|
|
28325
|
+
const resetsAt = resetIso2(row.resets_at ?? row.resetsAt);
|
|
28326
|
+
return {
|
|
28327
|
+
bucket: {
|
|
28328
|
+
limitId: key,
|
|
28329
|
+
product: { kind: "reported", id: "claude", displayName: "Claude" },
|
|
28330
|
+
model,
|
|
28331
|
+
window: window2
|
|
28332
|
+
},
|
|
28333
|
+
usedPercent: row.utilization,
|
|
28334
|
+
...resetsAt ? { resetsAt } : {}
|
|
28335
|
+
};
|
|
28336
|
+
}
|
|
28337
|
+
async function readClaudeQuota(options) {
|
|
28338
|
+
const env = options.env ?? process.env;
|
|
28339
|
+
if (env.ANTHROPIC_API_KEY || env.ANTHROPIC_BASE_URL)
|
|
28340
|
+
return null;
|
|
28341
|
+
const credentials = await claudeCredentials(options);
|
|
28342
|
+
const token = credentials?.claudeAiOauth?.accessToken;
|
|
28343
|
+
if (typeof token !== "string" || token.length === 0)
|
|
28344
|
+
return null;
|
|
28345
|
+
if (claudeAccessToken !== token) {
|
|
28346
|
+
claudeAccessToken = token;
|
|
28347
|
+
claudeSourceEpoch = randomBytes5(16).toString("base64url");
|
|
28348
|
+
}
|
|
28349
|
+
let response;
|
|
28350
|
+
try {
|
|
28351
|
+
response = await (options.fetchUsage ?? fetch)("https://api.anthropic.com/api/oauth/usage", {
|
|
28352
|
+
headers: {
|
|
28353
|
+
authorization: `Bearer ${token}`,
|
|
28354
|
+
"anthropic-beta": "oauth-2025-04-20"
|
|
28355
|
+
},
|
|
28356
|
+
signal: AbortSignal.timeout(5000)
|
|
28357
|
+
});
|
|
28358
|
+
} catch {
|
|
28359
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "network", retryable: true };
|
|
28360
|
+
}
|
|
28361
|
+
if (response.status === 401 || response.status === 403) {
|
|
28362
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "unauthorized", retryable: false };
|
|
28363
|
+
}
|
|
28364
|
+
if (!response.ok) {
|
|
28365
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "provider_error", retryable: response.status === 429 || response.status >= 500 };
|
|
28366
|
+
}
|
|
28367
|
+
let body;
|
|
28368
|
+
try {
|
|
28369
|
+
body = await response.json();
|
|
28370
|
+
} catch {
|
|
28371
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
|
|
28372
|
+
}
|
|
28373
|
+
if (!body || typeof body !== "object") {
|
|
28374
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
|
|
28375
|
+
}
|
|
28376
|
+
const record5 = body;
|
|
28377
|
+
const limits = ["five_hour", "seven_day", "seven_day_sonnet", "seven_day_opus"].map((key) => claudeLimit(key, record5[key])).filter((limit) => limit !== null);
|
|
28378
|
+
if (limits.length === 0) {
|
|
28379
|
+
return { status: "error", sourceEpoch: claudeSourceEpoch, code: "invalid_response", retryable: true };
|
|
28380
|
+
}
|
|
28381
|
+
const planName = mappedPlanName2(credentials?.claudeAiOauth?.subscriptionType);
|
|
28382
|
+
return {
|
|
28383
|
+
status: "available",
|
|
28384
|
+
sourceEpoch: claudeSourceEpoch,
|
|
28385
|
+
...planName ? { planName } : {},
|
|
28386
|
+
freshForSeconds: 300,
|
|
28387
|
+
limits
|
|
28388
|
+
};
|
|
28389
|
+
}
|
|
28390
|
+
async function readBuiltinProviderQuota(backend, options = {}) {
|
|
28391
|
+
return backend === "claude" ? readClaudeQuota(options) : null;
|
|
28392
|
+
}
|
|
27206
28393
|
// src/runtime/errorDiagnostics.ts
|
|
27207
28394
|
function scrubRuntimeErrorDiagnosticText(value) {
|
|
27208
28395
|
return scrubDriverErrorMessage(value);
|
|
@@ -27798,9 +28985,13 @@ class AgentProcessManager {
|
|
|
27798
28985
|
state;
|
|
27799
28986
|
sessions = new Map;
|
|
27800
28987
|
runtimeConfigs = new Map;
|
|
28988
|
+
appliedRuntimeConfigs = new Map;
|
|
28989
|
+
pendingRuntimeConfigUpdates = new Map;
|
|
28990
|
+
runtimeConfigApplyRunning = new Set;
|
|
27801
28991
|
resumeSessions = new Map;
|
|
27802
28992
|
launchIds = new Map;
|
|
27803
28993
|
liveSessions = new Map;
|
|
28994
|
+
liveBackendIds = new Map;
|
|
27804
28995
|
activeSpawnState = new Map;
|
|
27805
28996
|
publishedAgentActivity = new Map;
|
|
27806
28997
|
traceProcessNonce = randomUUID5();
|
|
@@ -27829,19 +29020,156 @@ class AgentProcessManager {
|
|
|
27829
29020
|
this.state = createInitialManagerState(this.opts.staleThresholdMs, this.opts.idleTimeoutMs, this.opts.resetStuckThresholdMs, this.opts.stoppingStuckThresholdMs, this.opts.idleResetTimeoutMs);
|
|
27830
29021
|
}
|
|
27831
29022
|
register(agentId, launch) {
|
|
27832
|
-
|
|
27833
|
-
this.runtimeConfigs.set(agentId, launch.runtimeConfig);
|
|
29023
|
+
const runtimeConfigAcceptance = launch?.runtimeConfig ? this.acceptRuntimeConfig(agentId, launch.runtimeConfig) : undefined;
|
|
27834
29024
|
if (launch?.sessionId)
|
|
27835
29025
|
this.resumeSessions.set(agentId, launch.sessionId);
|
|
27836
29026
|
if (launch?.launchId)
|
|
27837
29027
|
this.launchIds.set(agentId, launch.launchId);
|
|
27838
29028
|
this.dispatch({ type: "register", agentId });
|
|
29029
|
+
const registered = this.state.agents[agentId];
|
|
29030
|
+
if (launch?.runtimeConfig && launch.applyRuntimeConfig !== false && (runtimeConfigAcceptance === "accepted" || this.pendingRuntimeConfigUpdates.has(agentId)) && this.sessions.has(agentId) && registered && !isActivelyWorking(registered)) {
|
|
29031
|
+
this.convergeRuntimeConfig(agentId);
|
|
29032
|
+
}
|
|
29033
|
+
}
|
|
29034
|
+
async updateRuntimeConfig(agentId, config2) {
|
|
29035
|
+
const accepted = this.acceptRuntimeConfig(agentId, config2);
|
|
29036
|
+
if (accepted === "stale" || accepted === "idempotent")
|
|
29037
|
+
return accepted;
|
|
29038
|
+
const session2 = this.sessions.get(agentId);
|
|
29039
|
+
if (!session2) {
|
|
29040
|
+
this.pendingRuntimeConfigUpdates.delete(agentId);
|
|
29041
|
+
return "saved_for_start";
|
|
29042
|
+
}
|
|
29043
|
+
const agent2 = this.state.agents[agentId];
|
|
29044
|
+
if (agent2 && (agent2.turnActive || isActivelyWorking(agent2)))
|
|
29045
|
+
return "deferred";
|
|
29046
|
+
return this.convergeRuntimeConfig(agentId);
|
|
29047
|
+
}
|
|
29048
|
+
acceptRuntimeConfig(agentId, config2) {
|
|
29049
|
+
const desired = this.runtimeConfigs.get(agentId);
|
|
29050
|
+
const revision = config2.runtimeConfigRevision ?? 0;
|
|
29051
|
+
const desiredRevision = desired?.runtimeConfigRevision ?? 0;
|
|
29052
|
+
if (desired && revision < desiredRevision)
|
|
29053
|
+
return "stale";
|
|
29054
|
+
if (desired && revision === desiredRevision) {
|
|
29055
|
+
if (this.runtimeConfigTuple(desired) !== this.runtimeConfigTuple(config2)) {
|
|
29056
|
+
throw new Error(`Conflicting runtime config for ${agentId} at revision ${revision}`);
|
|
29057
|
+
}
|
|
29058
|
+
this.runtimeConfigs.set(agentId, config2);
|
|
29059
|
+
return "idempotent";
|
|
29060
|
+
}
|
|
29061
|
+
this.runtimeConfigs.set(agentId, config2);
|
|
29062
|
+
this.pendingRuntimeConfigUpdates.set(agentId, config2);
|
|
29063
|
+
return "accepted";
|
|
29064
|
+
}
|
|
29065
|
+
runtimeConfigTuple(config2) {
|
|
29066
|
+
return JSON.stringify({
|
|
29067
|
+
version: config2.version,
|
|
29068
|
+
runtime: config2.runtime,
|
|
29069
|
+
model: config2.model,
|
|
29070
|
+
mode: config2.mode,
|
|
29071
|
+
reasoningEffort: config2.reasoningEffort ?? null,
|
|
29072
|
+
provider: config2.provider ?? null,
|
|
29073
|
+
command: config2.command ?? null,
|
|
29074
|
+
disallowedTools: config2.disallowedTools ?? null,
|
|
29075
|
+
envVars: config2.envVars ?? null
|
|
29076
|
+
});
|
|
29077
|
+
}
|
|
29078
|
+
runtimeLaunchTuple(config2) {
|
|
29079
|
+
return JSON.stringify({
|
|
29080
|
+
version: config2.version,
|
|
29081
|
+
runtime: config2.runtime,
|
|
29082
|
+
model: config2.model,
|
|
29083
|
+
mode: config2.mode,
|
|
29084
|
+
provider: config2.provider ?? null,
|
|
29085
|
+
command: config2.command ?? null,
|
|
29086
|
+
disallowedTools: config2.disallowedTools ?? null,
|
|
29087
|
+
envVars: config2.envVars ?? null
|
|
29088
|
+
});
|
|
29089
|
+
}
|
|
29090
|
+
async convergeRuntimeConfig(agentId, restartOnFailure = true) {
|
|
29091
|
+
if (this.runtimeConfigApplyRunning.has(agentId))
|
|
29092
|
+
return "deferred";
|
|
29093
|
+
const session2 = this.sessions.get(agentId);
|
|
29094
|
+
if (!session2)
|
|
29095
|
+
return "saved_for_start";
|
|
29096
|
+
this.runtimeConfigApplyRunning.add(agentId);
|
|
29097
|
+
try {
|
|
29098
|
+
while (this.sessions.get(agentId) === session2) {
|
|
29099
|
+
const desired = this.pendingRuntimeConfigUpdates.get(agentId) ?? this.runtimeConfigs.get(agentId);
|
|
29100
|
+
if (!desired)
|
|
29101
|
+
return "idempotent";
|
|
29102
|
+
const desiredRevision = desired.runtimeConfigRevision ?? 0;
|
|
29103
|
+
const applied = this.appliedRuntimeConfigs.get(agentId);
|
|
29104
|
+
const appliedRevision = applied?.runtimeConfigRevision ?? -1;
|
|
29105
|
+
if (applied && desiredRevision <= appliedRevision) {
|
|
29106
|
+
this.pendingRuntimeConfigUpdates.delete(agentId);
|
|
29107
|
+
return desiredRevision === appliedRevision ? "idempotent" : "stale";
|
|
29108
|
+
}
|
|
29109
|
+
const canApplyNatively = applied && this.runtimeLaunchTuple(applied) === this.runtimeLaunchTuple(desired) && typeof session2.updateSettings === "function";
|
|
29110
|
+
let result = { status: "unsupported" };
|
|
29111
|
+
if (canApplyNatively) {
|
|
29112
|
+
try {
|
|
29113
|
+
result = await session2.updateSettings({
|
|
29114
|
+
reasoningEffort: desired.reasoningEffort ?? null
|
|
29115
|
+
});
|
|
29116
|
+
} catch (error51) {
|
|
29117
|
+
this.log.warn("runtime config live apply threw; restarting at safe boundary", {
|
|
29118
|
+
agentId,
|
|
29119
|
+
revision: desiredRevision,
|
|
29120
|
+
error: String(error51)
|
|
29121
|
+
});
|
|
29122
|
+
if (restartOnFailure)
|
|
29123
|
+
await this.restartForRuntimeConfig(agentId, session2);
|
|
29124
|
+
return "saved_for_start";
|
|
29125
|
+
}
|
|
29126
|
+
}
|
|
29127
|
+
if (result.status !== "applied") {
|
|
29128
|
+
this.log.warn("runtime config live apply unavailable; restarting at safe boundary", {
|
|
29129
|
+
agentId,
|
|
29130
|
+
revision: desiredRevision,
|
|
29131
|
+
status: result.status,
|
|
29132
|
+
code: result.error?.code
|
|
29133
|
+
});
|
|
29134
|
+
if (restartOnFailure)
|
|
29135
|
+
await this.restartForRuntimeConfig(agentId, session2);
|
|
29136
|
+
return "saved_for_start";
|
|
29137
|
+
}
|
|
29138
|
+
this.appliedRuntimeConfigs.set(agentId, desired);
|
|
29139
|
+
if ((this.pendingRuntimeConfigUpdates.get(agentId)?.runtimeConfigRevision ?? -1) === desiredRevision) {
|
|
29140
|
+
this.pendingRuntimeConfigUpdates.delete(agentId);
|
|
29141
|
+
}
|
|
29142
|
+
const latestRevision = this.runtimeConfigs.get(agentId)?.runtimeConfigRevision ?? 0;
|
|
29143
|
+
if (latestRevision <= desiredRevision) {
|
|
29144
|
+
this.dispatch({ type: "runtime_config_applied", agentId });
|
|
29145
|
+
return "applied";
|
|
29146
|
+
}
|
|
29147
|
+
}
|
|
29148
|
+
return "saved_for_start";
|
|
29149
|
+
} finally {
|
|
29150
|
+
this.runtimeConfigApplyRunning.delete(agentId);
|
|
29151
|
+
}
|
|
29152
|
+
}
|
|
29153
|
+
async restartForRuntimeConfig(agentId, session2) {
|
|
29154
|
+
if (this.sessions.get(agentId) !== session2)
|
|
29155
|
+
return;
|
|
29156
|
+
this.opts.timeline?.fenceSession(agentId);
|
|
29157
|
+
this.markResetting(agentId);
|
|
29158
|
+
await this.stop(agentId);
|
|
27839
29159
|
}
|
|
27840
29160
|
deliver(agentId, message2) {
|
|
27841
29161
|
const normalized = message2.id ? message2 : {
|
|
27842
29162
|
...message2,
|
|
27843
29163
|
id: message2.seq !== undefined ? `${agentId}:source:${message2.seq}` : `${agentId}:synthetic:${this.nextDeliveryOrdinal++}`
|
|
27844
29164
|
};
|
|
29165
|
+
if (this.sessions.has(agentId) && this.pendingRuntimeConfigUpdates.has(agentId)) {
|
|
29166
|
+
this.dispatch({
|
|
29167
|
+
type: "runtime_config_queued",
|
|
29168
|
+
agentId,
|
|
29169
|
+
message: normalized
|
|
29170
|
+
});
|
|
29171
|
+
return this.state.agents[agentId] !== undefined;
|
|
29172
|
+
}
|
|
27845
29173
|
const effects = this.dispatch({ type: "wake", agentId, message: normalized, nowMs: this.now() });
|
|
27846
29174
|
return effects.length > 0;
|
|
27847
29175
|
}
|
|
@@ -27885,7 +29213,11 @@ class AgentProcessManager {
|
|
|
27885
29213
|
this.emitErrorAudit(agentId, "reset", "resume_control_update_failed", "Reset aborted because resume control could not be persisted");
|
|
27886
29214
|
throw new Error("Reset aborted because resume control could not be persisted");
|
|
27887
29215
|
}
|
|
27888
|
-
this.register(agentId, {
|
|
29216
|
+
this.register(agentId, {
|
|
29217
|
+
runtimeConfig: opts.runtimeConfig,
|
|
29218
|
+
launchId: opts.launchId,
|
|
29219
|
+
applyRuntimeConfig: false
|
|
29220
|
+
});
|
|
27889
29221
|
if (!opts.forgetSession)
|
|
27890
29222
|
this.opts.timeline?.fenceSession(agentId);
|
|
27891
29223
|
this.abortCurrentTurn(agentId, opts.abortCause);
|
|
@@ -27986,6 +29318,9 @@ class AgentProcessManager {
|
|
|
27986
29318
|
const agent2 = this.state.agents[agentId];
|
|
27987
29319
|
return agent2 ? this.deriveActivity(agent2) : null;
|
|
27988
29320
|
}
|
|
29321
|
+
agentBackendId(agentId) {
|
|
29322
|
+
return this.liveBackendIds.get(agentId) ?? null;
|
|
29323
|
+
}
|
|
27989
29324
|
statusProjection(nowMs) {
|
|
27990
29325
|
return Object.values(this.state.agents).map((a) => ({
|
|
27991
29326
|
agentId: a.agentId,
|
|
@@ -28511,6 +29846,7 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
28511
29846
|
throw new Error(`AgentProcessManager: spawn for ${agentId} has no command`);
|
|
28512
29847
|
const prompt = this.withFooter(first.text);
|
|
28513
29848
|
const driver = this.opts.driverFor(agentId, this.runtimeConfigs.get(agentId));
|
|
29849
|
+
this.liveBackendIds.set(agentId, driver.id);
|
|
28514
29850
|
const base = this.opts.baseContextFor(agentId);
|
|
28515
29851
|
const configuredRuntime = this.runtimeConfigs.get(agentId) ?? base.config?.runtimeConfig;
|
|
28516
29852
|
this.log.info("spawning agent", {
|
|
@@ -28631,7 +29967,10 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
28631
29967
|
if (state.session && this.sessions.get(agentId) === state.session)
|
|
28632
29968
|
this.sessions.delete(agentId);
|
|
28633
29969
|
this.liveSessions.delete(agentId);
|
|
29970
|
+
if (this.activeSpawnState.get(agentId) === state)
|
|
29971
|
+
this.liveBackendIds.delete(agentId);
|
|
28634
29972
|
if (this.activeSpawnState.get(agentId) === state) {
|
|
29973
|
+
this.appliedRuntimeConfigs.delete(agentId);
|
|
28635
29974
|
this.activeSpawnState.delete(agentId);
|
|
28636
29975
|
this.nonCleanEndMarker.delete(agentId);
|
|
28637
29976
|
}
|
|
@@ -28679,6 +30018,10 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
28679
30018
|
state.session = session2;
|
|
28680
30019
|
state.sessionInstanceId = session2.sessionInstanceId;
|
|
28681
30020
|
this.sessions.set(agentId, session2);
|
|
30021
|
+
this.appliedRuntimeConfigs.set(agentId, runtimeConfig);
|
|
30022
|
+
if ((this.pendingRuntimeConfigUpdates.get(agentId)?.runtimeConfigRevision ?? -1) <= (runtimeConfig.runtimeConfigRevision ?? 0)) {
|
|
30023
|
+
this.pendingRuntimeConfigUpdates.delete(agentId);
|
|
30024
|
+
}
|
|
28682
30025
|
this.dispatch({
|
|
28683
30026
|
type: "attach_session",
|
|
28684
30027
|
agentId,
|
|
@@ -28912,6 +30255,12 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
28912
30255
|
runtime: runtimeId
|
|
28913
30256
|
});
|
|
28914
30257
|
}
|
|
30258
|
+
if (event.type === "token_usage") {
|
|
30259
|
+
this.opts.onTokenUsage?.({ agentId, backendId: runtimeId, usage: event.usage });
|
|
30260
|
+
}
|
|
30261
|
+
if (event.type === "rate_limits") {
|
|
30262
|
+
this.opts.onProviderQuota?.({ agentId, backendId: runtimeId, quota: event.quota });
|
|
30263
|
+
}
|
|
28915
30264
|
if (event.type === "turn_started") {
|
|
28916
30265
|
const timelineTurnOwner = {
|
|
28917
30266
|
sessionInstanceId: event.sessionInstanceId,
|
|
@@ -29026,7 +30375,7 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
29026
30375
|
this.logSessionEnded(agentId, "turn_end");
|
|
29027
30376
|
const marker = this.nonCleanEndMarker.get(agentId);
|
|
29028
30377
|
this.nonCleanEndMarker.delete(agentId);
|
|
29029
|
-
|
|
30378
|
+
const completionEvent = marker !== undefined ? {
|
|
29030
30379
|
type: "turn_completed",
|
|
29031
30380
|
agentId,
|
|
29032
30381
|
sessionInstanceId: event.sessionInstanceId,
|
|
@@ -29041,7 +30390,26 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
29041
30390
|
sessionInstanceId: event.sessionInstanceId,
|
|
29042
30391
|
nowMs: this.now(),
|
|
29043
30392
|
turnId: event.turnId
|
|
29044
|
-
}
|
|
30393
|
+
};
|
|
30394
|
+
if (this.pendingRuntimeConfigUpdates.has(agentId) && this.sessions.get(agentId) === owner.session) {
|
|
30395
|
+
this.convergeRuntimeConfig(agentId, false).then((result) => {
|
|
30396
|
+
if (result === "saved_for_start" && owner.session)
|
|
30397
|
+
this.markResetting(agentId);
|
|
30398
|
+
this.dispatch(completionEvent, owner);
|
|
30399
|
+
if (result === "saved_for_start" && owner.session) {
|
|
30400
|
+
this.restartForRuntimeConfig(agentId, owner.session);
|
|
30401
|
+
}
|
|
30402
|
+
}).catch((error51) => {
|
|
30403
|
+
this.log.error("runtime config convergence failed", { agentId, error: String(error51) });
|
|
30404
|
+
if (owner.session)
|
|
30405
|
+
this.markResetting(agentId);
|
|
30406
|
+
this.dispatch(completionEvent, owner);
|
|
30407
|
+
if (owner.session)
|
|
30408
|
+
this.restartForRuntimeConfig(agentId, owner.session);
|
|
30409
|
+
});
|
|
30410
|
+
return;
|
|
30411
|
+
}
|
|
30412
|
+
this.dispatch(completionEvent, owner);
|
|
29045
30413
|
}
|
|
29046
30414
|
}
|
|
29047
30415
|
}
|
|
@@ -29116,7 +30484,8 @@ class AgentRouter {
|
|
|
29116
30484
|
version: r.version,
|
|
29117
30485
|
status: r.status ?? "healthy",
|
|
29118
30486
|
lastError: r.lastError,
|
|
29119
|
-
lastErrorAt: r.lastErrorAt
|
|
30487
|
+
lastErrorAt: r.lastErrorAt,
|
|
30488
|
+
reasoning: r.reasoning
|
|
29120
30489
|
});
|
|
29121
30490
|
}
|
|
29122
30491
|
}
|
|
@@ -29125,7 +30494,7 @@ class AgentRouter {
|
|
|
29125
30494
|
this.opts.channel.onResync?.(() => ({
|
|
29126
30495
|
ready: this.buildReady(),
|
|
29127
30496
|
sessions: this.opts.manager.liveSessionReports(),
|
|
29128
|
-
activities: this.opts.manager.liveAgentActivities()
|
|
30497
|
+
activities: this.opts.resyncActivities ? this.opts.resyncActivities() : this.opts.manager.liveAgentActivities()
|
|
29129
30498
|
}));
|
|
29130
30499
|
await this.opts.channel.reportReady(this.buildReady());
|
|
29131
30500
|
}
|
|
@@ -29138,7 +30507,8 @@ class AgentRouter {
|
|
|
29138
30507
|
platform: this.opts.platform,
|
|
29139
30508
|
arch: this.opts.arch,
|
|
29140
30509
|
osRelease: this.opts.osRelease,
|
|
29141
|
-
daemonVersion: this.opts.daemonVersion
|
|
30510
|
+
daemonVersion: this.opts.daemonVersion,
|
|
30511
|
+
...this.opts.providerQuotas ? { providerQuotas: this.opts.providerQuotas() } : {}
|
|
29142
30512
|
};
|
|
29143
30513
|
}
|
|
29144
30514
|
healthyRuntimeIds() {
|
|
@@ -29179,11 +30549,10 @@ class AgentRouter {
|
|
|
29179
30549
|
return;
|
|
29180
30550
|
if (existing.status === "healthy" && !existing.lastError && !existing.lastErrorAt)
|
|
29181
30551
|
return;
|
|
29182
|
-
|
|
29183
|
-
|
|
29184
|
-
|
|
29185
|
-
|
|
29186
|
-
});
|
|
30552
|
+
const healthy = { ...existing, status: "healthy" };
|
|
30553
|
+
delete healthy.lastError;
|
|
30554
|
+
delete healthy.lastErrorAt;
|
|
30555
|
+
this.runtimes.set(id, healthy);
|
|
29187
30556
|
this.log.info("runtime marked healthy again", { runtimeId: id });
|
|
29188
30557
|
this.scheduleReadyFrameResend();
|
|
29189
30558
|
}
|
|
@@ -29360,6 +30729,27 @@ class AgentRouter {
|
|
|
29360
30729
|
rewakePrompt: MODEL_SWITCH_REWAKE_PROMPT
|
|
29361
30730
|
}));
|
|
29362
30731
|
break;
|
|
30732
|
+
case "agent:runtime_config_update": {
|
|
30733
|
+
this.log.info("agent:runtime_config_update received", {
|
|
30734
|
+
agentId: cmd.agentId,
|
|
30735
|
+
revision: cmd.config.runtimeConfigRevision ?? 0
|
|
30736
|
+
});
|
|
30737
|
+
try {
|
|
30738
|
+
const result = await this.opts.manager.updateRuntimeConfig(cmd.agentId, cmd.config);
|
|
30739
|
+
this.log.info("agent:runtime_config_update accepted", {
|
|
30740
|
+
agentId: cmd.agentId,
|
|
30741
|
+
revision: cmd.config.runtimeConfigRevision ?? 0,
|
|
30742
|
+
result
|
|
30743
|
+
});
|
|
30744
|
+
} catch (err) {
|
|
30745
|
+
this.log.warn("agent:runtime_config_update rejected", {
|
|
30746
|
+
agentId: cmd.agentId,
|
|
30747
|
+
revision: cmd.config.runtimeConfigRevision ?? 0,
|
|
30748
|
+
error: err instanceof Error ? err.message : String(err)
|
|
30749
|
+
});
|
|
30750
|
+
}
|
|
30751
|
+
break;
|
|
30752
|
+
}
|
|
29363
30753
|
case "agent:stop":
|
|
29364
30754
|
this.log.info("agent:stop received", { agentId: cmd.agentId });
|
|
29365
30755
|
try {
|
|
@@ -29417,8 +30807,8 @@ function createTypingScopeTracker() {
|
|
|
29417
30807
|
}
|
|
29418
30808
|
// src/timeline/timeline.ts
|
|
29419
30809
|
import * as fs9 from "node:fs";
|
|
29420
|
-
import { createHash as createHash2, randomBytes as
|
|
29421
|
-
import { basename as basename3, dirname as dirname5, join as
|
|
30810
|
+
import { createHash as createHash2, randomBytes as randomBytes6 } from "node:crypto";
|
|
30811
|
+
import { basename as basename3, dirname as dirname5, join as join12 } from "node:path";
|
|
29422
30812
|
|
|
29423
30813
|
// src/timeline/filelock.ts
|
|
29424
30814
|
import * as fs8 from "fs";
|
|
@@ -29761,7 +31151,7 @@ function scanTimelineFile(filePath) {
|
|
|
29761
31151
|
}
|
|
29762
31152
|
}
|
|
29763
31153
|
function atomicReplaceTimeline(filePath, lines) {
|
|
29764
|
-
const tempPath =
|
|
31154
|
+
const tempPath = join12(dirname5(filePath), `.${basename3(filePath)}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
|
|
29765
31155
|
let fd = null;
|
|
29766
31156
|
try {
|
|
29767
31157
|
fd = fs9.openSync(tempPath, "wx", 384);
|
|
@@ -29845,14 +31235,14 @@ function readRecentEntries(timelineDir, opts = {}) {
|
|
|
29845
31235
|
const filenames = recentFilenames(maxDays, now).reverse();
|
|
29846
31236
|
const entries = [];
|
|
29847
31237
|
for (const filename of filenames) {
|
|
29848
|
-
entries.push(...readJsonl(
|
|
31238
|
+
entries.push(...readJsonl(join12(timelineDir, filename)));
|
|
29849
31239
|
}
|
|
29850
31240
|
return entries;
|
|
29851
31241
|
}
|
|
29852
31242
|
function readResumeControlState(timelineDir) {
|
|
29853
31243
|
if (timelineDirectoryState(timelineDir) !== "safe")
|
|
29854
31244
|
return { kind: "missing" };
|
|
29855
|
-
const filePath =
|
|
31245
|
+
const filePath = join12(timelineDir, RESUME_CONTROL_FILENAME);
|
|
29856
31246
|
let source;
|
|
29857
31247
|
try {
|
|
29858
31248
|
source = fs9.lstatSync(filePath);
|
|
@@ -29928,8 +31318,8 @@ function updateResumeControlState(timelineDir, update) {
|
|
|
29928
31318
|
`;
|
|
29929
31319
|
if (Buffer.byteLength(body, "utf8") > RESUME_CONTROL_MAX_BYTES)
|
|
29930
31320
|
return false;
|
|
29931
|
-
const filePath =
|
|
29932
|
-
const tempPath =
|
|
31321
|
+
const filePath = join12(timelineDir, RESUME_CONTROL_FILENAME);
|
|
31322
|
+
const tempPath = join12(timelineDir, `.${RESUME_CONTROL_FILENAME}.${process.pid}.${randomBytes6(12).toString("hex")}.tmp`);
|
|
29933
31323
|
let fd = null;
|
|
29934
31324
|
try {
|
|
29935
31325
|
fd = fs9.openSync(tempPath, "wx", 384);
|
|
@@ -29959,7 +31349,7 @@ function appendTrackedEntry(timelineDir, entry, now = new Date) {
|
|
|
29959
31349
|
if (timelineDirectoryState(timelineDir) !== "safe")
|
|
29960
31350
|
return { status: "rejected", reason: "unsafe" };
|
|
29961
31351
|
const filename = filenameForDate(now);
|
|
29962
|
-
const filePath =
|
|
31352
|
+
const filePath = join12(timelineDir, filename);
|
|
29963
31353
|
const lockPath = lockPathFor(timelineDir, filename);
|
|
29964
31354
|
try {
|
|
29965
31355
|
if (!acquireLock(lockPath))
|
|
@@ -29987,7 +31377,7 @@ function updateTrackedEntry(timelineDir, handle, update) {
|
|
|
29987
31377
|
if (!DATE_FILENAME_PATTERN.test(handle.filename) || basename3(handle.filename) !== handle.filename) {
|
|
29988
31378
|
return { status: "rejected", reason: "unsafe" };
|
|
29989
31379
|
}
|
|
29990
|
-
const filePath =
|
|
31380
|
+
const filePath = join12(timelineDir, handle.filename);
|
|
29991
31381
|
const lockPath = lockPathFor(timelineDir, handle.filename);
|
|
29992
31382
|
try {
|
|
29993
31383
|
if (!acquireLock(lockPath))
|
|
@@ -30046,10 +31436,10 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
|
|
|
30046
31436
|
return;
|
|
30047
31437
|
}
|
|
30048
31438
|
for (const agentName of agentNames) {
|
|
30049
|
-
const agentDir =
|
|
31439
|
+
const agentDir = join12(workingDirectoryBase, agentName);
|
|
30050
31440
|
if (!isRealDirectory(agentDir))
|
|
30051
31441
|
continue;
|
|
30052
|
-
const timelineDir =
|
|
31442
|
+
const timelineDir = join12(agentDir, ".context_timeline");
|
|
30053
31443
|
if (!isRealDirectory(timelineDir))
|
|
30054
31444
|
continue;
|
|
30055
31445
|
let filenames;
|
|
@@ -30059,7 +31449,7 @@ async function sweepTimelineHistory(workingDirectoryBase, opts = {}) {
|
|
|
30059
31449
|
continue;
|
|
30060
31450
|
}
|
|
30061
31451
|
for (const filename of filenames) {
|
|
30062
|
-
const filePath =
|
|
31452
|
+
const filePath = join12(timelineDir, filename);
|
|
30063
31453
|
let source;
|
|
30064
31454
|
try {
|
|
30065
31455
|
source = fs9.lstatSync(filePath);
|
|
@@ -30743,15 +32133,15 @@ class MessageReminderScheduler {
|
|
|
30743
32133
|
const startedAt = this.now();
|
|
30744
32134
|
const dueAt = startedAt + input.remindAfterMs;
|
|
30745
32135
|
const sentRef = `${input.channel}#${input.sentSeq}`;
|
|
30746
|
-
const
|
|
32136
|
+
const record5 = {
|
|
30747
32137
|
...input,
|
|
30748
32138
|
sentRef,
|
|
30749
32139
|
startedAt,
|
|
30750
32140
|
dueAt,
|
|
30751
32141
|
timer: undefined
|
|
30752
32142
|
};
|
|
30753
|
-
|
|
30754
|
-
if (this.reminders.get(key) !==
|
|
32143
|
+
record5.timer = this.setTimer(() => {
|
|
32144
|
+
if (this.reminders.get(key) !== record5)
|
|
30755
32145
|
return;
|
|
30756
32146
|
this.reminders.delete(key);
|
|
30757
32147
|
try {
|
|
@@ -30762,8 +32152,8 @@ class MessageReminderScheduler {
|
|
|
30762
32152
|
Promise.resolve(delivery).catch(() => {});
|
|
30763
32153
|
} catch {}
|
|
30764
32154
|
}, input.remindAfterMs);
|
|
30765
|
-
|
|
30766
|
-
this.reminders.set(key,
|
|
32155
|
+
record5.timer.unref?.();
|
|
32156
|
+
this.reminders.set(key, record5);
|
|
30767
32157
|
return { armed: true, dueAt };
|
|
30768
32158
|
}
|
|
30769
32159
|
observe(agentId, channel2, latestSeq) {
|
|
@@ -30803,8 +32193,8 @@ class MessageReminderScheduler {
|
|
|
30803
32193
|
|
|
30804
32194
|
// src/manager/agentDriverHost.ts
|
|
30805
32195
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
30806
|
-
import { homedir as
|
|
30807
|
-
import { join as
|
|
32196
|
+
import { homedir as homedir4 } from "node:os";
|
|
32197
|
+
import { join as join13 } from "node:path";
|
|
30808
32198
|
|
|
30809
32199
|
// src/drivers/gitIdentityEnv.ts
|
|
30810
32200
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -30929,7 +32319,7 @@ function createDaemonAgentDriverHost(ctx, onRawLine) {
|
|
|
30929
32319
|
hostUser: readHostGitIdentity() ?? undefined
|
|
30930
32320
|
}),
|
|
30931
32321
|
platformProtected: {
|
|
30932
|
-
ALOOK_HOME: process.env.ALOOK_HOME ??
|
|
32322
|
+
ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir4(), ".alook"),
|
|
30933
32323
|
ALOOK_ID: ctx.agentId,
|
|
30934
32324
|
ALOOK_CLI: ctx.agentCliPath,
|
|
30935
32325
|
ALOOK_SERVER_URL: ctx.config.serverUrl,
|
|
@@ -31034,6 +32424,182 @@ class DaemonSelfSleepScheduler {
|
|
|
31034
32424
|
}
|
|
31035
32425
|
}
|
|
31036
32426
|
|
|
32427
|
+
// src/telemetry/dailyTokenUsage.ts
|
|
32428
|
+
import { chmod, mkdir, open, readFile as readFile2, rename, rm } from "node:fs/promises";
|
|
32429
|
+
import { dirname as dirname6, join as join14 } from "node:path";
|
|
32430
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
32431
|
+
function dayKey(at) {
|
|
32432
|
+
return at.toISOString().slice(0, 10);
|
|
32433
|
+
}
|
|
32434
|
+
function retainedDays(at) {
|
|
32435
|
+
const days = new Set;
|
|
32436
|
+
for (let offset = 0;offset < 7; offset += 1) {
|
|
32437
|
+
days.add(dayKey(new Date(at.getTime() - offset * 86400000)));
|
|
32438
|
+
}
|
|
32439
|
+
return days;
|
|
32440
|
+
}
|
|
32441
|
+
function isMetric(value) {
|
|
32442
|
+
return value === null || typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
32443
|
+
}
|
|
32444
|
+
function isSnapshot(value) {
|
|
32445
|
+
if (!value || typeof value !== "object")
|
|
32446
|
+
return false;
|
|
32447
|
+
const snapshot = value;
|
|
32448
|
+
const metrics = snapshot.metrics;
|
|
32449
|
+
return typeof snapshot.botId === "string" && snapshot.botId.length > 0 && typeof snapshot.day === "string" && /^\d{4}-\d{2}-\d{2}$/.test(snapshot.day) && !!metrics && isMetric(metrics.input) && isMetric(metrics.output) && isMetric(metrics.cache);
|
|
32450
|
+
}
|
|
32451
|
+
function mergeMetric(existing, delta, hasExistingSnapshot) {
|
|
32452
|
+
if (delta === null)
|
|
32453
|
+
return null;
|
|
32454
|
+
if (!Number.isSafeInteger(delta) || delta < 0) {
|
|
32455
|
+
throw new RangeError("token usage delta must be a non-negative safe integer");
|
|
32456
|
+
}
|
|
32457
|
+
if (!hasExistingSnapshot)
|
|
32458
|
+
return delta;
|
|
32459
|
+
if (existing === null)
|
|
32460
|
+
return null;
|
|
32461
|
+
const sum = existing + delta;
|
|
32462
|
+
if (!Number.isSafeInteger(sum))
|
|
32463
|
+
throw new RangeError("daily token usage exceeds safe integer range");
|
|
32464
|
+
return sum;
|
|
32465
|
+
}
|
|
32466
|
+
function emptySnapshot(botId, day) {
|
|
32467
|
+
return {
|
|
32468
|
+
botId,
|
|
32469
|
+
day,
|
|
32470
|
+
metrics: {
|
|
32471
|
+
input: null,
|
|
32472
|
+
output: null,
|
|
32473
|
+
cache: null
|
|
32474
|
+
}
|
|
32475
|
+
};
|
|
32476
|
+
}
|
|
32477
|
+
|
|
32478
|
+
class DailyTokenUsageStore {
|
|
32479
|
+
now;
|
|
32480
|
+
tail = Promise.resolve();
|
|
32481
|
+
loaded = false;
|
|
32482
|
+
data = { version: 1, bots: {} };
|
|
32483
|
+
filePath;
|
|
32484
|
+
constructor(workingDirectoryBase, now = () => new Date) {
|
|
32485
|
+
this.now = now;
|
|
32486
|
+
this.filePath = join14(workingDirectoryBase, ".telemetry", "daily-token-usage.json");
|
|
32487
|
+
}
|
|
32488
|
+
record(botId, delta) {
|
|
32489
|
+
return this.enqueue(async () => {
|
|
32490
|
+
await this.load();
|
|
32491
|
+
const at = this.now();
|
|
32492
|
+
this.prune(at);
|
|
32493
|
+
const day = dayKey(at);
|
|
32494
|
+
const snapshots = this.data.bots[botId] ?? [];
|
|
32495
|
+
const existing = snapshots.find((snapshot) => snapshot.day === day);
|
|
32496
|
+
const next = existing ?? emptySnapshot(botId, day);
|
|
32497
|
+
next.metrics = {
|
|
32498
|
+
input: mergeMetric(next.metrics.input, delta.input, existing !== undefined),
|
|
32499
|
+
output: mergeMetric(next.metrics.output, delta.output, existing !== undefined),
|
|
32500
|
+
cache: mergeMetric(next.metrics.cache, delta.cache, existing !== undefined)
|
|
32501
|
+
};
|
|
32502
|
+
if (!existing)
|
|
32503
|
+
snapshots.push(next);
|
|
32504
|
+
snapshots.sort((a, b) => a.day.localeCompare(b.day));
|
|
32505
|
+
this.data.bots[botId] = snapshots;
|
|
32506
|
+
await this.persist();
|
|
32507
|
+
});
|
|
32508
|
+
}
|
|
32509
|
+
snapshots(botId) {
|
|
32510
|
+
let result = [];
|
|
32511
|
+
return this.enqueue(async () => {
|
|
32512
|
+
await this.load();
|
|
32513
|
+
if (this.prune(this.now()))
|
|
32514
|
+
await this.persist();
|
|
32515
|
+
result = (this.data.bots[botId] ?? []).map((snapshot) => structuredClone(snapshot));
|
|
32516
|
+
}).then(() => result);
|
|
32517
|
+
}
|
|
32518
|
+
enqueue(operation) {
|
|
32519
|
+
const result = this.tail.then(operation, operation);
|
|
32520
|
+
this.tail = result.then(() => {
|
|
32521
|
+
return;
|
|
32522
|
+
}, () => {
|
|
32523
|
+
return;
|
|
32524
|
+
});
|
|
32525
|
+
return result;
|
|
32526
|
+
}
|
|
32527
|
+
async load() {
|
|
32528
|
+
if (this.loaded)
|
|
32529
|
+
return;
|
|
32530
|
+
let source;
|
|
32531
|
+
try {
|
|
32532
|
+
source = await readFile2(this.filePath, "utf8");
|
|
32533
|
+
} catch (error51) {
|
|
32534
|
+
if (!error51 || typeof error51 !== "object" || !("code" in error51) || error51.code !== "ENOENT") {
|
|
32535
|
+
throw error51;
|
|
32536
|
+
}
|
|
32537
|
+
this.data = { version: 1, bots: {} };
|
|
32538
|
+
this.loaded = true;
|
|
32539
|
+
return;
|
|
32540
|
+
}
|
|
32541
|
+
const parsed = JSON.parse(source);
|
|
32542
|
+
if (!parsed || typeof parsed !== "object" || parsed.version !== 1) {
|
|
32543
|
+
throw new Error("invalid daily token usage file version");
|
|
32544
|
+
}
|
|
32545
|
+
const bots = parsed.bots;
|
|
32546
|
+
if (!bots || typeof bots !== "object" || Array.isArray(bots)) {
|
|
32547
|
+
throw new Error("invalid daily token usage bots map");
|
|
32548
|
+
}
|
|
32549
|
+
const valid = {};
|
|
32550
|
+
for (const [botId, value] of Object.entries(bots)) {
|
|
32551
|
+
if (!Array.isArray(value) || !value.every(isSnapshot) || value.some((snapshot) => snapshot.botId !== botId)) {
|
|
32552
|
+
throw new Error(`invalid daily token usage snapshots for bot ${botId}`);
|
|
32553
|
+
}
|
|
32554
|
+
if (value.length > 0) {
|
|
32555
|
+
valid[botId] = value;
|
|
32556
|
+
}
|
|
32557
|
+
}
|
|
32558
|
+
this.data = { version: 1, bots: valid };
|
|
32559
|
+
this.loaded = true;
|
|
32560
|
+
}
|
|
32561
|
+
prune(at) {
|
|
32562
|
+
const keep = retainedDays(at);
|
|
32563
|
+
let changed = false;
|
|
32564
|
+
for (const [botId, snapshots] of Object.entries(this.data.bots)) {
|
|
32565
|
+
const retained = snapshots.filter((snapshot) => keep.has(snapshot.day)).sort((a, b) => a.day.localeCompare(b.day)).slice(-7);
|
|
32566
|
+
if (retained.length !== snapshots.length || retained.some((snapshot, index2) => snapshot !== snapshots[index2]))
|
|
32567
|
+
changed = true;
|
|
32568
|
+
if (retained.length === 0)
|
|
32569
|
+
delete this.data.bots[botId];
|
|
32570
|
+
else
|
|
32571
|
+
this.data.bots[botId] = retained;
|
|
32572
|
+
}
|
|
32573
|
+
return changed;
|
|
32574
|
+
}
|
|
32575
|
+
async persist() {
|
|
32576
|
+
const directory = dirname6(this.filePath);
|
|
32577
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
32578
|
+
const temporary = `${this.filePath}.${randomUUID7()}.tmp`;
|
|
32579
|
+
try {
|
|
32580
|
+
const file2 = await open(temporary, "wx", 384);
|
|
32581
|
+
try {
|
|
32582
|
+
await file2.writeFile(JSON.stringify(this.data), { encoding: "utf8" });
|
|
32583
|
+
await file2.sync();
|
|
32584
|
+
} finally {
|
|
32585
|
+
await file2.close();
|
|
32586
|
+
}
|
|
32587
|
+
await rename(temporary, this.filePath);
|
|
32588
|
+
await chmod(this.filePath, 384);
|
|
32589
|
+
try {
|
|
32590
|
+
const directoryHandle = await open(directory, "r");
|
|
32591
|
+
try {
|
|
32592
|
+
await directoryHandle.sync();
|
|
32593
|
+
} finally {
|
|
32594
|
+
await directoryHandle.close();
|
|
32595
|
+
}
|
|
32596
|
+
} catch {}
|
|
32597
|
+
} catch (error51) {
|
|
32598
|
+
await rm(temporary, { force: true }).catch(() => {});
|
|
32599
|
+
throw error51;
|
|
32600
|
+
}
|
|
32601
|
+
}
|
|
32602
|
+
}
|
|
31037
32603
|
// src/daemon/createDaemon.ts
|
|
31038
32604
|
var WARMUP_BACKOFF_MS = [250, 500, 1000, 2000, 4000];
|
|
31039
32605
|
var WARMUP_CEILING_MS = 30000;
|
|
@@ -31171,9 +32737,25 @@ function createBuiltinDaemonSessionFactory(onRuntimeRawLine) {
|
|
|
31171
32737
|
}
|
|
31172
32738
|
async function createDaemon(opts) {
|
|
31173
32739
|
const log2 = opts.logger ?? createLogger2({ header: "@alook/daemon" });
|
|
31174
|
-
const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${
|
|
32740
|
+
const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir5()}/.alook`) + "/daemon";
|
|
31175
32741
|
const workingDirectoryBase = opts.workingDirectoryBase ?? fallbackBase;
|
|
31176
32742
|
const workdirFor = (agentId) => `${workingDirectoryBase}/${agentId}`;
|
|
32743
|
+
const dailyTokenUsage2 = new DailyTokenUsageStore(workingDirectoryBase);
|
|
32744
|
+
const providerQuotaReader = opts.providerQuotaReader ?? (opts.sessionFactory ? async () => null : readBuiltinProviderQuota);
|
|
32745
|
+
const providerQuotaByBackend = new Map;
|
|
32746
|
+
let requestReadyQuotaResend = () => {};
|
|
32747
|
+
const recordProviderQuota = (backendId, quota) => {
|
|
32748
|
+
const previous = providerQuotaByBackend.get(backendId);
|
|
32749
|
+
if (previous?.observation.status === "available" && quota.status === "error" && previous.observation.sourceEpoch === quota.sourceEpoch)
|
|
32750
|
+
return;
|
|
32751
|
+
providerQuotaByBackend.set(backendId, {
|
|
32752
|
+
agentBackendId: backendId,
|
|
32753
|
+
observation: structuredClone(quota)
|
|
32754
|
+
});
|
|
32755
|
+
if (previous && previous.observation.sourceEpoch !== quota.sourceEpoch) {
|
|
32756
|
+
requestReadyQuotaResend();
|
|
32757
|
+
}
|
|
32758
|
+
};
|
|
31177
32759
|
sweepTimelineHistory(workingDirectoryBase).catch(() => {
|
|
31178
32760
|
log2.warn("timeline startup sweep failed");
|
|
31179
32761
|
});
|
|
@@ -31189,6 +32771,27 @@ async function createDaemon(opts) {
|
|
|
31189
32771
|
});
|
|
31190
32772
|
let channelRef = null;
|
|
31191
32773
|
let managerRef = null;
|
|
32774
|
+
const providerQuotaSnapshots = () => [...providerQuotaByBackend.values()].map((snapshot) => structuredClone(snapshot));
|
|
32775
|
+
const activityPayload = async (info) => {
|
|
32776
|
+
if (info.state !== "idle")
|
|
32777
|
+
return info;
|
|
32778
|
+
const backendId = managerRef?.agentBackendId(info.agentId);
|
|
32779
|
+
if (backendId === "claude") {
|
|
32780
|
+
const observed = await providerQuotaReader("claude");
|
|
32781
|
+
if (observed)
|
|
32782
|
+
recordProviderQuota("claude", observed);
|
|
32783
|
+
}
|
|
32784
|
+
const quota = backendId === "claude" || backendId === "codex" ? providerQuotaByBackend.get(backendId) : undefined;
|
|
32785
|
+
const dailyUsage = await dailyTokenUsage2.snapshots(info.agentId);
|
|
32786
|
+
return {
|
|
32787
|
+
...info,
|
|
32788
|
+
...dailyUsage.length > 0 ? { dailyUsage } : {},
|
|
32789
|
+
...quota ? { quota: structuredClone(quota) } : {}
|
|
32790
|
+
};
|
|
32791
|
+
};
|
|
32792
|
+
let reportAgentActivity = (info) => {
|
|
32793
|
+
channelRef?.reportAgentActivity?.(info);
|
|
32794
|
+
};
|
|
31192
32795
|
let reminderSchedulerRef = null;
|
|
31193
32796
|
const selfSleepScheduler = opts.onSelfSleep ? new DaemonSelfSleepScheduler({
|
|
31194
32797
|
onSleep: opts.onSelfSleep,
|
|
@@ -31253,7 +32856,7 @@ async function createDaemon(opts) {
|
|
|
31253
32856
|
function reassertAgentActivity(agentId) {
|
|
31254
32857
|
const state = managerRef?.agentActivity(agentId);
|
|
31255
32858
|
if (state)
|
|
31256
|
-
|
|
32859
|
+
reportAgentActivity({ agentId, state });
|
|
31257
32860
|
}
|
|
31258
32861
|
function startTypingHeartbeat(agentId) {
|
|
31259
32862
|
stopTypingHeartbeat(agentId);
|
|
@@ -31397,6 +33000,20 @@ async function createDaemon(opts) {
|
|
|
31397
33000
|
logger: log2.child("ws")
|
|
31398
33001
|
});
|
|
31399
33002
|
channelRef = channel2;
|
|
33003
|
+
const activityReportTails = new Map;
|
|
33004
|
+
reportAgentActivity = (info) => {
|
|
33005
|
+
const prior = activityReportTails.get(info.agentId) ?? Promise.resolve();
|
|
33006
|
+
const next = prior.then(async () => {
|
|
33007
|
+
await channel2.reportAgentActivity(await activityPayload(info));
|
|
33008
|
+
}).catch(() => {
|
|
33009
|
+
log2.warn("agent activity telemetry report failed", { agentId: info.agentId, state: info.state });
|
|
33010
|
+
});
|
|
33011
|
+
activityReportTails.set(info.agentId, next);
|
|
33012
|
+
next.finally(() => {
|
|
33013
|
+
if (activityReportTails.get(info.agentId) === next)
|
|
33014
|
+
activityReportTails.delete(info.agentId);
|
|
33015
|
+
});
|
|
33016
|
+
};
|
|
31400
33017
|
function restorePendingIdleResetEvents(agentId) {
|
|
31401
33018
|
for (const pending of timeline2.pendingIdleResetEvents(agentId)) {
|
|
31402
33019
|
channel2.restorePendingBotAuditEvent({
|
|
@@ -31515,7 +33132,7 @@ async function createDaemon(opts) {
|
|
|
31515
33132
|
onAgentSession: (info) => void channel2.reportAgentSession(info),
|
|
31516
33133
|
onAgentActivity: (info) => {
|
|
31517
33134
|
selfSleepScheduler?.observeAgentActivity(info.agentId, info.state === "running");
|
|
31518
|
-
|
|
33135
|
+
reportAgentActivity(info);
|
|
31519
33136
|
if (info.state === "starting" || info.state === "running") {
|
|
31520
33137
|
if (!typingHeartbeats.has(info.agentId)) {
|
|
31521
33138
|
startTypingHeartbeat(info.agentId);
|
|
@@ -31524,6 +33141,16 @@ async function createDaemon(opts) {
|
|
|
31524
33141
|
emitTypingStopsAndClear(info.agentId);
|
|
31525
33142
|
}
|
|
31526
33143
|
},
|
|
33144
|
+
onTokenUsage: ({ agentId, usage }) => {
|
|
33145
|
+
dailyTokenUsage2.record(agentId, usage).catch(() => {
|
|
33146
|
+
log2.warn("daily token usage persistence failed", { agentId });
|
|
33147
|
+
});
|
|
33148
|
+
},
|
|
33149
|
+
onProviderQuota: ({ backendId, quota }) => {
|
|
33150
|
+
if (backendId !== "claude" && backendId !== "codex")
|
|
33151
|
+
return;
|
|
33152
|
+
recordProviderQuota(backendId, quota);
|
|
33153
|
+
},
|
|
31527
33154
|
onBotAuditEvent: (agentId, event, context) => emitBotAuditEvent(agentId, event, context),
|
|
31528
33155
|
onAgentLocallyStopped: (info) => router?.markLocallyStopped(info.agentId),
|
|
31529
33156
|
onRuntimeRawLine,
|
|
@@ -31570,6 +33197,11 @@ async function createDaemon(opts) {
|
|
|
31570
33197
|
arch: opts.arch,
|
|
31571
33198
|
osRelease: opts.osRelease,
|
|
31572
33199
|
daemonVersion: opts.daemonVersion,
|
|
33200
|
+
providerQuotas: providerQuotaSnapshots,
|
|
33201
|
+
resyncActivities: async () => {
|
|
33202
|
+
const activities = await Promise.all(manager.liveAgentActivities().map((info) => activityPayload(info)));
|
|
33203
|
+
return activities.filter((activity) => manager.agentActivity(activity.agentId) === activity.state);
|
|
33204
|
+
},
|
|
31573
33205
|
typingTracker,
|
|
31574
33206
|
logger: log2.child("router"),
|
|
31575
33207
|
onBeforeAgent: async (agentId) => {
|
|
@@ -31585,6 +33217,10 @@ async function createDaemon(opts) {
|
|
|
31585
33217
|
await enrollAgent(agentId);
|
|
31586
33218
|
}
|
|
31587
33219
|
});
|
|
33220
|
+
requestReadyQuotaResend = () => {
|
|
33221
|
+
if (router)
|
|
33222
|
+
channel2.sendReady?.(router.buildReady());
|
|
33223
|
+
};
|
|
31588
33224
|
channel2.onCommand(createSelfUpdateCommandListener(opts.handleSelfUpdate));
|
|
31589
33225
|
channel2.onCommand(createDiagnosticsCommandListener({
|
|
31590
33226
|
handleDiagnosticCommand: opts.handleDiagnosticCommand,
|
|
@@ -31614,6 +33250,11 @@ async function createDaemon(opts) {
|
|
|
31614
33250
|
resyncPendingWakes();
|
|
31615
33251
|
resyncPendingDiagnostics();
|
|
31616
33252
|
});
|
|
33253
|
+
if (opts.runtimeReport.some((runtime) => runtime.id === "claude")) {
|
|
33254
|
+
const observed = await providerQuotaReader("claude");
|
|
33255
|
+
if (observed)
|
|
33256
|
+
recordProviderQuota("claude", observed);
|
|
33257
|
+
}
|
|
31617
33258
|
channel2.connect();
|
|
31618
33259
|
await router.start();
|
|
31619
33260
|
selfSleepScheduler?.start();
|
|
@@ -32424,7 +34065,7 @@ async function buildDiagnosticBundle(args) {
|
|
|
32424
34065
|
};
|
|
32425
34066
|
}
|
|
32426
34067
|
// src/diagnostics/coordinator.ts
|
|
32427
|
-
import { createHash as createHash4, randomBytes as
|
|
34068
|
+
import { createHash as createHash4, randomBytes as randomBytes7 } from "node:crypto";
|
|
32428
34069
|
import {
|
|
32429
34070
|
chmodSync as chmodSync2,
|
|
32430
34071
|
closeSync as closeSync4,
|
|
@@ -32440,7 +34081,7 @@ import {
|
|
|
32440
34081
|
unlinkSync as unlinkSync6,
|
|
32441
34082
|
writeSync
|
|
32442
34083
|
} from "node:fs";
|
|
32443
|
-
import { join as
|
|
34084
|
+
import { join as join15 } from "node:path";
|
|
32444
34085
|
class CoordinatorError extends Error {
|
|
32445
34086
|
code;
|
|
32446
34087
|
constructor(code) {
|
|
@@ -32450,7 +34091,7 @@ class CoordinatorError extends Error {
|
|
|
32450
34091
|
}
|
|
32451
34092
|
function defaultFsOps() {
|
|
32452
34093
|
return {
|
|
32453
|
-
randomSuffix: () =>
|
|
34094
|
+
randomSuffix: () => randomBytes7(12).toString("hex"),
|
|
32454
34095
|
open: (path11, flags, mode) => openSync4(path11, flags, mode),
|
|
32455
34096
|
write: (fd, bytes) => {
|
|
32456
34097
|
writeSync(fd, bytes);
|
|
@@ -32507,15 +34148,15 @@ function commandFrom(sidecar) {
|
|
|
32507
34148
|
}
|
|
32508
34149
|
function createDiagnosticReportCoordinator(args) {
|
|
32509
34150
|
const fsOps = args.fsOps ?? defaultFsOps();
|
|
32510
|
-
const dir =
|
|
34151
|
+
const dir = join15(args.machineDir, "diagnostics");
|
|
32511
34152
|
let stopped = false;
|
|
32512
34153
|
let active = null;
|
|
32513
34154
|
const retryCancels = new Set;
|
|
32514
34155
|
const checkpoint = (point) => {
|
|
32515
34156
|
args.checkpoint?.(point);
|
|
32516
34157
|
};
|
|
32517
|
-
const archivePath = (reportId) =>
|
|
32518
|
-
const sidecarPath = (reportId) =>
|
|
34158
|
+
const archivePath = (reportId) => join15(dir, `report-${reportId}.ndjson.gz`);
|
|
34159
|
+
const sidecarPath = (reportId) => join15(dir, `report-${reportId}.json`);
|
|
32519
34160
|
const ensureDir = () => {
|
|
32520
34161
|
if (existsSync7(dir)) {
|
|
32521
34162
|
const stat = lstatSync5(dir);
|
|
@@ -32549,7 +34190,7 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
32549
34190
|
let temp = "";
|
|
32550
34191
|
let fd = null;
|
|
32551
34192
|
for (let attempt = 0;attempt < 32; attempt += 1) {
|
|
32552
|
-
temp =
|
|
34193
|
+
temp = join15(dir, `.${sidecar.reportId}.${sidecar.phase}.${fsOps.randomSuffix()}.tmp`);
|
|
32553
34194
|
try {
|
|
32554
34195
|
fd = fsOps.open(temp, "wx", 384);
|
|
32555
34196
|
break;
|
|
@@ -32666,7 +34307,7 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
32666
34307
|
return ready;
|
|
32667
34308
|
};
|
|
32668
34309
|
const buildAndCommit = async (command, collecting) => {
|
|
32669
|
-
const temp =
|
|
34310
|
+
const temp = join15(dir, `.${command.reportId}.archive.${fsOps.randomSuffix()}.tmp`);
|
|
32670
34311
|
const artifact2 = await args.buildBundle({ command, outputPath: temp });
|
|
32671
34312
|
checkpoint("archive_temp_written");
|
|
32672
34313
|
const fd = fsOps.open(artifact2.path, "r+");
|
|
@@ -32807,11 +34448,11 @@ async function strictOutcome(response, allowed) {
|
|
|
32807
34448
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
32808
34449
|
return { kind: "retryable" };
|
|
32809
34450
|
}
|
|
32810
|
-
const
|
|
32811
|
-
if (Object.keys(
|
|
34451
|
+
const record5 = value;
|
|
34452
|
+
if (Object.keys(record5).length !== 2 || record5.kind !== "terminal") {
|
|
32812
34453
|
return { kind: "retryable" };
|
|
32813
34454
|
}
|
|
32814
|
-
const status =
|
|
34455
|
+
const status = record5.status;
|
|
32815
34456
|
if (status !== "uploaded" && status !== "failed" || allowed[status] !== response.status) {
|
|
32816
34457
|
return { kind: "retryable" };
|
|
32817
34458
|
}
|
|
@@ -33305,7 +34946,7 @@ function createDaemonProcessLogger(daemonDir, foreground) {
|
|
|
33305
34946
|
`) : quiet,
|
|
33306
34947
|
err: foreground ? (line2) => process.stderr.write(line2 + `
|
|
33307
34948
|
`) : quiet,
|
|
33308
|
-
record: (
|
|
34949
|
+
record: (record5) => sink.write(JSON.stringify(record5))
|
|
33309
34950
|
});
|
|
33310
34951
|
return { logger, logPath, sink };
|
|
33311
34952
|
}
|
|
@@ -33514,7 +35155,7 @@ var LEGACY_DAEMON_ID_PATTERN = /^[a-f0-9]{12}$/;
|
|
|
33514
35155
|
var DEFAULT_SERVER_URL = "https://alook.ai";
|
|
33515
35156
|
var DEFAULT_WS_URL = "wss://alook.ai/api/ws/community-daemon";
|
|
33516
35157
|
function resolveDefaultBaseDir() {
|
|
33517
|
-
const root = process.env.ALOOK_PROJECT_ROOT || path13.join(
|
|
35158
|
+
const root = process.env.ALOOK_PROJECT_ROOT || path13.join(homedir6(), ".alook");
|
|
33518
35159
|
return path13.join(root, "daemon");
|
|
33519
35160
|
}
|
|
33520
35161
|
var DEFAULT_BASE_DIR = resolveDefaultBaseDir();
|
|
@@ -34048,18 +35689,18 @@ function readCredentialFile(filePath) {
|
|
|
34048
35689
|
} catch {}
|
|
34049
35690
|
return null;
|
|
34050
35691
|
}
|
|
34051
|
-
function writeCredentialFile(filePath,
|
|
34052
|
-
writePrivateJsonAtomic2(filePath,
|
|
35692
|
+
function writeCredentialFile(filePath, record5) {
|
|
35693
|
+
writePrivateJsonAtomic2(filePath, record5);
|
|
34053
35694
|
}
|
|
34054
35695
|
function readDaemonLaunchRecord(baseDir, machineId) {
|
|
34055
|
-
const
|
|
34056
|
-
if (!
|
|
35696
|
+
const record5 = readCredentialFile(credentialFilePathByMachineId(baseDir, machineId));
|
|
35697
|
+
if (!record5 || !("schemaVersion" in record5) || record5.schemaVersion !== 1 || !parseReleaseVersion(record5.daemonVersion)) {
|
|
34057
35698
|
throw new Error("daemon launch record is missing or requires a manual start upgrade");
|
|
34058
35699
|
}
|
|
34059
|
-
validateMachineId(
|
|
34060
|
-
if (
|
|
35700
|
+
validateMachineId(record5.machineId);
|
|
35701
|
+
if (record5.machineId !== machineId)
|
|
34061
35702
|
throw new Error("daemon launch record machine mismatch");
|
|
34062
|
-
return
|
|
35703
|
+
return record5;
|
|
34063
35704
|
}
|
|
34064
35705
|
function findExistingCredentialForBearer(baseDir, bearer) {
|
|
34065
35706
|
const dir = daemonsDir(baseDir);
|
|
@@ -34224,23 +35865,23 @@ async function daemonResume(opts) {
|
|
|
34224
35865
|
if (!/^[A-Za-z0-9_-]{16,128}$/.test(opts.requestId))
|
|
34225
35866
|
throw new Error("invalid replacement request id");
|
|
34226
35867
|
const baseDir = opts.baseDir || process.env.ALOOK_DATA_DIR || DEFAULT_BASE_DIR;
|
|
34227
|
-
const
|
|
35868
|
+
const record5 = readDaemonLaunchRecord(baseDir, opts.id);
|
|
34228
35869
|
if (false) {}
|
|
34229
35870
|
await daemonStart({
|
|
34230
|
-
machineKey:
|
|
34231
|
-
serverUrl:
|
|
34232
|
-
wsUrl:
|
|
35871
|
+
machineKey: record5.credential,
|
|
35872
|
+
serverUrl: record5.serverUrl,
|
|
35873
|
+
wsUrl: record5.wsUrl,
|
|
34233
35874
|
baseDir,
|
|
34234
35875
|
resumeRequestId: opts.requestId
|
|
34235
35876
|
});
|
|
34236
35877
|
}
|
|
34237
35878
|
async function daemonStartById(opts) {
|
|
34238
35879
|
const baseDir = opts.baseDir || process.env.ALOOK_DATA_DIR || DEFAULT_BASE_DIR;
|
|
34239
|
-
const
|
|
35880
|
+
const record5 = readDaemonLaunchRecord(baseDir, opts.id);
|
|
34240
35881
|
await daemonStart({
|
|
34241
|
-
machineKey:
|
|
34242
|
-
serverUrl:
|
|
34243
|
-
wsUrl:
|
|
35882
|
+
machineKey: record5.credential,
|
|
35883
|
+
serverUrl: record5.serverUrl,
|
|
35884
|
+
wsUrl: record5.wsUrl,
|
|
34244
35885
|
baseDir,
|
|
34245
35886
|
foreground: opts.foreground
|
|
34246
35887
|
});
|
|
@@ -34567,6 +36208,61 @@ async function armMessageReminderFromEnv(input, env = process.env, fetchImpl = f
|
|
|
34567
36208
|
return { armed: false, reason: "local reminder returned an invalid response" };
|
|
34568
36209
|
}
|
|
34569
36210
|
|
|
36211
|
+
// src/cli/imageThumbnail.ts
|
|
36212
|
+
var RASTER_CONTENT_TYPES = new Set([
|
|
36213
|
+
"image/png",
|
|
36214
|
+
"image/jpeg",
|
|
36215
|
+
"image/webp",
|
|
36216
|
+
"image/gif"
|
|
36217
|
+
]);
|
|
36218
|
+
var QUALITIES = [80, 70, 60, 50, 40, 30, 20];
|
|
36219
|
+
var DIMENSION_ATTEMPTS = 10;
|
|
36220
|
+
var DIMENSION_SCALE = 0.85;
|
|
36221
|
+
async function prepareCommunityImageUpload(bytes, contentType) {
|
|
36222
|
+
if (!RASTER_CONTENT_TYPES.has(contentType))
|
|
36223
|
+
return {};
|
|
36224
|
+
let required2 = bytes.byteLength > MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES;
|
|
36225
|
+
try {
|
|
36226
|
+
const { default: sharp } = await import("sharp");
|
|
36227
|
+
const metadata = await sharp(bytes, { failOn: "error" }).metadata();
|
|
36228
|
+
const width = metadata.width;
|
|
36229
|
+
const height = metadata.height;
|
|
36230
|
+
if (!width || !height) {
|
|
36231
|
+
if (required2)
|
|
36232
|
+
throw new Error("missing image dimensions");
|
|
36233
|
+
return {};
|
|
36234
|
+
}
|
|
36235
|
+
required2 = required2 || Math.max(width, height) > MAX_ATTACHMENT_THUMBNAIL_EDGE_PX;
|
|
36236
|
+
if (!required2)
|
|
36237
|
+
return { width, height };
|
|
36238
|
+
const fittedScale = Math.min(1, MAX_ATTACHMENT_THUMBNAIL_EDGE_PX / Math.max(width, height));
|
|
36239
|
+
const fittedWidth = Math.max(1, Math.round(width * fittedScale));
|
|
36240
|
+
const fittedHeight = Math.max(1, Math.round(height * fittedScale));
|
|
36241
|
+
for (let attempt = 0;attempt < DIMENSION_ATTEMPTS; attempt++) {
|
|
36242
|
+
const scale = DIMENSION_SCALE ** attempt;
|
|
36243
|
+
const targetWidth = Math.max(1, Math.round(fittedWidth * scale));
|
|
36244
|
+
const targetHeight = Math.max(1, Math.round(fittedHeight * scale));
|
|
36245
|
+
for (const quality of QUALITIES) {
|
|
36246
|
+
const jpeg = await sharp(bytes, { failOn: "error" }).resize({
|
|
36247
|
+
width: targetWidth,
|
|
36248
|
+
height: targetHeight,
|
|
36249
|
+
fit: "inside",
|
|
36250
|
+
withoutEnlargement: true
|
|
36251
|
+
}).jpeg({ quality }).toBuffer();
|
|
36252
|
+
if (jpeg.byteLength <= MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES) {
|
|
36253
|
+
return { thumbnail: new Uint8Array(jpeg), width, height };
|
|
36254
|
+
}
|
|
36255
|
+
}
|
|
36256
|
+
}
|
|
36257
|
+
throw new Error("no policy-compliant JPEG candidate");
|
|
36258
|
+
} catch {
|
|
36259
|
+
if (required2) {
|
|
36260
|
+
throw new Error("could not generate a required image preview");
|
|
36261
|
+
}
|
|
36262
|
+
return {};
|
|
36263
|
+
}
|
|
36264
|
+
}
|
|
36265
|
+
|
|
34570
36266
|
// src/cli/index.ts
|
|
34571
36267
|
function messagesInLocalTime(messages) {
|
|
34572
36268
|
return messages.map((m) => ({ ...m, time: toLocalISO(m.time) }));
|
|
@@ -34773,7 +36469,7 @@ ${MESSAGE_SEND_STDIN_POLICY}`);
|
|
|
34773
36469
|
}
|
|
34774
36470
|
replyToSeq = n;
|
|
34775
36471
|
}
|
|
34776
|
-
const nonce =
|
|
36472
|
+
const nonce = randomUUID10();
|
|
34777
36473
|
const res = await sendWithRetry(api2, {
|
|
34778
36474
|
agentId: agent2,
|
|
34779
36475
|
channel: channel2,
|
|
@@ -34884,22 +36580,19 @@ async function cmdAttachmentUpload(opts) {
|
|
|
34884
36580
|
let height;
|
|
34885
36581
|
if (["image/png", "image/jpeg", "image/webp", "image/gif"].includes(contentType)) {
|
|
34886
36582
|
try {
|
|
34887
|
-
const
|
|
34888
|
-
|
|
34889
|
-
|
|
34890
|
-
if (
|
|
34891
|
-
width = metadata.width;
|
|
34892
|
-
height = metadata.height;
|
|
34893
|
-
}
|
|
34894
|
-
const jpeg = await image.resize({ width: 200, height: 200, fit: "inside", withoutEnlargement: true }).jpeg({ quality: 70 }).toBuffer();
|
|
34895
|
-
if (jpeg.byteLength <= MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES) {
|
|
36583
|
+
const prepared = await prepareCommunityImageUpload(bytes, contentType);
|
|
36584
|
+
width = prepared.width;
|
|
36585
|
+
height = prepared.height;
|
|
36586
|
+
if (prepared.thumbnail) {
|
|
34896
36587
|
thumbnail = {
|
|
34897
|
-
data:
|
|
36588
|
+
data: prepared.thumbnail,
|
|
34898
36589
|
filename: "thumbnail.jpg",
|
|
34899
36590
|
contentType: "image/jpeg"
|
|
34900
36591
|
};
|
|
34901
36592
|
}
|
|
34902
|
-
} catch {
|
|
36593
|
+
} catch (error51) {
|
|
36594
|
+
throw new CliError(`message attachment upload: ${error51.message}`);
|
|
36595
|
+
}
|
|
34903
36596
|
}
|
|
34904
36597
|
const result = await api2.attachmentUpload({
|
|
34905
36598
|
agentId: agent2,
|