@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.
Files changed (3) hide show
  1. package/dist/cli/index.js +2018 -325
  2. package/dist/index.js +1964 -323
  3. 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 randomUUID9 } from "node:crypto";
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 MAX_ATTACHMENT_THUMBNAIL_SIZE_BYTES = 50 * 1024;
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
- }).refine((v) => v.name !== undefined || v.description !== undefined || v.image !== undefined || v.runtime !== undefined || ("model" in v), {
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 homedir5 } from "os";
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 (normalized.model.kind === "custom" && normalized.provider?.kind === "custom_endpoint") {
20132
- providerEnv.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
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 && event?.total_cost_usd == null)
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
- usageKind: "per_turn",
20451
- attrs: {
20452
- inputTokens: u?.input_tokens,
20453
- outputTokens: u?.output_tokens,
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
- if (!explicit)
20646
- return probeClaude();
20647
- const result = probeCommandVersion(explicit);
20648
- return result.ok ? { status: "healthy", version: result.version } : { status: "unhealthy", lastError: result.error };
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 mapCodexTelemetry(method, params) {
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?.usage ?? params ?? {};
20697
- return [
20698
- {
20699
- kind: "telemetry",
20700
- name: "token_usage",
20701
- source: "codex_thread_token_usage_updated",
20702
- usageKind: "cumulative_session",
20703
- attrs: {
20704
- totalTokens: u.totalTokens ?? u.total_tokens,
20705
- inputTokens: u.inputTokens ?? u.input_tokens,
20706
- cachedInputTokens: u.cachedInputTokens ?? u.cached_input_tokens,
20707
- outputTokens: u.outputTokens ?? u.output_tokens,
20708
- reasoningOutputTokens: u.reasoningOutputTokens ?? u.reasoning_output_tokens,
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
- const r = params ?? {};
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 mapCodexTelemetry(method, params);
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
- return probeCliRuntime("codex", {}, command);
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 record2(value) {
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 = record2(error51);
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 = record2(payload?.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 = record2(await this.call("initialize", {
21285
- protocolVersion: ACP_PROTOCOL_VERSION,
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 !== ACP_PROTOCOL_VERSION) {
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 = record2(initialize.agentCapabilities);
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) => record2(method)?.id === AUTH_METHOD_ID)) {
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: AUTH_METHOD_ID });
22242
+ await this.call("authenticate", { methodId: AUTH_METHOD_ID2 });
21304
22243
  let session2;
21305
22244
  if (ctx.config.sessionId) {
21306
22245
  try {
21307
- session2 = record2(await this.call("session/load", {
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 = record2(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
22258
+ session2 = record3(await this.call("session/new", { cwd: ctx.workingDirectory, mcpServers: [] }));
21320
22259
  }
21321
- if (!session2 || typeof session2.sessionId !== "string" || !session2.sessionId.trim()) {
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 && session2.sessionId !== ctx.config.sessionId) {
21325
- throw new CursorAcpResetRequiredError("Cursor ACP loaded a different session; reset this agent before continuing");
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(record2).find((option) => option?.id === "model") ?? null;
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 = flattenSelectOptions(modelConfig.options);
21340
- const match = options.find((option) => option.value === requestedModel) ?? options.find((option) => option.name === 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 = record2(value);
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 = record2(value);
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 = record2(message2.error);
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 = record2(message2.error);
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 = record2(params);
22555
+ const payload = record3(params);
21610
22556
  const sameSession = payload?.sessionId === this.sessionId;
21611
- const options = Array.isArray(payload?.options) ? payload.options.map(record2).filter(Boolean) : [];
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 = record2(params);
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 = record2(payload.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 = record2(update.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 = record2(update.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
- probe(command) {
21733
- return probeCliRuntime("cursor-agent", {}, command);
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 randomBytes2 } from "node:crypto";
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 record3(value) {
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 = record3(value);
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 ?? randomBytes(32).toString("base64url");
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 = record3(body);
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 = record3(body);
22193
- const paths = record3(document?.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) => !record3(paths[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 = record3(record3(body2)?.data);
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 = record3(responseBody);
22241
- const session2 = record3(payload?.data);
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 = record3(responseBody);
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 = record3(value);
22417
- const durable = record3(event?.durable);
22418
- const data = record3(event?.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 = record3(data.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
- attrs: tokens
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 = record3(value);
22530
- const data = record3(event?.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 = record3(responseBody);
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 = record3(item);
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 = record3(responseBody);
22616
- const admitted = record3(body?.data);
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 = record3(responseBody);
22684
- const active = record3(body?.data);
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_${randomBytes(16).toString("hex")}`;
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_${randomBytes2(16).toString("hex")}`;
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
- return probeCliRuntime("opencode", {}, command);
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 = readPiSdkVersion();
24260
+ async probe() {
24261
+ const version3 = this.readVersion();
23275
24262
  if (!version3) {
23276
24263
  return { status: "unhealthy", lastError: "sdk_not_installed" };
23277
24264
  }
23278
- return { status: "healthy", version: version3 };
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: {}, details });
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, details });
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 { status: "healthy", version: result.version, capabilities: capabilities2 };
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({ id, status: "healthy", version: probe.version });
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 record4 = options.record;
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
- record4?.({ time: time3, header, level, message: message2, fields: recordFields(data) });
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 homedir4 } from "os";
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 { ready, sessions, activities } = this.resyncProvider();
25919
- this.sendFrame({ type: "ready", ...ready });
25920
- for (const s of sessions)
25921
- this.sendFrame({ type: "agent_session", ...s });
25922
- const liveActivities = activities ?? [];
25923
- for (const a of liveActivities)
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
- this.log.info("resync sent", {
25929
- ready: ready.runtimeReport.length,
25930
- sessions: sessions.length,
25931
- activities: liveActivities.length,
25932
- pendingAuditEvents: this.pendingBotAuditEvents.size
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 record4 = value;
26318
- if (Object.keys(record4).sort().join(",") !== "channel,remindAfterMs,sentSeq")
27343
+ const record5 = value;
27344
+ if (Object.keys(record5).sort().join(",") !== "channel,remindAfterMs,sentSeq")
26319
27345
  return null;
26320
- if (typeof record4.channel !== "string" || !isCanonicalChannelScope(record4.channel))
27346
+ if (typeof record5.channel !== "string" || !isCanonicalChannelScope(record5.channel))
26321
27347
  return null;
26322
- if (!Number.isSafeInteger(record4.sentSeq) || record4.sentSeq < 1)
27348
+ if (!Number.isSafeInteger(record5.sentSeq) || record5.sentSeq < 1)
26323
27349
  return null;
26324
- if (!Number.isSafeInteger(record4.remindAfterMs) || record4.remindAfterMs !== 0 && record4.remindAfterMs < LOCAL_MESSAGE_REMINDER_MIN_MS || record4.remindAfterMs > LOCAL_MESSAGE_REMINDER_MAX_MS)
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: record4.channel,
26329
- sentSeq: record4.sentSeq,
26330
- remindAfterMs: record4.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 record4 = existing.pendingAdmissions.find((entry) => entry.sessionInstanceId === event.sessionInstanceId && entry.commandId === event.commandId);
26677
- if (!record4)
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, [record4]) : []);
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((record4) => record4.requeueOnFailure).map((record4) => ({
28226
+ return records.filter((record5) => record5.requeueOnFailure).map((record5) => ({
27178
28227
  type: "requeue_delivery",
27179
28228
  agentId: agent2.agentId,
27180
- message: record4.exactAgentMsg,
27181
- mode: record4.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
- if (launch?.runtimeConfig)
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, { runtimeConfig: opts.runtimeConfig, launchId: opts.launchId });
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
- this.dispatch(marker !== undefined ? {
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
- }, owner);
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
- this.runtimes.set(id, {
29183
- id: existing.id,
29184
- version: existing.version,
29185
- status: "healthy"
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 randomBytes4 } from "node:crypto";
29421
- import { basename as basename3, dirname as dirname5, join as join11 } from "node:path";
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 = join11(dirname5(filePath), `.${basename3(filePath)}.${process.pid}.${randomBytes4(12).toString("hex")}.tmp`);
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(join11(timelineDir, filename)));
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 = join11(timelineDir, RESUME_CONTROL_FILENAME);
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 = join11(timelineDir, RESUME_CONTROL_FILENAME);
29932
- const tempPath = join11(timelineDir, `.${RESUME_CONTROL_FILENAME}.${process.pid}.${randomBytes4(12).toString("hex")}.tmp`);
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 = join11(timelineDir, filename);
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 = join11(timelineDir, handle.filename);
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 = join11(workingDirectoryBase, agentName);
31439
+ const agentDir = join12(workingDirectoryBase, agentName);
30050
31440
  if (!isRealDirectory(agentDir))
30051
31441
  continue;
30052
- const timelineDir = join11(agentDir, ".context_timeline");
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 = join11(timelineDir, filename);
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 record4 = {
32136
+ const record5 = {
30747
32137
  ...input,
30748
32138
  sentRef,
30749
32139
  startedAt,
30750
32140
  dueAt,
30751
32141
  timer: undefined
30752
32142
  };
30753
- record4.timer = this.setTimer(() => {
30754
- if (this.reminders.get(key) !== record4)
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
- record4.timer.unref?.();
30766
- this.reminders.set(key, record4);
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 homedir3 } from "node:os";
30807
- import { join as join12 } from "node:path";
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 ?? join12(homedir3(), ".alook"),
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 || `${homedir4()}/.alook`) + "/daemon";
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
- channel2.reportAgentActivity?.({ agentId, state });
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
- channel2.reportAgentActivity?.(info);
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 randomBytes5 } from "node:crypto";
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 join13 } from "node:path";
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: () => randomBytes5(12).toString("hex"),
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 = join13(args.machineDir, "diagnostics");
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) => join13(dir, `report-${reportId}.ndjson.gz`);
32518
- const sidecarPath = (reportId) => join13(dir, `report-${reportId}.json`);
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 = join13(dir, `.${sidecar.reportId}.${sidecar.phase}.${fsOps.randomSuffix()}.tmp`);
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 = join13(dir, `.${command.reportId}.archive.${fsOps.randomSuffix()}.tmp`);
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 record4 = value;
32811
- if (Object.keys(record4).length !== 2 || record4.kind !== "terminal") {
34451
+ const record5 = value;
34452
+ if (Object.keys(record5).length !== 2 || record5.kind !== "terminal") {
32812
34453
  return { kind: "retryable" };
32813
34454
  }
32814
- const status = record4.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: (record4) => sink.write(JSON.stringify(record4))
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(homedir5(), ".alook");
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, record4) {
34052
- writePrivateJsonAtomic2(filePath, record4);
35692
+ function writeCredentialFile(filePath, record5) {
35693
+ writePrivateJsonAtomic2(filePath, record5);
34053
35694
  }
34054
35695
  function readDaemonLaunchRecord(baseDir, machineId) {
34055
- const record4 = readCredentialFile(credentialFilePathByMachineId(baseDir, machineId));
34056
- if (!record4 || !("schemaVersion" in record4) || record4.schemaVersion !== 1 || !parseReleaseVersion(record4.daemonVersion)) {
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(record4.machineId);
34060
- if (record4.machineId !== machineId)
35700
+ validateMachineId(record5.machineId);
35701
+ if (record5.machineId !== machineId)
34061
35702
  throw new Error("daemon launch record machine mismatch");
34062
- return record4;
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 record4 = readDaemonLaunchRecord(baseDir, opts.id);
35868
+ const record5 = readDaemonLaunchRecord(baseDir, opts.id);
34228
35869
  if (false) {}
34229
35870
  await daemonStart({
34230
- machineKey: record4.credential,
34231
- serverUrl: record4.serverUrl,
34232
- wsUrl: record4.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 record4 = readDaemonLaunchRecord(baseDir, opts.id);
35880
+ const record5 = readDaemonLaunchRecord(baseDir, opts.id);
34240
35881
  await daemonStart({
34241
- machineKey: record4.credential,
34242
- serverUrl: record4.serverUrl,
34243
- wsUrl: record4.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 = randomUUID9();
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 { default: sharp } = await import("sharp");
34888
- const image = sharp(bytes, { failOn: "error" });
34889
- const metadata = await image.metadata();
34890
- if (metadata.width && metadata.height) {
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: new Uint8Array(jpeg),
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,