@codexhost/cli-win32-arm64 0.13.2 → 0.14.0

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 (34) hide show
  1. package/README.md +1 -1
  2. package/app/codexhost-distribution.json +1 -1
  3. package/app/console-server.mjs +94 -8
  4. package/app/console-web.js +537 -146
  5. package/app/desktop-controller.mjs +108 -12
  6. package/app/host-runtime.mjs +847 -235
  7. package/app/plugins/antigravity/plugin.mjs +143 -20
  8. package/app/plugins/claude-code/plugin.mjs +601 -71
  9. package/app/plugins/codebuddy/plugin.mjs +227 -30
  10. package/app/plugins/codex-usage/plugin.mjs +212 -77
  11. package/app/plugins/copilot/assets/icon.png +0 -0
  12. package/app/plugins/copilot/manifest.json +26 -0
  13. package/app/plugins/copilot/plugin.mjs +34546 -0
  14. package/app/plugins/cursor-cli/plugin.mjs +20805 -44862
  15. package/app/plugins/deepseek-harness/manifest.json +2 -2
  16. package/app/plugins/deepseek-harness/plugin.mjs +202 -21
  17. package/app/plugins/enabled.json +1 -0
  18. package/app/plugins/grok/plugin.mjs +278 -29
  19. package/app/plugins/hermes/plugin.mjs +143 -12
  20. package/app/plugins/kimi-code/plugin.mjs +140 -16
  21. package/app/plugins/kiro-cli/plugin.mjs +121 -10
  22. package/app/plugins/omp/plugin.mjs +2200 -2002
  23. package/app/plugins/opencode/plugin.mjs +137 -16
  24. package/app/plugins/pi/plugin.mjs +2563 -2282
  25. package/app/plugins/qoder/plugin.mjs +133 -14
  26. package/app/plugins/qoder-cn/plugin.mjs +133 -14
  27. package/app/plugins/workbuddy/plugin.mjs +603 -56
  28. package/app/plugins/zcode/plugin.mjs +133 -14
  29. package/app/renderer-extension.js +1171 -529
  30. package/bin/codexhost.exe +0 -0
  31. package/libexec/codexhost-node-repl.exe +0 -0
  32. package/libexec/codexhost-shim.exe +0 -0
  33. package/libexec/codexhost-updater.exe +0 -0
  34. package/package.json +1 -1
@@ -3716,8 +3716,10 @@ List Harnesses available in the active Runtime.`,
3716
3716
  "harness inspect": `codexhost harness inspect <harness> [--cwd <path>] [--refresh true|false] [--format json|compact]
3717
3717
  Read available Models, native defaults, Thinking options, and configuration capabilities.
3718
3718
  Use the returned Model and Thinking IDs for explicit selections.`,
3719
- "delegate start": `codexhost delegate start --harness <id> --task <text> [--cwd <path>] [--model <opaque-ref>] [--thinking <option-id>] [--parent-thread <thread>] [--request-id <id>] [--watch true|false] [--watch-timeout-ms <n>] [--format json|compact]
3719
+ "delegate start": `codexhost delegate start --harness <id> --task <text> [--task-name <name>] [--cwd <path>] [--model <opaque-ref>] [--thinking <option-id>] [--parent-thread <thread>] [--request-id <id>] [--watch true|false] [--watch-timeout-ms <n>] [--format json|compact]
3720
3720
  Create an independent child Thread, submit the task, and return immediately.
3721
+ --task-name supplies a short subtask name (1-120 characters); otherwise the first task line is used.
3722
+ The child title is "subtask name · parent title", fixed at creation; renaming the parent does not rename existing children.
3721
3723
  Omit --model and --thinking to use the Harness native defaults.
3722
3724
  --cwd overrides the child workspace. Otherwise use the resolved parent Thread workspace, then the Host Runtime process cwd.
3723
3725
  --parent-thread overrides caller inference. PARENT_THREAD_AMBIGUOUS requires an explicit parent.
@@ -4161,6 +4163,7 @@ async function runDelegationCli(input) {
4161
4163
  rejectUnknown(parsed2, [
4162
4164
  "--harness",
4163
4165
  "--task",
4166
+ "--task-name",
4164
4167
  "--cwd",
4165
4168
  "--model",
4166
4169
  "--thinking",
@@ -4195,6 +4198,7 @@ async function runDelegationCli(input) {
4195
4198
  body: {
4196
4199
  harnessId,
4197
4200
  task,
4201
+ ...value(parsed2, "--task-name") !== void 0 ? { taskName: value(parsed2, "--task-name") } : {},
4198
4202
  ...value(parsed2, "--cwd") ? { cwd: value(parsed2, "--cwd") } : {},
4199
4203
  ...value(parsed2, "--model") ? { model: { id: value(parsed2, "--model") } } : {},
4200
4204
  ...value(parsed2, "--thinking") ? { thinkingOptionId: value(parsed2, "--thinking") } : {},
@@ -20027,6 +20031,15 @@ var harnessDisplaySetSchema = external_exports.strictObject({
20027
20031
  initializeOnly: external_exports.boolean().optional()
20028
20032
  });
20029
20033
 
20034
+ // packages/shared-contracts/dist/harness-presentation.js
20035
+ var localeSchema = external_exports.string().max(64).regex(/^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/u);
20036
+ var displayText = (maximum) => external_exports.string().min(1).max(maximum).refine((text) => text.trim().length > 0);
20037
+ var harnessLocalizationsSchema = external_exports.record(localeSchema, external_exports.object({
20038
+ label: displayText(256).optional(),
20039
+ description: displayText(1024).optional()
20040
+ }).strict()).refine((entries) => Object.keys(entries).length <= 16, "Too many locales");
20041
+ var harnessLocalizedTextSchema = external_exports.record(localeSchema, displayText(4096)).refine((entries) => typeof entries.en === "string", "English fallback is required").refine((entries) => Object.keys(entries).length <= 16, "Too many locales");
20042
+
20030
20043
  // packages/shared-contracts/dist/model-price-overrides.js
20031
20044
  var MODEL_PRICE_OVERRIDES_GET_METHOD = "codexhost/usage/model-prices/get";
20032
20045
  var MODEL_PRICE_OVERRIDES_SET_METHOD = "codexhost/usage/model-prices/set";
@@ -20080,10 +20093,19 @@ var model = external_exports.string().min(1).max(512);
20080
20093
  var project = external_exports.string().min(1).max(4096);
20081
20094
  var usageStatisticsParamsSchema = external_exports.strictObject({
20082
20095
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
20096
+ /** Display timezone for ranges, dates and hourly buckets; omitted means the Host's local zone. */
20097
+ timeZone: external_exports.string().min(1).max(128).refine((value2) => {
20098
+ try {
20099
+ new Intl.DateTimeFormat("en", { timeZone: value2 });
20100
+ return true;
20101
+ } catch {
20102
+ return false;
20103
+ }
20104
+ }, "Unsupported usage statistics timezone").optional(),
20083
20105
  harness: harness.optional(),
20084
20106
  model: model.nullable().optional(),
20085
20107
  project: project.nullable().optional(),
20086
- /** One local date inside the range. */
20108
+ /** One date in the query timezone inside the range. */
20087
20109
  date: localDate.optional()
20088
20110
  });
20089
20111
  var usageStatisticsTotalsSchema = external_exports.strictObject({
@@ -20119,7 +20141,7 @@ var usageStatisticsSessionSchema = external_exports.strictObject({
20119
20141
  });
20120
20142
  var usageStatisticsResultSchema = external_exports.strictObject({
20121
20143
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
20122
- /** First local date of the range; null for all time. */
20144
+ /** First date in the query timezone (Host local when omitted); null for all time. */
20123
20145
  from: localDate.nullable(),
20124
20146
  to: localDate,
20125
20147
  /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
@@ -20159,7 +20181,7 @@ var usageStatisticsResultSchema = external_exports.strictObject({
20159
20181
  totals: usageStatisticsTotalsSchema,
20160
20182
  /** Each date and Harness of the range: every filter except the day. */
20161
20183
  daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
20162
- /** Local weekday (0 = Sunday) and hour, all filters applied. */
20184
+ /** Weekday (0 = Sunday) and hour in the query timezone, all filters applied. */
20163
20185
  hourly: external_exports.array(external_exports.strictObject({
20164
20186
  weekday: external_exports.number().int().min(0).max(6),
20165
20187
  hour: external_exports.number().int().min(0).max(23),
@@ -20222,6 +20244,8 @@ var CONSOLE_HOST_METHODS = Object.freeze([
20222
20244
  "codexhost/harness/display-settings/set",
20223
20245
  "codexhost/harness/inspect",
20224
20246
  "codexhost/harness/installation",
20247
+ "codexhost/harness/installation/start",
20248
+ "codexhost/harness/installation/status",
20225
20249
  "codexhost/harness/launch-settings/get",
20226
20250
  "codexhost/harness/launch-settings/set",
20227
20251
  "codexhost/harness/web-ui/open",
@@ -20303,7 +20327,18 @@ var threadManualCompactionStartedSchema = external_exports.strictObject({
20303
20327
  var nonNegativeSafeIntegerSchema = external_exports.number().int().safe().nonnegative();
20304
20328
  var finiteNonNegativeNumberSchema = external_exports.number().finite().nonnegative();
20305
20329
  var cacheHitRatePercentSchema = external_exports.number().finite().min(0).max(100);
20330
+ var indicatorTextSchema = (maximum) => harnessLocalizedTextSchema.refine((entries) => Object.values(entries).every((text) => text.length <= maximum));
20331
+ var composerIndicatorsSchema = external_exports.array(external_exports.object({
20332
+ kind: external_exports.literal("countdown"),
20333
+ id: external_exports.string().min(1).max(64),
20334
+ expiresAtUnix: nonNegativeSafeIntegerSchema,
20335
+ label: indicatorTextSchema(256).optional(),
20336
+ tooltip: indicatorTextSchema(1024).optional()
20337
+ }).strict()).max(2).refine((entries) => new Set(entries.map(({ id: id2 }) => id2)).size === entries.length, {
20338
+ message: "Composer indicator IDs must be unique"
20339
+ });
20306
20340
  var threadUsageSnapshotSchema = external_exports.object({
20341
+ composerIndicators: composerIndicatorsSchema.optional(),
20307
20342
  inputTokens: nonNegativeSafeIntegerSchema.optional(),
20308
20343
  cachedInputTokens: nonNegativeSafeIntegerSchema.optional(),
20309
20344
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
@@ -20376,8 +20411,24 @@ var threadUsageSnapshotSchema = external_exports.object({
20376
20411
  }
20377
20412
  });
20378
20413
  var usagePercentSchema = external_exports.number().finite().min(0).max(100);
20414
+ var accountCreditsPeriodSchema = external_exports.enum([
20415
+ "weekly",
20416
+ "monthly",
20417
+ "five_hour",
20418
+ "seven_day",
20419
+ "unknown"
20420
+ ]);
20421
+ var accountCreditsScopeSchema = external_exports.object({
20422
+ id: external_exports.string().min(1).max(256),
20423
+ label: external_exports.string().min(1).max(256),
20424
+ localizations: harnessLocalizationsSchema.optional()
20425
+ }).strict();
20379
20426
  var accountCreditsProductUsageSchema = external_exports.object({
20380
20427
  product: external_exports.string().min(1),
20428
+ /** Native period and scope, never inferred from the display name. */
20429
+ periodType: accountCreditsPeriodSchema.optional(),
20430
+ scope: accountCreditsScopeSchema.optional(),
20431
+ localizations: harnessLocalizationsSchema.optional(),
20381
20432
  used: external_exports.number().finite().nonnegative().optional(),
20382
20433
  // Zero is a real empty cap, not an absent field.
20383
20434
  limit: external_exports.number().finite().nonnegative().optional(),
@@ -20397,9 +20448,11 @@ var accountCreditsSnapshotSchema = external_exports.object({
20397
20448
  unit: external_exports.string().min(1).max(32).optional(),
20398
20449
  /** Native label when the primary limit is scoped to a model or product group. */
20399
20450
  label: external_exports.string().min(1).optional(),
20451
+ scope: accountCreditsScopeSchema.optional(),
20452
+ localizations: harnessLocalizationsSchema.optional(),
20400
20453
  usedPercent: usagePercentSchema,
20401
20454
  resetsAt: external_exports.string().min(1).optional(),
20402
- periodType: external_exports.enum(["weekly", "monthly", "five_hour", "seven_day", "unknown"]),
20455
+ periodType: accountCreditsPeriodSchema,
20403
20456
  productUsage: external_exports.array(accountCreditsProductUsageSchema).min(1).optional(),
20404
20457
  resetCredits: accountResetCreditsSchema.optional()
20405
20458
  }).strict();
@@ -20410,7 +20463,9 @@ var threadUsageInspectionParamsSchema = external_exports.object({
20410
20463
  var threadUsageInspectionSchema = external_exports.object({
20411
20464
  threadId: hostThreadIdSchema,
20412
20465
  usage: threadUsageSnapshotSchema.nullable(),
20413
- accountCredits: accountCreditsSnapshotSchema.optional()
20466
+ accountCredits: accountCreditsSnapshotSchema.optional(),
20467
+ /** Whether this source can provide cached account quota independently of usage. */
20468
+ accountCreditsSupported: external_exports.boolean().optional()
20414
20469
  }).strict();
20415
20470
 
20416
20471
  // packages/shared-contracts/dist/harness-accounts.js
@@ -20537,7 +20592,8 @@ var harnessPermissionModeSchema = external_exports.object({
20537
20592
  id: harnessPermissionModeIdSchema,
20538
20593
  label: nonBlankTextSchema.max(HARNESS_PERMISSION_MODE_LABEL_MAX_LENGTH),
20539
20594
  description: nonBlankTextSchema.max(HARNESS_PERMISSION_MODE_DESCRIPTION_MAX_LENGTH).optional(),
20540
- dangerous: external_exports.boolean().optional()
20595
+ dangerous: external_exports.boolean().optional(),
20596
+ localizations: harnessLocalizationsSchema.optional()
20541
20597
  }).strict();
20542
20598
  var harnessPermissionModeCatalogSchema = external_exports.object({
20543
20599
  modes: external_exports.array(harnessPermissionModeSchema).min(1).max(HARNESS_PERMISSION_MODE_CATALOG_MAX_LENGTH),
@@ -20698,7 +20754,9 @@ var harnessSessionCapabilitiesSchema = external_exports.object({
20698
20754
  }).strict().optional(),
20699
20755
  autonomousTurns: external_exports.object({
20700
20756
  observe: external_exports.boolean()
20701
- }).strict().optional()
20757
+ }).strict().optional(),
20758
+ /** True only when `turn.steer` delivers input into the active Turn through a native primitive. */
20759
+ steer: external_exports.boolean().optional()
20702
20760
  }).strict();
20703
20761
  var harnessConfigurationStateSchema = external_exports.object({
20704
20762
  effectiveModel: harnessModelRefSchema.optional(),
@@ -20746,7 +20804,9 @@ var harnessInspectionSchema = external_exports.union([
20746
20804
  var harnessInspectParamsSchema = external_exports.object({
20747
20805
  harnessId: harnessIdSchema,
20748
20806
  cwd: nonBlankTextSchema2.max(16384).optional(),
20749
- refresh: external_exports.boolean().optional()
20807
+ refresh: external_exports.boolean().optional(),
20808
+ /** `availability` asks only whether the Harness can run; see `InspectHarnessInput.purpose`. */
20809
+ purpose: external_exports.literal("availability").optional()
20750
20810
  }).strict();
20751
20811
  var harnessWebUiOpenParamsSchema = external_exports.object({
20752
20812
  harnessId: harnessIdSchema
@@ -20829,6 +20889,16 @@ var threadOwnershipListResultSchema = external_exports.object({
20829
20889
  seen.add(thread.threadId);
20830
20890
  }
20831
20891
  });
20892
+ var threadSteeringInspectParamsSchema = external_exports.object({
20893
+ threadId: hostThreadIdSchema,
20894
+ /** The text about to be steered; a Harness command always stops and restarts. */
20895
+ text: external_exports.string().optional()
20896
+ }).strict();
20897
+ var THREAD_STEER_DELIVERY_PARAM = "codexhostSteerDelivery";
20898
+ var threadSteerDeliverySchema = external_exports.enum(["activeTurn", "newTurn"]);
20899
+ var threadSteeringInspectResultSchema = external_exports.object({
20900
+ delivery: external_exports.enum(["official", "activeTurn", "newTurn"])
20901
+ }).strict();
20832
20902
 
20833
20903
  // packages/shared-contracts/dist/harness-plugins.js
20834
20904
  var HARNESS_PLUGIN_API_VERSION = 1;
@@ -20949,6 +21019,8 @@ var harnessLaunchSettingsSchema = external_exports.object({
20949
21019
 
20950
21020
  // packages/shared-contracts/dist/harness-installation.js
20951
21021
  var HARNESS_INSTALLATION_METHOD = "codexhost/harness/installation";
21022
+ var HARNESS_INSTALLATION_START_METHOD = "codexhost/harness/installation/start";
21023
+ var HARNESS_INSTALLATION_STATUS_METHOD = "codexhost/harness/installation/status";
20952
21024
  var harnessInstallationParamsSchema = external_exports.object({
20953
21025
  harnessId: harnessIdSchema,
20954
21026
  action: external_exports.enum(["check", "update", "install"])
@@ -20958,11 +21030,31 @@ var harnessInstallationStateSchema = external_exports.object({
20958
21030
  latestVersion: external_exports.string().min(1).max(128),
20959
21031
  updateAvailable: external_exports.boolean(),
20960
21032
  canUpdate: external_exports.boolean(),
21033
+ /** Defaults to CLI; some Harnesses ship only inside a desktop application. */
21034
+ versionKind: external_exports.enum(["cli", "application"]).optional(),
21035
+ /** Official download opened by the user; does not imply an automatic installation. */
21036
+ manualUpdateUrl: external_exports.url().max(2048).refine((value2) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value2), "Manual updates require a credential-free HTTPS URL").optional(),
20961
21037
  // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
20962
21038
  message: external_exports.string().max(2048).optional(),
20963
21039
  messageCode: external_exports.string().min(1).max(128).optional(),
21040
+ /** Safe plugin-owned explanation, distinct from raw diagnostic message. */
21041
+ notice: harnessLocalizedTextSchema.optional(),
20964
21042
  latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
20965
21043
  }).strict();
21044
+ var harnessInstallationTaskParamsSchema = external_exports.object({ taskId: external_exports.string().uuid() }).strict();
21045
+ var harnessInstallationTaskSchema = external_exports.discriminatedUnion("status", [
21046
+ external_exports.object({ taskId: external_exports.string().uuid(), status: external_exports.literal("running") }).strict(),
21047
+ external_exports.object({
21048
+ taskId: external_exports.string().uuid(),
21049
+ status: external_exports.literal("succeeded"),
21050
+ result: harnessInstallationStateSchema
21051
+ }).strict(),
21052
+ external_exports.object({
21053
+ taskId: external_exports.string().uuid(),
21054
+ status: external_exports.literal("failed"),
21055
+ error: external_exports.object({ code: external_exports.number().int(), message: external_exports.string().min(1).max(2048) }).strict()
21056
+ }).strict()
21057
+ ]);
20966
21058
 
20967
21059
  // packages/shared-contracts/dist/codex-accounts.js
20968
21060
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
@@ -21091,6 +21183,7 @@ var harnessCommandDescriptorSchema = external_exports.object({
21091
21183
  invocation: commandInvocationSchema,
21092
21184
  label: commandLabelSchema,
21093
21185
  description: commandDescriptionSchema.optional(),
21186
+ localizations: harnessLocalizationsSchema.optional(),
21094
21187
  argumentMode: external_exports.enum(["none", "text"]),
21095
21188
  /**
21096
21189
  * Native distinction reported by the Harness. Omitted when the Harness does
@@ -21482,6 +21575,9 @@ var RuntimeMaintenance = class {
21482
21575
  #watch = null;
21483
21576
  #starting = null;
21484
21577
  #startingVersion = null;
21578
+ get #startTimeoutMs() {
21579
+ return this.options.updaterStartTimeoutMs ?? UPDATER_START_TIMEOUT_MS;
21580
+ }
21485
21581
  #ensureWatching() {
21486
21582
  if (this.#watch) return;
21487
21583
  this.#watch = setInterval(() => {
@@ -21578,7 +21674,7 @@ var RuntimeMaintenance = class {
21578
21674
  }
21579
21675
  } else {
21580
21676
  const written = (await stat(this.#statusPath)).mtimeMs;
21581
- alive = Date.now() - written < UPDATER_START_TIMEOUT_MS * 2;
21677
+ alive = Date.now() - written < this.#startTimeoutMs * 2;
21582
21678
  }
21583
21679
  if (!alive) {
21584
21680
  parsed2.phase = "failed";
@@ -21702,7 +21798,7 @@ var RuntimeMaintenance = class {
21702
21798
  if (!Number.isSafeInteger(helperPid) || helperPid <= 0) {
21703
21799
  throw new Error("Remote updater could not be started");
21704
21800
  }
21705
- const deadline = Date.now() + UPDATER_START_TIMEOUT_MS;
21801
+ const deadline = Date.now() + this.#startTimeoutMs;
21706
21802
  while (!await this.#helperReported()) {
21707
21803
  if (Date.now() >= deadline) {
21708
21804
  try {
@@ -22946,9 +23042,13 @@ function requestManagerFromHookState(value2, hostId2) {
22946
23042
  const direct = manager(value2) ?? manager(value2.manager);
22947
23043
  if (direct)
22948
23044
  return direct;
22949
- if (!hostId2 || typeof value2.addManager !== "function" || typeof value2.getForHostId !== "function" || typeof value2.waitForManagerForHostId !== "function")
23045
+ if (!hostId2 || typeof value2.addManager !== "function" || typeof value2.getForHostId !== "function")
22950
23046
  return null;
22951
- return manager(value2.getForHostId(hostId2));
23047
+ try {
23048
+ return manager(value2.getForHostId(hostId2));
23049
+ } catch {
23050
+ return null;
23051
+ }
22952
23052
  }
22953
23053
  function discoverRendererHosts(root, ancestors = committedReactAncestors, managerFrom = requestManagerFromHookState) {
22954
23054
  const editors = [
@@ -22984,7 +23084,7 @@ function discoverRendererHosts(root, ancestors = committedReactAncestors, manage
22984
23084
  const manager = managerFrom(value2);
22985
23085
  if (manager)
22986
23086
  managers.add(manager);
22987
- if (value2 && typeof value2 === "object" && "addManager" in value2 && typeof value2.addManager === "function" && "getForHostId" in value2 && typeof value2.getForHostId === "function" && "waitForManagerForHostId" in value2 && typeof value2.waitForManagerForHostId === "function") {
23087
+ if (value2 && typeof value2 === "object" && "addManager" in value2 && typeof value2.addManager === "function" && "getForHostId" in value2 && typeof value2.getForHostId === "function") {
22988
23088
  registries.add(value2);
22989
23089
  }
22990
23090
  hook = hook.next && typeof hook.next === "object" ? hook.next : null;
@@ -23000,7 +23100,13 @@ function discoverRendererHosts(root, ancestors = committedReactAncestors, manage
23000
23100
  function resolveRendererHostManager(discovery, hostId2, managerFrom = requestManagerFromHookState) {
23001
23101
  if (!hostId2)
23002
23102
  return null;
23003
- const candidates = discovery.registries.length ? discovery.registries.map((registry2) => managerFrom(registry2.getForHostId(hostId2))) : discovery.managers;
23103
+ const candidates = discovery.registries.length ? discovery.registries.flatMap((registry2) => {
23104
+ try {
23105
+ return [managerFrom(registry2.getForHostId(hostId2))];
23106
+ } catch {
23107
+ return [];
23108
+ }
23109
+ }) : discovery.managers;
23004
23110
  const matching = new Set(candidates.filter((manager) => manager && (manager.getHostId?.() ?? manager.requestClient.hostId) === hostId2 && (manager.requestClient.hostId === void 0 || manager.requestClient.hostId === hostId2)));
23005
23111
  return matching.size === 1 ? matching.values().next().value ?? null : null;
23006
23112
  }
@@ -24012,6 +24118,64 @@ async function handleHarnessInstallation(params, adapters) {
24012
24118
  }
24013
24119
  }
24014
24120
 
24121
+ // packages/host-runtime/src/harness-installation-tasks.ts
24122
+ import { randomUUID as randomUUID3 } from "node:crypto";
24123
+ var HarnessInstallationTasks = class {
24124
+ #tasks = /* @__PURE__ */ new Map();
24125
+ #active = /* @__PURE__ */ new Map();
24126
+ #prune() {
24127
+ const cutoff = Date.now() - 15 * 6e4;
24128
+ for (const [id2, task] of this.#tasks) {
24129
+ if (task.finishedAt !== void 0 && task.finishedAt < cutoff) this.#tasks.delete(id2);
24130
+ }
24131
+ }
24132
+ start(params, adapters) {
24133
+ const parsed2 = harnessInstallationParamsSchema.safeParse(params);
24134
+ if (!parsed2.success)
24135
+ throw new HarnessInstallationError(-32602, "Invalid Harness installation request");
24136
+ this.#prune();
24137
+ const key = JSON.stringify([parsed2.data.harnessId, parsed2.data.action]);
24138
+ const activeId = this.#active.get(key);
24139
+ const active = activeId ? this.#tasks.get(activeId) : void 0;
24140
+ if (active) return active.state;
24141
+ const taskId = randomUUID3();
24142
+ const task = {
24143
+ state: { taskId, status: "running" }
24144
+ };
24145
+ this.#tasks.set(taskId, task);
24146
+ this.#active.set(key, taskId);
24147
+ void handleHarnessInstallation(parsed2.data, adapters).then(
24148
+ (result) => {
24149
+ task.state = { taskId, status: "succeeded", result };
24150
+ },
24151
+ (error51) => {
24152
+ task.state = {
24153
+ taskId,
24154
+ status: "failed",
24155
+ error: error51 instanceof HarnessInstallationError ? { code: error51.code, message: error51.message } : { code: -32077, message: "Harness version management failed" }
24156
+ };
24157
+ }
24158
+ ).finally(() => {
24159
+ task.finishedAt = Date.now();
24160
+ this.#active.delete(key);
24161
+ });
24162
+ return task.state;
24163
+ }
24164
+ status(params) {
24165
+ const parsed2 = harnessInstallationTaskParamsSchema.safeParse(params);
24166
+ if (!parsed2.success)
24167
+ throw new HarnessInstallationError(-32602, "Invalid Harness installation task request");
24168
+ this.#prune();
24169
+ const task = this.#tasks.get(parsed2.data.taskId);
24170
+ if (!task)
24171
+ throw new HarnessInstallationError(
24172
+ -32079,
24173
+ "Harness installation task is unavailable on this Host"
24174
+ );
24175
+ return task.state;
24176
+ }
24177
+ };
24178
+
24015
24179
  // packages/host-runtime/src/account/codex-credential-export.ts
24016
24180
  import { createHash as createHash3 } from "node:crypto";
24017
24181
  import { readFile as readFile8 } from "node:fs/promises";
@@ -24104,7 +24268,7 @@ async function listOthers(adapter) {
24104
24268
  }
24105
24269
 
24106
24270
  // packages/host-runtime/src/harness-display-settings.ts
24107
- import { randomUUID as randomUUID3 } from "node:crypto";
24271
+ import { randomUUID as randomUUID4 } from "node:crypto";
24108
24272
  import { link, mkdir as mkdir6, readFile as readFile9, rename as rename4, rm as rm5, writeFile as writeFile6 } from "node:fs/promises";
24109
24273
  import os4 from "node:os";
24110
24274
  import path13 from "node:path";
@@ -24129,7 +24293,7 @@ var HarnessDisplaySettingsStore = class {
24129
24293
  async set(input) {
24130
24294
  const entries = harnessDisplayEntriesSchema.parse(input.entries);
24131
24295
  await mkdir6(path13.dirname(this.#file), { recursive: true, mode: 448 });
24132
- const temporary = `${this.#file}.${randomUUID3()}.tmp`;
24296
+ const temporary = `${this.#file}.${randomUUID4()}.tmp`;
24133
24297
  try {
24134
24298
  await writeFile6(temporary, `${JSON.stringify(entries)}
24135
24299
  `, { mode: 384, flag: "wx" });
@@ -24150,7 +24314,7 @@ var HarnessDisplaySettingsStore = class {
24150
24314
  };
24151
24315
 
24152
24316
  // packages/host-runtime/src/model-price-overrides-file.ts
24153
- import { randomUUID as randomUUID4 } from "node:crypto";
24317
+ import { randomUUID as randomUUID5 } from "node:crypto";
24154
24318
  import { mkdir as mkdir8, readFile as readFile11, rename as rename6, rm as rm6, writeFile as writeFile8 } from "node:fs/promises";
24155
24319
  import path15 from "node:path";
24156
24320
 
@@ -24725,7 +24889,7 @@ async function updateModelPriceOverridesFile(file2, change) {
24725
24889
  const updated = { ...document, models: next };
24726
24890
  parseModelPriceOverrides(updated);
24727
24891
  await mkdir8(path15.dirname(file2), { recursive: true });
24728
- const temporary = `${file2}.${randomUUID4()}.tmp`;
24892
+ const temporary = `${file2}.${randomUUID5()}.tmp`;
24729
24893
  try {
24730
24894
  await writeFile8(temporary, `${JSON.stringify(updated, null, 2)}
24731
24895
  `, { flag: "wx" });
@@ -24761,11 +24925,11 @@ async function handleModelPriceOverridesRequest(catalog, method, params) {
24761
24925
  }
24762
24926
 
24763
24927
  // packages/host-runtime/src/delegation-skill.ts
24764
- import { createHash as createHash4, randomUUID as randomUUID5 } from "node:crypto";
24928
+ import { createHash as createHash4, randomUUID as randomUUID6 } from "node:crypto";
24765
24929
  import { mkdir as mkdir9, open as open4, readFile as readFile12, rename as rename7, rm as rm7, stat as stat5 } from "node:fs/promises";
24766
24930
  import os6 from "node:os";
24767
24931
  import path16 from "node:path";
24768
- var SKILL_VERSION = 9;
24932
+ var SKILL_VERSION = 10;
24769
24933
  var DELEGATION_SKILL_NAME = "codexhost-delegation";
24770
24934
  var SKILL_RELATIVE_PATH = path16.join("skills", DELEGATION_SKILL_NAME, "SKILL.md");
24771
24935
  var PREVIOUS_MANAGED_DIGESTS = [
@@ -24779,7 +24943,9 @@ var PREVIOUS_MANAGED_DIGESTS = [
24779
24943
  "fa7944cd1e72ffbaf932fca2074bdb78aad4670d8990b6711220dd83c39509a0",
24780
24944
  "b9c1cce41d2556e73a6514aa55618d7d3f2f9ef935f5ae50b0da5a04941514b4",
24781
24945
  // v8, which ran a bare `codexhost` from PATH.
24782
- "ca06be59f0d47c9a54d05ed2d9588c9c737306cc155cb4dff3fe2a2a61891dde"
24946
+ "ca06be59f0d47c9a54d05ed2d9588c9c737306cc155cb4dff3fe2a2a61891dde",
24947
+ // v9, before the child-session name guidance.
24948
+ "b72a059e139bd5b93b95f7ac02ab80f428d5a721cc67b004210d5263e75baed7"
24783
24949
  ];
24784
24950
  var CODEXHOST_DELEGATION_SKILL = `---
24785
24951
  name: codexhost-delegation
@@ -24816,7 +24982,8 @@ Use the Harness native defaults. Inspect the target when a Model or Thinking
24816
24982
  selection is needed or the default is unavailable.
24817
24983
 
24818
24984
  For a new delegation, create an independent child session and submit the
24819
- requested task. For an existing external session, resolve the target from the
24985
+ requested task. Give it a short task name that tells it apart from sibling
24986
+ tasks, using the help's task-name option. For an existing external session, resolve the target from the
24820
24987
  user-provided session link, identifier, or context and operate on that Thread
24821
24988
  directly; it need not have been created by the current assistant. If the target
24822
24989
  is ambiguous, ask the user to identify it. Keep requests to view or summarize a
@@ -24858,7 +25025,7 @@ async function readOptional(filePath) {
24858
25025
  }
24859
25026
  async function atomicWrite(filePath, content) {
24860
25027
  await mkdir9(path16.dirname(filePath), { recursive: true, mode: 448 });
24861
- const temporaryPath = path16.join(path16.dirname(filePath), `.SKILL.md.${randomUUID5()}.tmp`);
25028
+ const temporaryPath = path16.join(path16.dirname(filePath), `.SKILL.md.${randomUUID6()}.tmp`);
24862
25029
  const handle = await open4(temporaryPath, "wx", 384);
24863
25030
  try {
24864
25031
  await handle.writeFile(content, "utf8");
@@ -25003,6 +25170,16 @@ function rewriteDelegationMentionInput(input, skill) {
25003
25170
  return rewritten;
25004
25171
  }
25005
25172
 
25173
+ // packages/harness-adapter/dist/account-credits.js
25174
+ function parseHarnessAccountCredits(value2) {
25175
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2))
25176
+ return null;
25177
+ const publicValue = { ...value2 };
25178
+ delete publicValue.fetchedAt;
25179
+ const parsed2 = accountCreditsSnapshotSchema.safeParse(publicValue);
25180
+ return parsed2.success ? parsed2.data : null;
25181
+ }
25182
+
25006
25183
  // packages/harness-adapter/dist/output-channel.js
25007
25184
  var HarnessOutputChannel = class {
25008
25185
  outputs;
@@ -25085,6 +25262,7 @@ var usageFields = /* @__PURE__ */ new Set([
25085
25262
  ...safeIntegerFields,
25086
25263
  ...percentFields,
25087
25264
  "sessionCacheUsage",
25265
+ "composerIndicators",
25088
25266
  "totalCostUsd",
25089
25267
  "totalCredits",
25090
25268
  "contextUsagePercent",
@@ -25107,6 +25285,7 @@ function parseHostUsage(value2) {
25107
25285
  throw new Error(`Harness Usage contains unknown field '${key}'`);
25108
25286
  }
25109
25287
  }
25288
+ const indicators = value2.composerIndicators === void 0 ? {} : { composerIndicators: composerIndicatorsSchema.parse(value2.composerIndicators) };
25110
25289
  for (const field of [
25111
25290
  "totalCredits",
25112
25291
  "contextUsagePercent",
@@ -25177,9 +25356,9 @@ function parseHostUsage(value2) {
25177
25356
  if (value2.costSource !== "publicPrice") {
25178
25357
  throw new Error("Harness Usage 'unpricedModels' must be provided with a 'publicPrice' cost");
25179
25358
  }
25180
- return { ...value2, unpricedModels: [...models] };
25359
+ return { ...value2, ...indicators, unpricedModels: [...models] };
25181
25360
  }
25182
- return { ...value2 };
25361
+ return { ...value2, ...indicators };
25183
25362
  }
25184
25363
  var requestTokenFields = [
25185
25364
  "inputTokens",
@@ -25330,7 +25509,7 @@ var packageMetadata = {
25330
25509
 
25331
25510
  // packages/mapping-store/dist/mapping-store.js
25332
25511
  import { execFileSync } from "node:child_process";
25333
- import { randomUUID as randomUUID8 } from "node:crypto";
25512
+ import { randomUUID as randomUUID9 } from "node:crypto";
25334
25513
  import { constants as constants3 } from "node:fs";
25335
25514
  import { copyFile as copyFile3, mkdir as mkdir11, open as open7, readFile as readFile15, readdir as readdir4, rename as rename10, rm as rm10, stat as stat6 } from "node:fs/promises";
25336
25515
  import path18 from "node:path";
@@ -25390,7 +25569,7 @@ var storedThreadMetadataV1Schema = external_exports.object({
25390
25569
  formatVersion: external_exports.literal(1),
25391
25570
  hostThreadId: storedHostThreadIdSchema,
25392
25571
  ...storedThreadMetadataFieldsShape
25393
- }).strict();
25572
+ }).passthrough();
25394
25573
  function refineThreadRecord(record4, context) {
25395
25574
  if (record4.state === "ready" && !record4.nativeSessionRef) {
25396
25575
  context.addIssue({
@@ -25449,8 +25628,8 @@ function refineThreadRecord(record4, context) {
25449
25628
  }
25450
25629
  }
25451
25630
  }
25452
- var storedThreadCoreV1Schema = external_exports.object(storedThreadCoreV1Shape).strict().superRefine(refineThreadRecord);
25453
- var storedThreadRecordV1Schema = external_exports.object({ ...storedThreadCoreV1Shape, ...storedThreadMetadataFieldsShape }).strict().superRefine(refineThreadRecord);
25631
+ var storedThreadCoreV1Schema = external_exports.object(storedThreadCoreV1Shape).passthrough().superRefine(refineThreadRecord);
25632
+ var storedThreadRecordV1Schema = external_exports.object({ ...storedThreadCoreV1Shape, ...storedThreadMetadataFieldsShape }).passthrough().superRefine(refineThreadRecord);
25454
25633
  var delegationStatusSchema = external_exports.enum([
25455
25634
  "creating",
25456
25635
  "running",
@@ -25477,7 +25656,7 @@ var storedDelegationRecordV1Schema = external_exports.object({
25477
25656
  });
25478
25657
 
25479
25658
  // packages/mapping-store/dist/section-placements.js
25480
- import { randomUUID as randomUUID6 } from "node:crypto";
25659
+ import { randomUUID as randomUUID7 } from "node:crypto";
25481
25660
  import { constants } from "node:fs";
25482
25661
  import { open as open5, readFile as readFile13, rename as rename8, rm as rm8 } from "node:fs/promises";
25483
25662
  var threadIdSchema = external_exports.string().min(1).max(1024);
@@ -25529,7 +25708,7 @@ async function readSectionPlacementsFile(file2) {
25529
25708
  }
25530
25709
  async function writeSectionPlacementsFile(file2, placements) {
25531
25710
  const parsed2 = parseSectionPlacements({ formatVersion: 1, placements });
25532
- const temp = `${file2}.tmp-${randomUUID6()}`;
25711
+ const temp = `${file2}.tmp-${randomUUID7()}`;
25533
25712
  let handle = null;
25534
25713
  try {
25535
25714
  handle = await open5(temp, "wx", constants.S_IRUSR | constants.S_IWUSR);
@@ -25548,7 +25727,7 @@ async function writeSectionPlacementsFile(file2, placements) {
25548
25727
  }
25549
25728
 
25550
25729
  // packages/mapping-store/dist/superseded-sessions.js
25551
- import { randomUUID as randomUUID7 } from "node:crypto";
25730
+ import { randomUUID as randomUUID8 } from "node:crypto";
25552
25731
  import { constants as constants2 } from "node:fs";
25553
25732
  import { mkdir as mkdir10, open as open6, readFile as readFile14, rename as rename9, rm as rm9 } from "node:fs/promises";
25554
25733
  import path17 from "node:path";
@@ -25583,7 +25762,7 @@ async function writeSupersededSessionsFile(file2, sessions) {
25583
25762
  formatVersion: 1,
25584
25763
  sessions: sessions.slice(-SUPERSEDED_SESSIONS_MAX)
25585
25764
  }).sessions;
25586
- const temp = `${file2}.tmp-${randomUUID7()}`;
25765
+ const temp = `${file2}.tmp-${randomUUID8()}`;
25587
25766
  let handle = null;
25588
25767
  try {
25589
25768
  await mkdir10(path17.dirname(file2), { recursive: true });
@@ -25828,7 +26007,7 @@ var MappingStore = class {
25828
26007
  this.#sectionsDirectory = path18.join(this.#directory, "sections");
25829
26008
  this.#metadataDirectory = path18.join(this.#directory, "thread-metadata");
25830
26009
  this.#lockPath = path18.join(this.#directory, "store.lock");
25831
- this.#instanceId = options2.instanceId ?? randomUUID8();
26010
+ this.#instanceId = options2.instanceId ?? randomUUID9();
25832
26011
  this.#now = options2.now ?? (() => /* @__PURE__ */ new Date());
25833
26012
  this.#beforeReplace = options2.beforeReplace;
25834
26013
  }
@@ -26352,7 +26531,7 @@ var MappingStore = class {
26352
26531
  this.#rebuildIndexes();
26353
26532
  }
26354
26533
  async #replaceFile(target, record4, preserveBackup) {
26355
- const temp = `${target}.tmp-${randomUUID8()}`;
26534
+ const temp = `${target}.tmp-${randomUUID9()}`;
26356
26535
  let handle = null;
26357
26536
  try {
26358
26537
  await this.#beforeReplace?.(cloneRecord(record4));
@@ -26376,18 +26555,20 @@ var MappingStore = class {
26376
26555
  });
26377
26556
  }
26378
26557
  }
26379
- /** Atomically writes, or with null removes, a Thread's Desktop metadata file. */
26558
+ /** Atomically patches owned metadata fields, retaining unknown fields on disk. */
26380
26559
  async #writeMetadataFile(hostThreadId, metadata) {
26381
26560
  const target = this.#metadataPath(hostThreadId);
26382
- const temp = `${target}.tmp-${randomUUID8()}`;
26561
+ const temp = `${target}.tmp-${randomUUID9()}`;
26383
26562
  let handle = null;
26384
26563
  try {
26385
- if (!metadata) {
26564
+ const retained = Object.fromEntries(Object.entries(await this.#readMetadata(hostThreadId) ?? {}).filter(([name]) => !["formatVersion", "hostThreadId", ...THREAD_METADATA_FIELDS].includes(name)));
26565
+ if (!metadata && Object.keys(retained).length === 0) {
26386
26566
  await rm10(target, { force: true });
26387
26567
  return;
26388
26568
  }
26569
+ const next = { ...retained, formatVersion: 1, hostThreadId, ...metadata };
26389
26570
  handle = await open7(temp, "wx", constants3.S_IRUSR | constants3.S_IWUSR);
26390
- await handle.writeFile(`${JSON.stringify(metadata, null, 2)}
26571
+ await handle.writeFile(`${JSON.stringify(next, null, 2)}
26391
26572
  `, "utf8");
26392
26573
  await handle.sync();
26393
26574
  await handle.close();
@@ -26424,7 +26605,7 @@ var MappingStore = class {
26424
26605
  }
26425
26606
  async #replaceDelegationFile(record4) {
26426
26607
  const target = this.#delegationPath(record4.delegationId);
26427
- const temp = `${target}.tmp-${randomUUID8()}`;
26608
+ const temp = `${target}.tmp-${randomUUID9()}`;
26428
26609
  let handle = null;
26429
26610
  try {
26430
26611
  handle = await open7(temp, "wx", constants3.S_IRUSR | constants3.S_IWUSR);
@@ -27102,6 +27283,7 @@ function projectCodexRateLimitsToCredits(usage, resetCredits) {
27102
27283
  const resetsAtUnix = hasFiveHour ? usage.planFiveHourResetsAtUnix : usage.planSevenDayResetsAtUnix;
27103
27284
  const secondary = hasFiveHour && hasSevenDay && usage.planSevenDayUsedPercent !== void 0 ? {
27104
27285
  product: "7-day window",
27286
+ periodType: "seven_day",
27105
27287
  usagePercent: usage.planSevenDayUsedPercent,
27106
27288
  ...usage.planSevenDayResetsAtUnix !== void 0 ? { resetsAt: isoFromUnix(usage.planSevenDayResetsAtUnix) } : {}
27107
27289
  } : void 0;
@@ -28108,8 +28290,18 @@ function collabAgentStatus(status2) {
28108
28290
  return "interrupted";
28109
28291
  }
28110
28292
  }
28293
+ function projectUserMessage(item, clientId) {
28294
+ return {
28295
+ id: item.itemId,
28296
+ type: "userMessage",
28297
+ clientId,
28298
+ content: item.input.map(({ text }) => ({ type: "text", text, text_elements: [] }))
28299
+ };
28300
+ }
28111
28301
  function projectItem(item, outcome, defaultCwd, includeCommandOutput = true, senderThreadId) {
28112
28302
  switch (item.type) {
28303
+ case "userMessage":
28304
+ return projectUserMessage(item, null);
28113
28305
  case "agentMessage":
28114
28306
  return {
28115
28307
  id: item.itemId,
@@ -28321,6 +28513,7 @@ var CodexTurnProjector = class {
28321
28513
  #interactions = /* @__PURE__ */ new Map();
28322
28514
  #items = /* @__PURE__ */ new Map();
28323
28515
  #wireItemOrder = [];
28516
+ #userMessageClientIds = /* @__PURE__ */ new Map();
28324
28517
  #startedAt;
28325
28518
  #startedAtMs;
28326
28519
  #threadId;
@@ -28362,6 +28555,10 @@ var CodexTurnProjector = class {
28362
28555
  }
28363
28556
  return items;
28364
28557
  }
28558
+ /** Associate steered input with the Desktop message that submitted it, before it is published. */
28559
+ bindUserMessageClientId(itemId, clientId) {
28560
+ this.#userMessageClientIds.set(itemId, clientId);
28561
+ }
28365
28562
  pendingTurn(startedAt = null) {
28366
28563
  return {
28367
28564
  id: this.#turnId,
@@ -28372,6 +28569,8 @@ var CodexTurnProjector = class {
28372
28569
  const projected = this.#items.get(itemId);
28373
28570
  if (!projected?.wireStarted)
28374
28571
  return [];
28572
+ if (projected.item.type === "userMessage")
28573
+ return [this.#projectItem(projected.item)];
28375
28574
  if (projected.item.type === "agentMessage") {
28376
28575
  return [projectItem(projected.item, projected.outcome, this.#cwd)];
28377
28576
  }
@@ -28699,7 +28898,7 @@ var CodexTurnProjector = class {
28699
28898
  return { messages: [] };
28700
28899
  }
28701
28900
  const messages = [
28702
- completedItem(projectItem(projected.item, projected.outcome, this.#cwd, !projected.streamedCommandOutput, this.#threadId))
28901
+ completedItem(this.#projectItem(projected.item, projected.outcome, !projected.streamedCommandOutput))
28703
28902
  ];
28704
28903
  if (projected.item.type === "reasoning") {
28705
28904
  const reasoning = projected.item;
@@ -28764,6 +28963,8 @@ var CodexTurnProjector = class {
28764
28963
  projectReasoningTranscriptItem(reasoning, projected.outcome, this.#cwd, projected.durationMs ?? null)
28765
28964
  ];
28766
28965
  }
28966
+ if (projected.item.type === "userMessage")
28967
+ return [this.#projectItem(projected.item)];
28767
28968
  if (projected.item.type === "agentMessage") {
28768
28969
  return [projectItem(projected.item, projected.outcome, this.#cwd)];
28769
28970
  }
@@ -28849,10 +29050,13 @@ var CodexTurnProjector = class {
28849
29050
  threadId: this.#threadId,
28850
29051
  turnId: this.#turnId,
28851
29052
  startedAtMs,
28852
- item: projectItem(item, null, this.#cwd, true, this.#threadId)
29053
+ item: this.#projectItem(item, null)
28853
29054
  }
28854
29055
  };
28855
29056
  }
29057
+ #projectItem(item, outcome = null, includeCommandOutput = true) {
29058
+ return item.type === "userMessage" ? projectUserMessage(item, this.#userMessageClientIds.get(item.itemId) ?? null) : projectItem(item, outcome, this.#cwd, includeCommandOutput, this.#threadId);
29059
+ }
28856
29060
  #startReasoningTranscript(item, startedAtMs) {
28857
29061
  return {
28858
29062
  method: "item/started",
@@ -30244,7 +30448,7 @@ var HarnessAccountInspectionCache = class {
30244
30448
  };
30245
30449
 
30246
30450
  // packages/host-runtime/src/app-server-host.ts
30247
- import { createHash as createHash9, randomUUID as randomUUID14 } from "node:crypto";
30451
+ import { createHash as createHash9, randomUUID as randomUUID16 } from "node:crypto";
30248
30452
  import os8 from "node:os";
30249
30453
  import path25 from "node:path";
30250
30454
 
@@ -30252,12 +30456,12 @@ import path25 from "node:path";
30252
30456
  import nodePath from "node:path";
30253
30457
 
30254
30458
  // packages/host-runtime/src/external-thread-repository.ts
30255
- import { randomUUID as randomUUID10 } from "node:crypto";
30459
+ import { randomUUID as randomUUID11 } from "node:crypto";
30256
30460
  import os7 from "node:os";
30257
30461
  import path22 from "node:path";
30258
30462
 
30259
30463
  // packages/host-runtime/src/external-subagent-threads.ts
30260
- import { createHash as createHash6, randomUUID as randomUUID9 } from "node:crypto";
30464
+ import { createHash as createHash6, randomUUID as randomUUID10 } from "node:crypto";
30261
30465
  function childCreateRequest(parent, nativeRef, id2) {
30262
30466
  const key = createHash6("sha256").update(JSON.stringify([parent.hostThreadId, parent.harnessId, nativeRef.nativeSessionId, id2])).digest("hex");
30263
30467
  return `subagent:${key}`;
@@ -30332,7 +30536,7 @@ function subagentMaterializer(store, parent, previousParent) {
30332
30536
  return rebind(existing, parent);
30333
30537
  }
30334
30538
  const provisional = existing ?? await store.createProvisional({
30335
- hostThreadId: hostThreadIdSchema.parse(randomUUID9()),
30539
+ hostThreadId: hostThreadIdSchema.parse(randomUUID10()),
30336
30540
  createRequestId,
30337
30541
  harnessId: parent.harnessId,
30338
30542
  cwd: parent.cwd,
@@ -30505,7 +30709,7 @@ var ExternalThreadRepository = class {
30505
30709
  throw new Error("Derived Snapshot identity does not belong to its Native Session");
30506
30710
  }
30507
30711
  return {
30508
- hostTurnId: hostTurnIdSchema.parse(randomUUID10()),
30712
+ hostTurnId: hostTurnIdSchema.parse(randomUUID11()),
30509
30713
  nativeTurnRef: turn.nativeTurnRef,
30510
30714
  ...turn.checkpoint ? { nativeCheckpointRef: turn.checkpoint } : {}
30511
30715
  };
@@ -30628,14 +30832,14 @@ var ExternalThreadRepository = class {
30628
30832
  const aligned = snapshot2.turns.map((turn) => {
30629
30833
  const existing = mappingsByNative.get(nativeTurnKey2(turn.nativeTurnRef));
30630
30834
  const mapping = existing ?? {
30631
- hostTurnId: hostTurnIdSchema.parse(randomUUID10()),
30835
+ hostTurnId: hostTurnIdSchema.parse(randomUUID11()),
30632
30836
  nativeTurnRef: turn.nativeTurnRef,
30633
30837
  ...turn.checkpoint ? { nativeCheckpointRef: turn.checkpoint } : {}
30634
30838
  };
30635
30839
  return {
30636
30840
  snapshot: turn,
30637
30841
  mapping: {
30638
- ...mapping,
30842
+ hostTurnId: mapping.hostTurnId,
30639
30843
  nativeTurnRef: turn.nativeTurnRef,
30640
30844
  ...turn.checkpoint ? { nativeCheckpointRef: turn.checkpoint } : {}
30641
30845
  }
@@ -30655,8 +30859,8 @@ var ExternalThreadRepository = class {
30655
30859
  };
30656
30860
  function createExternalThreadRecordInput(input) {
30657
30861
  return {
30658
- hostThreadId: input.hostThreadId ?? hostThreadIdSchema.parse(randomUUID10()),
30659
- createRequestId: input.createRequestId ?? randomUUID10(),
30862
+ hostThreadId: input.hostThreadId ?? hostThreadIdSchema.parse(randomUUID11()),
30863
+ createRequestId: input.createRequestId ?? randomUUID11(),
30660
30864
  harnessId: input.harnessId,
30661
30865
  cwd: input.cwd,
30662
30866
  ...input.title ? { title: input.title } : {},
@@ -30774,6 +30978,15 @@ async function executeExternalThreadFork(input) {
30774
30978
  boundaryIndex = mappings.length - 1;
30775
30979
  }
30776
30980
  const boundary = mappings[boundaryIndex];
30981
+ if (boundary && source.steeredTurnIds.has(boundary.hostTurnId)) {
30982
+ return {
30983
+ ok: false,
30984
+ error: {
30985
+ code: -32080,
30986
+ message: "This Turn took steered input; reopen the Thread before forking from it"
30987
+ }
30988
+ };
30989
+ }
30777
30990
  if (boundaryIndex < 0 || !boundary?.nativeCheckpointRef) {
30778
30991
  return {
30779
30992
  ok: false,
@@ -30830,6 +31043,7 @@ async function executeExternalThreadFork(input) {
30830
31043
  return { ok: false, error: mapExternalThreadHarnessError(opened.error, "fork") };
30831
31044
  }
30832
31045
  const session = opened.value;
31046
+ await inheritSourceModel(source, session);
30833
31047
  const inheritedPermissionModeId = await inheritSourcePermissionMode(source, session);
30834
31048
  try {
30835
31049
  const derivedNativeRef = session.initialState.nativeRef;
@@ -30847,7 +31061,7 @@ async function executeExternalThreadFork(input) {
30847
31061
  source.harnessId,
30848
31062
  provisional.transportModelId
30849
31063
  );
30850
- if (effectiveModel && selection?.model && selection.model.id !== effectiveModel.id) {
31064
+ if (effectiveModel && selection && selection.model?.id !== effectiveModel.id) {
30851
31065
  const transportModelId = encodeExternalTransportSelection(source.harnessId, {
30852
31066
  ...selection,
30853
31067
  model: effectiveModel
@@ -30892,6 +31106,22 @@ async function executeExternalThreadFork(input) {
30892
31106
  };
30893
31107
  }
30894
31108
  }
31109
+ async function inheritSourceModel(source, session) {
31110
+ let persisted;
31111
+ try {
31112
+ persisted = decodeExternalTransportSelection(source.harnessId, source.transportModelId)?.model;
31113
+ } catch {
31114
+ persisted = void 0;
31115
+ }
31116
+ const sourceModel = source.stateObserver.state.effectiveModel ?? source.requestedModel ?? persisted;
31117
+ if (!sourceModel || !session.capabilities.configuration.selectModel || session.initialState.effectiveModel !== void 0) {
31118
+ return;
31119
+ }
31120
+ try {
31121
+ await session.execute({ type: "model.select", model: sourceModel });
31122
+ } catch {
31123
+ }
31124
+ }
30895
31125
  async function inheritSourcePermissionMode(source, session) {
30896
31126
  const sourceModeId = source.stateObserver.state.effectivePermissionModeId ?? source.requestedPermissionModeId;
30897
31127
  if (!sourceModeId) return void 0;
@@ -31513,6 +31743,16 @@ async function executeExternalThreadRollback(input) {
31513
31743
  if (derived.running) {
31514
31744
  return { ok: false, error: { code: -32072, message: "External Thread has an active Turn" } };
31515
31745
  }
31746
+ const undone = derived.record.turnMappings.slice(-rollback.numTurns);
31747
+ if (undone.some((mapping) => derived.steeredTurnIds.has(mapping.hostTurnId))) {
31748
+ return {
31749
+ ok: false,
31750
+ error: {
31751
+ code: -32080,
31752
+ message: "A Turn here took steered input; reopen the Thread before undoing Turns"
31753
+ }
31754
+ };
31755
+ }
31516
31756
  const refreshError = await runtime.refresh(derived);
31517
31757
  if (refreshError) return { ok: false, error: refreshError };
31518
31758
  if (expectedLastTurnId !== void 0 && derived.record.turnMappings.at(-1)?.hostTurnId !== expectedLastTurnId) {
@@ -32312,6 +32552,7 @@ var ExternalThreadRuntime = class {
32312
32552
  #repository;
32313
32553
  #lifecycle = new ExternalThreadLifecycle();
32314
32554
  #hasRunningDescendants;
32555
+ #steeredTurnIds = /* @__PURE__ */ new Map();
32315
32556
  #subagentRunning;
32316
32557
  #threads = /* @__PURE__ */ new Map();
32317
32558
  constructor(input) {
@@ -32325,7 +32566,8 @@ var ExternalThreadRuntime = class {
32325
32566
  this.idleRelease = new ExternalThreadIdleRelease({
32326
32567
  threads: () => this.values(),
32327
32568
  get: (id2) => this.get(id2),
32328
- remove: (id2) => this.remove(id2),
32569
+ // Idle release detaches only the Native Session. Host Thread state survives restoration.
32570
+ remove: (id2) => this.#detach(id2),
32329
32571
  queue: input.idleRelease?.queue ?? new DesktopRequestQueue(),
32330
32572
  canRelease: (thread) => !thread.sessionUnavailable && !this.#lifecycle.busy(thread.id) && (input.idleRelease?.canRelease(thread) ?? false),
32331
32573
  ...input.idleRelease?.onClosed ? { onClosed: input.idleRelease.onClosed } : {},
@@ -32335,14 +32577,21 @@ var ExternalThreadRuntime = class {
32335
32577
  get(threadId2) {
32336
32578
  return this.#threads.get(threadId2);
32337
32579
  }
32580
+ #detach(threadId2) {
32581
+ const thread = this.#threads.get(threadId2);
32582
+ this.#threads.delete(threadId2);
32583
+ if (!thread || thread.steeredTurnIds.size === 0) this.#steeredTurnIds.delete(threadId2);
32584
+ }
32338
32585
  values() {
32339
32586
  return [...this.#threads.values()];
32340
32587
  }
32341
32588
  remove(threadId2) {
32342
32589
  this.#threads.delete(threadId2);
32590
+ this.#steeredTurnIds.delete(threadId2);
32343
32591
  }
32344
32592
  clear() {
32345
32593
  this.#threads.clear();
32594
+ this.#steeredTurnIds.clear();
32346
32595
  }
32347
32596
  drain() {
32348
32597
  return this.#lifecycle.drain();
@@ -32399,6 +32648,11 @@ var ExternalThreadRuntime = class {
32399
32648
  ...effectivePermissionModeId ? { effectivePermissionModeId } : {}
32400
32649
  };
32401
32650
  const running = this.#subagentRunning(input.record.hostThreadId);
32651
+ let steeredTurnIds = this.#steeredTurnIds.get(input.record.hostThreadId);
32652
+ if (!steeredTurnIds) {
32653
+ steeredTurnIds = /* @__PURE__ */ new Set();
32654
+ this.#steeredTurnIds.set(input.record.hostThreadId, steeredTurnIds);
32655
+ }
32402
32656
  const externalThread = {
32403
32657
  id: input.record.hostThreadId,
32404
32658
  cwd: input.record.cwd,
@@ -32423,11 +32677,13 @@ var ExternalThreadRuntime = class {
32423
32677
  usageTurnId: null,
32424
32678
  projectedTurns: /* @__PURE__ */ new Map(),
32425
32679
  responseGates: /* @__PURE__ */ new Map(),
32680
+ pendingSteerProjections: /* @__PURE__ */ new Set(),
32426
32681
  ephemeralTurnIds: /* @__PURE__ */ new Set(),
32427
32682
  persistenceError: null,
32428
32683
  projectedTerminalTurnId: null,
32429
32684
  ignoredInteractionIds: /* @__PURE__ */ new Set(),
32430
- sessionUnavailable: false
32685
+ sessionUnavailable: false,
32686
+ steeredTurnIds
32431
32687
  };
32432
32688
  this.idleRelease.touch(externalThread);
32433
32689
  externalThread.outputTask = this.#consumeOutputs(externalThread);
@@ -32484,13 +32740,13 @@ var ExternalThreadRuntime = class {
32484
32740
  const retire = async () => {
32485
32741
  if (this.#threads.get(parentId) !== parent || this.#threads.get(child.id) !== child) return;
32486
32742
  if (bestEffort) {
32487
- this.#threads.delete(child.id);
32743
+ this.remove(child.id);
32488
32744
  await child.session.close();
32489
32745
  await child.outputTask;
32490
32746
  } else {
32491
32747
  child.sessionUnavailable = true;
32492
32748
  await this.#lifecycle.release(child.id, child.session, child.outputTask);
32493
- if (this.#threads.get(child.id) === child) this.#threads.delete(child.id);
32749
+ if (this.#threads.get(child.id) === child) this.remove(child.id);
32494
32750
  }
32495
32751
  };
32496
32752
  if (bestEffort) await retire().catch(this.#diagnose);
@@ -32863,6 +33119,71 @@ var ExternalThreadRuntime = class {
32863
33119
  };
32864
33120
 
32865
33121
  // packages/host-runtime/src/external-turn-steering.ts
33122
+ import { randomUUID as randomUUID12 } from "node:crypto";
33123
+
33124
+ // packages/host-runtime/src/external-command-routing.ts
33125
+ var ExternalCommandError = class extends Error {
33126
+ constructor(code, message2) {
33127
+ super(message2);
33128
+ this.code = code;
33129
+ }
33130
+ code;
33131
+ };
33132
+ function isExternalCommandCandidate(text) {
33133
+ return /^\/[^\s/]+(?:\s|$)/u.test(text.trimStart());
33134
+ }
33135
+ async function inspectLiveCommandCatalog(commands, timeoutMs = 1e3) {
33136
+ let timeout;
33137
+ try {
33138
+ const result = await Promise.race([
33139
+ commands.list(),
33140
+ new Promise((resolve) => {
33141
+ timeout = setTimeout(() => resolve(null), timeoutMs);
33142
+ })
33143
+ ]);
33144
+ return result?.ok ? harnessCommandCatalogSchema.parse(result.value) : null;
33145
+ } catch {
33146
+ return null;
33147
+ } finally {
33148
+ if (timeout !== void 0) clearTimeout(timeout);
33149
+ }
33150
+ }
33151
+ async function resolveExternalCommand(commands, text, options2 = {}) {
33152
+ const catalog = await commands.list();
33153
+ if (!catalog.ok) throw new ExternalCommandError(-32073, catalog.error.message);
33154
+ const commandText = text.trim();
33155
+ const matched = catalog.value.commands.toSorted((left, right) => right.invocation.length - left.invocation.length).find((command) => {
33156
+ if (commandText === command.invocation) return true;
33157
+ return command.argumentMode === "text" && commandText.startsWith(`${command.invocation} `);
33158
+ });
33159
+ if (!matched) {
33160
+ const name = /^\/(\S+)/u.exec(commandText)?.[1] ?? "";
33161
+ if (name && !isExcludedLiveCommand(name, "command") && options2.liveCatalogPending?.(catalog.value)) {
33162
+ return null;
33163
+ }
33164
+ throw new ExternalCommandError(
33165
+ -32078,
33166
+ "External Harness does not expose the requested command"
33167
+ );
33168
+ }
33169
+ const argumentText = commandText.slice(matched.invocation.length).trimStart();
33170
+ return {
33171
+ descriptor: matched,
33172
+ ...argumentText.length > 0 ? { arguments: { text: argumentText } } : {}
33173
+ };
33174
+ }
33175
+
33176
+ // packages/host-runtime/src/external-turn-steering.ts
33177
+ function isHarnessCommand(thread, text) {
33178
+ return thread.session.commands !== void 0 && isExternalCommandCandidate(restoreHarnessCommandMentions(text));
33179
+ }
33180
+ function externalSteeringDelivery(thread, text) {
33181
+ if (text !== void 0 && isHarnessCommand(thread, text)) return "newTurn";
33182
+ if (thread.session.readCapabilities) {
33183
+ return thread.session.readCapabilities().then((capabilities) => capabilities.steer === true ? "activeTurn" : "newTurn");
33184
+ }
33185
+ return thread.session.capabilities.steer === true ? "activeTurn" : "newTurn";
33186
+ }
32866
33187
  var ExternalSteerError = class extends Error {
32867
33188
  constructor(code, message2) {
32868
33189
  super(message2);
@@ -32870,6 +33191,11 @@ var ExternalSteerError = class extends Error {
32870
33191
  }
32871
33192
  code;
32872
33193
  };
33194
+ var ExternalSteerNotTakenError = class extends ExternalSteerError {
33195
+ constructor(message2) {
33196
+ super(-32074, message2);
33197
+ }
33198
+ };
32873
33199
  function parseInput(params) {
32874
33200
  if (typeof params.expectedTurnId !== "string" || !params.expectedTurnId.trim()) {
32875
33201
  throw new ExternalSteerError(-32602, "External steering requires expectedTurnId");
@@ -32886,12 +33212,47 @@ function parseInput(params) {
32886
33212
  if (clientUserMessageId != null && (typeof clientUserMessageId !== "string" || !clientUserMessageId.trim())) {
32887
33213
  throw new ExternalSteerError(-32602, "External steering message identity is invalid");
32888
33214
  }
33215
+ const delivery = params[THREAD_STEER_DELIVERY_PARAM];
33216
+ const parsedDelivery = delivery == null ? void 0 : threadSteerDeliverySchema.safeParse(delivery);
33217
+ if (parsedDelivery && !parsedDelivery.success) {
33218
+ throw new ExternalSteerError(-32602, "External steering delivery is invalid");
33219
+ }
32889
33220
  return {
32890
33221
  expectedTurnId: params.expectedTurnId,
32891
33222
  text,
32892
- ...typeof clientUserMessageId === "string" ? { clientUserMessageId } : {}
33223
+ ...typeof clientUserMessageId === "string" ? { clientUserMessageId } : {},
33224
+ ...parsedDelivery ? { delivery: parsedDelivery.data } : {}
32893
33225
  };
32894
33226
  }
33227
+ async function insertNativeSteer(thread, input, host) {
33228
+ const restored = restoreHarnessCommandMentions(input.text);
33229
+ const steered = [{ type: "text", text: rewriteDelegationMentionText(restored) }];
33230
+ const displayed = [{ type: "text", text: restored }];
33231
+ const projectionDone = Promise.withResolvers();
33232
+ thread.pendingSteerProjections.add(projectionDone.promise);
33233
+ try {
33234
+ const result = await thread.session.execute({
33235
+ type: "turn.steer",
33236
+ turnId: input.turnId,
33237
+ input: steered
33238
+ });
33239
+ if (!result.ok) throw new ExternalSteerNotTakenError(result.error.message);
33240
+ thread.steeredTurnIds.add(input.turnId);
33241
+ const item = {
33242
+ type: "userMessage",
33243
+ itemId: hostItemIdSchema.parse(randomUUID12()),
33244
+ input: displayed
33245
+ };
33246
+ try {
33247
+ await host.project(input.turnId, item, input.clientUserMessageId);
33248
+ } catch (error51) {
33249
+ host.diagnose(error51);
33250
+ }
33251
+ } finally {
33252
+ projectionDone.resolve(void 0);
33253
+ thread.pendingSteerProjections.delete(projectionDone.promise);
33254
+ }
33255
+ }
32895
33256
  var ExternalTurnSteering = class {
32896
33257
  constructor(timeoutMs = 2e4) {
32897
33258
  this.timeoutMs = timeoutMs;
@@ -32903,7 +33264,7 @@ var ExternalTurnSteering = class {
32903
33264
  hasPending(threadId2) {
32904
33265
  return threadId2 === void 0 ? this.#pending.size > 0 : this.#pending.has(threadId2);
32905
33266
  }
32906
- run(thread, params, start) {
33267
+ run(thread, params, start, insert) {
32907
33268
  try {
32908
33269
  const input = parseInput(params);
32909
33270
  if (this.#closed)
@@ -32925,7 +33286,7 @@ var ExternalTurnSteering = class {
32925
33286
  if (!thread.running || thread.activeTurnId !== input.expectedTurnId) {
32926
33287
  throw new ExternalSteerError(-32074, "External steering must reference the active Turn");
32927
33288
  }
32928
- const result = this.#replace(thread, input, start);
33289
+ const result = this.#deliver(thread, input, start, insert);
32929
33290
  if (key) {
32930
33291
  const receipt2 = { fingerprint, result, settled: false };
32931
33292
  this.#receipts.set(key, receipt2);
@@ -32948,6 +33309,56 @@ var ExternalTurnSteering = class {
32948
33309
  return Promise.reject(error51);
32949
33310
  }
32950
33311
  }
33312
+ /**
33313
+ * A Renderer that already presented a delivery gets exactly that one. Otherwise native delivery
33314
+ * is tried only when the Session can take the input now, and input the Harness did not take
33315
+ * falls back to stopping and restarting, so no case is worse than the stop-then-start path.
33316
+ */
33317
+ #deliver(thread, input, start, insert) {
33318
+ const delivery = input.delivery === "newTurn" || isHarnessCommand(thread, input.text) ? "newTurn" : input.delivery ?? externalSteeringDelivery(thread);
33319
+ const deliver = (mode) => {
33320
+ if (this.#closed || !thread.running || thread.activeTurnId !== input.expectedTurnId) {
33321
+ throw new ExternalSteerError(-32074, "External steering target is no longer active");
33322
+ }
33323
+ if (this.hasPending(thread.id))
33324
+ throw new ExternalSteerError(-32072, "External Thread is already changing direction");
33325
+ if (mode === "newTurn") return this.#replace(thread, input, start);
33326
+ const native = insert({
33327
+ turnId: thread.activeTurnId,
33328
+ text: input.text,
33329
+ ...input.clientUserMessageId ? { clientUserMessageId: input.clientUserMessageId } : {}
33330
+ }).then((admitted) => ({ ...admitted, delivery: "activeTurn" }));
33331
+ if (input.delivery === "activeTurn") return native;
33332
+ return native.catch(async (error51) => {
33333
+ if (!(error51 instanceof ExternalSteerNotTakenError)) throw error51;
33334
+ if (thread.running || thread.activeTurnId) return deliver("newTurn");
33335
+ const assertActive = () => {
33336
+ if (this.#closed)
33337
+ throw new ExternalSteerError(-32074, "External steering connection is closed");
33338
+ };
33339
+ return { ...await start(input.text, assertActive), delivery: "newTurn" };
33340
+ });
33341
+ };
33342
+ if (typeof delivery === "string") return deliver(delivery);
33343
+ const failure = Promise.withResolvers();
33344
+ let failureReason = null;
33345
+ const pending = {
33346
+ turnId: input.expectedTurnId,
33347
+ resolve: () => void 0,
33348
+ reject(error51) {
33349
+ failureReason ??= error51;
33350
+ failure.reject(error51);
33351
+ }
33352
+ };
33353
+ this.#pending.set(thread.id, pending);
33354
+ return Promise.race([delivery, failure.promise]).then((mode) => {
33355
+ if (failureReason) throw failureReason;
33356
+ if (this.#pending.get(thread.id) === pending) this.#pending.delete(thread.id);
33357
+ return deliver(mode);
33358
+ }).finally(() => {
33359
+ if (this.#pending.get(thread.id) === pending) this.#pending.delete(thread.id);
33360
+ });
33361
+ }
32951
33362
  terminal(threadId2, turnId, outcome) {
32952
33363
  const pending = this.#pending.get(threadId2);
32953
33364
  if (pending?.turnId === turnId) pending.resolve(outcome);
@@ -33002,7 +33413,9 @@ var ExternalTurnSteering = class {
33002
33413
  thread.session.execute({ type: "turn.cancel", turnId }),
33003
33414
  failure.promise
33004
33415
  ]);
33005
- if (!cancelled.ok) throw new ExternalSteerError(-32074, cancelled.error.message);
33416
+ if (!cancelled.ok && cancelled.error.code !== "invalidState") {
33417
+ throw new ExternalSteerError(-32074, cancelled.error.message);
33418
+ }
33006
33419
  const outcome = await Promise.race([terminal3.promise, failure.promise]);
33007
33420
  clearTimeout(timeout);
33008
33421
  const assertActive = () => {
@@ -33020,7 +33433,7 @@ var ExternalTurnSteering = class {
33020
33433
  if (thread.running || thread.activeTurnId) {
33021
33434
  throw new ExternalSteerError(-32072, "Another External Turn started before replacement");
33022
33435
  }
33023
- return await start(input.text, assertActive);
33436
+ return { ...await start(input.text, assertActive), delivery: "newTurn" };
33024
33437
  } finally {
33025
33438
  clearTimeout(timeout);
33026
33439
  if (this.#pending.get(thread.id) === pending) this.#pending.delete(thread.id);
@@ -33028,58 +33441,6 @@ var ExternalTurnSteering = class {
33028
33441
  }
33029
33442
  };
33030
33443
 
33031
- // packages/host-runtime/src/external-command-routing.ts
33032
- var ExternalCommandError = class extends Error {
33033
- constructor(code, message2) {
33034
- super(message2);
33035
- this.code = code;
33036
- }
33037
- code;
33038
- };
33039
- function isExternalCommandCandidate(text) {
33040
- return /^\/[^\s/]+(?:\s|$)/u.test(text.trimStart());
33041
- }
33042
- async function inspectLiveCommandCatalog(commands, timeoutMs = 1e3) {
33043
- let timeout;
33044
- try {
33045
- const result = await Promise.race([
33046
- commands.list(),
33047
- new Promise((resolve) => {
33048
- timeout = setTimeout(() => resolve(null), timeoutMs);
33049
- })
33050
- ]);
33051
- return result?.ok ? harnessCommandCatalogSchema.parse(result.value) : null;
33052
- } catch {
33053
- return null;
33054
- } finally {
33055
- if (timeout !== void 0) clearTimeout(timeout);
33056
- }
33057
- }
33058
- async function resolveExternalCommand(commands, text, options2 = {}) {
33059
- const catalog = await commands.list();
33060
- if (!catalog.ok) throw new ExternalCommandError(-32073, catalog.error.message);
33061
- const commandText = text.trim();
33062
- const matched = catalog.value.commands.toSorted((left, right) => right.invocation.length - left.invocation.length).find((command) => {
33063
- if (commandText === command.invocation) return true;
33064
- return command.argumentMode === "text" && commandText.startsWith(`${command.invocation} `);
33065
- });
33066
- if (!matched) {
33067
- const name = /^\/(\S+)/u.exec(commandText)?.[1] ?? "";
33068
- if (name && !isExcludedLiveCommand(name, "command") && options2.liveCatalogPending?.(catalog.value)) {
33069
- return null;
33070
- }
33071
- throw new ExternalCommandError(
33072
- -32078,
33073
- "External Harness does not expose the requested command"
33074
- );
33075
- }
33076
- const argumentText = commandText.slice(matched.invocation.length).trimStart();
33077
- return {
33078
- descriptor: matched,
33079
- ...argumentText.length > 0 ? { arguments: { text: argumentText } } : {}
33080
- };
33081
- }
33082
-
33083
33444
  // packages/host-runtime/src/live-command-catalog-cache.ts
33084
33445
  import path23 from "node:path";
33085
33446
  var MAX_ENTRIES = 64;
@@ -33111,7 +33472,7 @@ function sameWorkspace(left, right) {
33111
33472
  }
33112
33473
 
33113
33474
  // packages/host-runtime/src/harness-delegation-coordinator.ts
33114
- import { createHash as createHash8, randomUUID as randomUUID11 } from "node:crypto";
33475
+ import { createHash as createHash8, randomUUID as randomUUID13 } from "node:crypto";
33115
33476
  import path24 from "node:path";
33116
33477
 
33117
33478
  // packages/host-runtime/src/delegation-snapshot.ts
@@ -33257,6 +33618,15 @@ function projectDelegationThreadSnapshot(input) {
33257
33618
  // packages/host-runtime/src/harness-delegation-coordinator.ts
33258
33619
  var IMPLICIT_DEDUPLICATION_MS = 3e4;
33259
33620
  var NATIVE_REF_TIMEOUT_MS = 1e4;
33621
+ function truncateTitle(value2, length) {
33622
+ return value2.slice(0, length).replace(/[\uD800-\uDBFF]$/u, "");
33623
+ }
33624
+ function delegationTitle(parentTitle, input) {
33625
+ const taskName = (input.taskName ?? input.task.trim().split(/\r?\n/u)[0] ?? input.task).replace(/\s+/gu, " ").trim();
33626
+ const subtask = truncateTitle(taskName, 120);
33627
+ const parent = (parentTitle?.trim() || "Untitled Thread").replace(/\s+/gu, " ");
33628
+ return `${subtask} · ${truncateTitle(parent, 200 - subtask.length - 3)}`;
33629
+ }
33260
33630
  function delay3(milliseconds) {
33261
33631
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
33262
33632
  }
@@ -33283,6 +33653,12 @@ function statusFromThread(thread, busy) {
33283
33653
  function validateStart(input) {
33284
33654
  if (typeof input.task !== "string" || !input.task.trim())
33285
33655
  throw new DelegationControlError("INVALID_ARGUMENT", "Task must not be empty");
33656
+ if (input.taskName !== void 0 && (typeof input.taskName !== "string" || !input.taskName.trim() || input.taskName.length > 120)) {
33657
+ throw new DelegationControlError(
33658
+ "INVALID_ARGUMENT",
33659
+ "Task name must contain 1 to 120 characters"
33660
+ );
33661
+ }
33286
33662
  if (input.cwd !== void 0 && (typeof input.cwd !== "string" || !input.cwd.trim()))
33287
33663
  throw new DelegationControlError("INVALID_ARGUMENT", "cwd must not be empty");
33288
33664
  if (input.requestId !== void 0 && (typeof input.requestId !== "string" || !input.requestId.trim())) {
@@ -33303,7 +33679,7 @@ var HarnessDelegationCoordinator = class {
33303
33679
  #cancelOfficial;
33304
33680
  #startOfficial;
33305
33681
  #listOfficial;
33306
- #officialThreadCwd;
33682
+ #officialThreadMetadata;
33307
33683
  #activeOfficialParents;
33308
33684
  #externalThreadBusy;
33309
33685
  constructor(input) {
@@ -33320,7 +33696,7 @@ var HarnessDelegationCoordinator = class {
33320
33696
  this.#cancelOfficial = input.cancelOfficial;
33321
33697
  this.#startOfficial = input.startOfficial;
33322
33698
  this.#listOfficial = input.listOfficial;
33323
- this.#officialThreadCwd = input.officialThreadCwd;
33699
+ this.#officialThreadMetadata = input.officialThreadMetadata;
33324
33700
  this.#activeOfficialParents = input.activeOfficialParents;
33325
33701
  this.#externalThreadBusy = input.externalThreadBusy;
33326
33702
  }
@@ -33350,8 +33726,14 @@ var HarnessDelegationCoordinator = class {
33350
33726
  const parentThreadId = await this.#resolveParent(input.parentThreadId);
33351
33727
  const parent = await this.#parentMetadata(parentThreadId);
33352
33728
  const selectedCwd = input.cwd ?? parent.cwd ?? process.cwd();
33729
+ const title = delegationTitle(parent.title, input);
33353
33730
  if (input.harnessId === "codex") {
33354
- const result = await this.#startOfficial({ ...input, parentThreadId, cwd: selectedCwd });
33731
+ const result = await this.#startOfficial({
33732
+ ...input,
33733
+ parentThreadId,
33734
+ cwd: selectedCwd,
33735
+ title
33736
+ });
33355
33737
  return { ...result, parentThreadId, cwd: result.cwd ?? selectedCwd };
33356
33738
  }
33357
33739
  const startInput = { ...input, parentThreadId, cwd: path24.resolve(selectedCwd) };
@@ -33394,17 +33776,17 @@ var HarnessDelegationCoordinator = class {
33394
33776
  });
33395
33777
  this.#validateConfiguration(inspected.inspection, input.model, input.thinkingOptionId);
33396
33778
  }
33397
- const delegationId = hostThreadIdSchema.parse(randomUUID11());
33398
- const childThreadId = hostThreadIdSchema.parse(randomUUID11());
33399
- const turnId = hostTurnIdSchema.parse(randomUUID11());
33400
- const createRequestId = input.requestId ? `delegation:${input.requestId}` : randomUUID11();
33779
+ const delegationId = hostThreadIdSchema.parse(randomUUID13());
33780
+ const childThreadId = hostThreadIdSchema.parse(randomUUID13());
33781
+ const turnId = hostTurnIdSchema.parse(randomUUID13());
33782
+ const createRequestId = input.requestId ? `delegation:${input.requestId}` : randomUUID13();
33401
33783
  let record4 = await this.#repository.createProvisional(
33402
33784
  createExternalThreadRecordInput({
33403
33785
  hostThreadId: childThreadId,
33404
33786
  createRequestId,
33405
33787
  harnessId: harnessIdSchema.parse(targetHarnessId),
33406
33788
  cwd: startInput.cwd,
33407
- title: input.task.trim().slice(0, 120),
33789
+ title,
33408
33790
  transportModelId: input.model || input.thinkingOptionId ? encodeExternalTransportSelection(targetHarnessId, {
33409
33791
  ...input.model ? { model: input.model } : {},
33410
33792
  ...input.thinkingOptionId ? { thinkingOptionId: input.thinkingOptionId } : {}
@@ -33551,7 +33933,7 @@ var HarnessDelegationCoordinator = class {
33551
33933
  if (this.#externalThreadBusy(thread) || thread.activeTurnId) {
33552
33934
  throw new DelegationControlError("THREAD_BUSY", "Thread already has an active Turn");
33553
33935
  }
33554
- const turnId = hostTurnIdSchema.parse(randomUUID11());
33936
+ const turnId = hostTurnIdSchema.parse(randomUUID13());
33555
33937
  try {
33556
33938
  await this.#startExternalTurn(thread, input.message, turnId);
33557
33939
  } catch (error51) {
@@ -33765,9 +34147,14 @@ var HarnessDelegationCoordinator = class {
33765
34147
  }
33766
34148
  async #parentMetadata(parentThreadId) {
33767
34149
  const record4 = await this.#repository.find(parentThreadId);
33768
- if (record4) return { harnessId: record4.harnessId, cwd: record4.cwd };
33769
- const cwd = await this.#officialThreadCwd(parentThreadId).catch(() => void 0);
33770
- return { harnessId: "codex", ...cwd ? { cwd } : {} };
34150
+ if (record4)
34151
+ return {
34152
+ harnessId: record4.harnessId,
34153
+ cwd: record4.cwd,
34154
+ title: record4.title
34155
+ };
34156
+ const metadata = await this.#officialThreadMetadata(parentThreadId).catch(() => ({}));
34157
+ return { harnessId: "codex", ...metadata };
33771
34158
  }
33772
34159
  async #existingResult(delegation) {
33773
34160
  const record4 = await this.#repository.find(delegation.childHostThreadId);
@@ -35789,7 +36176,9 @@ function hasValidSessionUsage(value2) {
35789
36176
  }
35790
36177
  function isAdapter(value2) {
35791
36178
  if (!value2 || typeof value2 !== "object") return false;
35792
- return hasValidSessionUsage(value2) && ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function");
36179
+ return hasValidSessionUsage(value2) && ["inspect", "open", "close"].every((key) => typeof Reflect.get(value2, key) === "function") && ["credits", "refreshCredits"].every(
36180
+ (key) => Reflect.get(value2, key) === void 0 || typeof Reflect.get(value2, key) === "function"
36181
+ );
35793
36182
  }
35794
36183
  function isUsageAdapter(value2) {
35795
36184
  if (!value2 || typeof value2 !== "object" || typeof Reflect.get(value2, "close") !== "function")
@@ -36194,10 +36583,10 @@ var SingleNativeCodexAccount = class {
36194
36583
  };
36195
36584
 
36196
36585
  // packages/host-runtime/src/codex-runtime/official-runtime-owner.ts
36197
- import { randomUUID as randomUUID13 } from "node:crypto";
36586
+ import { randomUUID as randomUUID15 } from "node:crypto";
36198
36587
 
36199
36588
  // packages/host-runtime/src/official-request-broker.ts
36200
- import { randomUUID as randomUUID12 } from "node:crypto";
36589
+ import { randomUUID as randomUUID14 } from "node:crypto";
36201
36590
  var INTERNAL_REQUEST_PREFIX = "codexhost:official:";
36202
36591
  var MAX_RETIRED_IDS = 1024;
36203
36592
  function isRecord15(value2) {
@@ -36213,7 +36602,7 @@ var OfficialRequestBroker = class {
36213
36602
  constructor(input) {
36214
36603
  this.#send = input.send;
36215
36604
  this.#timeoutMs = input.timeoutMs ?? 3e4;
36216
- this.#nextId = input.nextId ?? (() => `${INTERNAL_REQUEST_PREFIX}${randomUUID12()}`);
36605
+ this.#nextId = input.nextId ?? (() => `${INTERNAL_REQUEST_PREFIX}${randomUUID14()}`);
36217
36606
  }
36218
36607
  get pendingCount() {
36219
36608
  return this.#pending.size;
@@ -36567,7 +36956,7 @@ var OfficialRuntimeOwner = class {
36567
36956
  }
36568
36957
  #attach(role, output, onBackendStopped) {
36569
36958
  const client = {
36570
- id: randomUUID13(),
36959
+ id: randomUUID15(),
36571
36960
  role,
36572
36961
  output,
36573
36962
  ...onBackendStopped ? { onBackendStopped } : {},
@@ -36725,7 +37114,7 @@ var OfficialRuntimeOwner = class {
36725
37114
  if (!object4(value2)) return client.output(event);
36726
37115
  if (typeof value2.id === "string" || typeof value2.id === "number") {
36727
37116
  if (typeof value2.method === "string") {
36728
- const id2 = `codexhost:server:${client.id}:${event.generation}:${randomUUID13()}`;
37117
+ const id2 = `codexhost:server:${client.id}:${event.generation}:${randomUUID15()}`;
36729
37118
  client.serverRequests.set(id2, value2.id);
36730
37119
  const projected = { ...value2, id: id2 };
36731
37120
  return client.output({
@@ -36901,16 +37290,16 @@ var OfficialRuntimeScope = class {
36901
37290
  #scheduleRecovery() {
36902
37291
  if (!this.#recovery || this.#closed || this.#recoveryTimer || this.#starting) return;
36903
37292
  const { delaysMs } = this.#recovery;
36904
- const delay6 = delaysMs[Math.min(this.#recoveryAttempt, delaysMs.length - 1)] ?? 0;
37293
+ const delay7 = delaysMs[Math.min(this.#recoveryAttempt, delaysMs.length - 1)] ?? 0;
36905
37294
  this.#recoveryAttempt++;
36906
37295
  this.#diagnosticOutput.write(
36907
- `codexhost: official Codex is unavailable; restarting in ${String(delay6)}ms
37296
+ `codexhost: official Codex is unavailable; restarting in ${String(delay7)}ms
36908
37297
  `
36909
37298
  );
36910
37299
  const timer = setTimeout(() => {
36911
37300
  this.#recoveryTimer = void 0;
36912
37301
  this.start().catch(() => void 0);
36913
- }, delay6);
37302
+ }, delay7);
36914
37303
  timer.unref();
36915
37304
  this.#recoveryTimer = timer;
36916
37305
  }
@@ -37657,6 +38046,16 @@ async function moveThreadSection(input) {
37657
38046
  }
37658
38047
  }
37659
38048
 
38049
+ // packages/host-runtime/src/external-thread-official-defaults.ts
38050
+ var EMPTY_RESULTS = /* @__PURE__ */ new Map([
38051
+ ["thread/attachment/list", () => ({ data: [], nextCursor: null })],
38052
+ ["thread/queue/list", () => ({ data: [], nextCursor: null })],
38053
+ ["thread/settings/update", () => ({})]
38054
+ ]);
38055
+ function externalThreadEmptyOfficialResult(method) {
38056
+ return EMPTY_RESULTS.get(method)?.();
38057
+ }
38058
+
37660
38059
  // packages/host-runtime/src/observed-session-usage.ts
37661
38060
  var ObservedSessionUsage = class {
37662
38061
  constructor(providers, prices, diagnose) {
@@ -37825,16 +38224,6 @@ function officialThreadBusy(thread) {
37825
38224
  const latestTurn = turns.at(-1);
37826
38225
  return isRecord18(latestTurn) && (latestTurn.status === "inProgress" || latestTurn.status === "running");
37827
38226
  }
37828
- function isCreditsAdapter(adapter) {
37829
- return typeof adapter.credits === "function";
37830
- }
37831
- function projectAccountCredits(value2) {
37832
- if (!isRecord18(value2)) return null;
37833
- const rest = { ...value2 };
37834
- delete rest.fetchedAt;
37835
- const parsed2 = accountCreditsSnapshotSchema.safeParse(rest);
37836
- return parsed2.success ? parsed2.data : null;
37837
- }
37838
38227
  function errorMessage4(error51) {
37839
38228
  return error51 instanceof Error ? error51.message : String(error51);
37840
38229
  }
@@ -38005,6 +38394,7 @@ var AppServerHost = class {
38005
38394
  #plugins;
38006
38395
  #launchSettings;
38007
38396
  #accountInspections = new HarnessAccountInspectionCache();
38397
+ #installationTasks = new HarnessInstallationTasks();
38008
38398
  #externalRuntime;
38009
38399
  #executionHosts = /* @__PURE__ */ new WeakMap();
38010
38400
  #externalPrewarms = new ExternalThreadPrewarms();
@@ -38035,7 +38425,7 @@ var AppServerHost = class {
38035
38425
  #sectionMoves = Promise.resolve();
38036
38426
  #writer;
38037
38427
  /** Console requests share Desktop handling; their replies return to the console. */
38038
- #consoleRequestPrefix = `codexhost-console:${randomUUID14()}:`;
38428
+ #consoleRequestPrefix = `codexhost-console:${randomUUID16()}:`;
38039
38429
  #consoleReplies = /* @__PURE__ */ new Map();
38040
38430
  #nextConsoleRequest = 0;
38041
38431
  #subagentThreadStatuses = /* @__PURE__ */ new Map();
@@ -38173,7 +38563,7 @@ var AppServerHost = class {
38173
38563
  cancelOfficial: (input) => this.#cancelOfficialDelegationThread(input),
38174
38564
  startOfficial: (input) => this.#startOfficialDelegation(input),
38175
38565
  listOfficial: (input) => this.#listDelegationThreads(input),
38176
- officialThreadCwd: (threadId2) => this.#readOfficialThreadCwd(threadId2),
38566
+ officialThreadMetadata: (threadId2) => this.#readOfficialThreadMetadata(threadId2),
38177
38567
  activeOfficialParents: () => [...this.#activeOfficialTurns.keys()],
38178
38568
  externalThreadBusy: (thread) => this.#externalThreadBusy(thread)
38179
38569
  });
@@ -38819,11 +39209,11 @@ var AppServerHost = class {
38819
39209
  this.#dispatchDesktopReply(request, () => this.#inspectHarness(request));
38820
39210
  return;
38821
39211
  }
38822
- if (request.method === HARNESS_INSTALLATION_METHOD) {
39212
+ if (request.method === HARNESS_INSTALLATION_METHOD || request.method === HARNESS_INSTALLATION_START_METHOD || request.method === HARNESS_INSTALLATION_STATUS_METHOD) {
38823
39213
  this.#dispatchDesktopReply(request, async () => {
38824
- await this.#waitForPlugins();
38825
39214
  try {
38826
- const result = await handleHarnessInstallation(request.params, this.#externalAdapters);
39215
+ if (request.method !== HARNESS_INSTALLATION_STATUS_METHOD) await this.#waitForPlugins();
39216
+ const result = request.method === HARNESS_INSTALLATION_START_METHOD ? this.#installationTasks.start(request.params, this.#externalAdapters) : request.method === HARNESS_INSTALLATION_STATUS_METHOD ? this.#installationTasks.status(request.params) : await handleHarnessInstallation(request.params, this.#externalAdapters);
38827
39217
  await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result) }));
38828
39218
  } catch (error51) {
38829
39219
  await this.#writer.json(
@@ -38908,6 +39298,10 @@ var AppServerHost = class {
38908
39298
  await this.#listThreadOwnership(request);
38909
39299
  return;
38910
39300
  }
39301
+ if (request.method === "codexhost/thread/steering/inspect") {
39302
+ await this.#inspectThreadSteering(request);
39303
+ return;
39304
+ }
38911
39305
  if (request.method === "codexhost/thread/model/select") {
38912
39306
  await this.#selectThreadModel(request);
38913
39307
  return;
@@ -39271,8 +39665,9 @@ var AppServerHost = class {
39271
39665
  const location = await this.#locateExternalThread(request.params.threadId);
39272
39666
  if (await this.#writeResolutionError(request, location)) return;
39273
39667
  if (location.kind === "external") {
39668
+ const emptyResult = externalThreadEmptyOfficialResult(request.method);
39274
39669
  await this.#writer.json(
39275
- rpcError(request, -32076, `External Thread does not support ${request.method}`)
39670
+ emptyResult ? rpcEnvelope(request, { result: emptyResult }) : rpcError(request, -32076, `External Thread does not support ${request.method}`)
39276
39671
  );
39277
39672
  return;
39278
39673
  }
@@ -39484,12 +39879,17 @@ var AppServerHost = class {
39484
39879
  ]);
39485
39880
  return thread !== null || childDelegation !== null || delegation !== null;
39486
39881
  }
39487
- async #readOfficialThreadCwd(threadId2) {
39488
- const response = await this.#requestOfficial("thread/read", { threadId: threadId2 });
39489
- if (isRecord18(response.error)) return void 0;
39882
+ async #readOfficialThreadMetadata(threadId2) {
39883
+ const response = await this.#requestOfficial("thread/read", { threadId: threadId2, includeTurns: false });
39884
+ if (isRecord18(response.error)) return {};
39490
39885
  const result = isRecord18(response.result) ? response.result : null;
39491
39886
  const thread = result && isRecord18(result.thread) ? result.thread : null;
39492
- return thread && typeof thread.cwd === "string" && thread.cwd.trim() ? thread.cwd : void 0;
39887
+ if (!thread) return {};
39888
+ const title = typeof thread.name === "string" && thread.name.trim() ? thread.name : thread.preview;
39889
+ return {
39890
+ ...typeof thread.cwd === "string" && thread.cwd.trim() ? { cwd: thread.cwd } : {},
39891
+ ...typeof title === "string" && title.trim() ? { title } : {}
39892
+ };
39493
39893
  }
39494
39894
  async #inspectOfficialDelegationTarget(input) {
39495
39895
  const response = await this.#requestOfficial("model/list", {});
@@ -39642,6 +40042,9 @@ var AppServerHost = class {
39642
40042
  this.#pendingOfficialDelegationThreads.add(threadId2);
39643
40043
  let turnId;
39644
40044
  try {
40045
+ await this.#requestOfficial("thread/name/set", { threadId: threadId2, name: input.title }).catch(
40046
+ () => void 0
40047
+ );
39645
40048
  const turn = await this.#requestOfficial("turn/start", {
39646
40049
  threadId: threadId2,
39647
40050
  input: [{ type: "text", text: input.task }],
@@ -39660,7 +40063,7 @@ var AppServerHost = class {
39660
40063
  throw error51;
39661
40064
  }
39662
40065
  this.#activeOfficialTurns.set(threadId2, turnId);
39663
- const delegationId = hostThreadIdSchema.parse(randomUUID14());
40066
+ const delegationId = hostThreadIdSchema.parse(randomUUID16());
39664
40067
  try {
39665
40068
  const source = await this.#repository.find(input.parentThreadId);
39666
40069
  const pendingTerminal = this.#pendingOfficialTerminalStatuses.get(threadId2);
@@ -39848,7 +40251,7 @@ var AppServerHost = class {
39848
40251
  async #listDelegationThreads(input) {
39849
40252
  const [sortKey, sortDirection2] = input.sort.split("-");
39850
40253
  const request = {
39851
- id: `codexhost:delegation-list:${randomUUID14()}`,
40254
+ id: `codexhost:delegation-list:${randomUUID16()}`,
39852
40255
  method: "thread/list",
39853
40256
  params: {
39854
40257
  cwd: input.cwd ? [input.cwd] : null,
@@ -40197,7 +40600,8 @@ var AppServerHost = class {
40197
40600
  try {
40198
40601
  inspection = await adapter.inspect({
40199
40602
  ...params.data.cwd ? { cwd: params.data.cwd } : {},
40200
- ...params.data.refresh !== void 0 ? { refresh: params.data.refresh } : {}
40603
+ ...params.data.refresh !== void 0 ? { refresh: params.data.refresh } : {},
40604
+ ...params.data.purpose ? { purpose: params.data.purpose } : {}
40201
40605
  });
40202
40606
  } catch (error51) {
40203
40607
  await this.#writer.json(
@@ -40328,6 +40732,7 @@ var AppServerHost = class {
40328
40732
  const result2 = threadUsageInspectionSchema.parse({
40329
40733
  threadId: params.data.threadId,
40330
40734
  usage: await this.#observedSessionUsage.read(params.data.threadId),
40735
+ accountCreditsSupported: true,
40331
40736
  ...accountCredits ? { accountCredits } : {}
40332
40737
  });
40333
40738
  await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result2) }));
@@ -40335,11 +40740,17 @@ var AppServerHost = class {
40335
40740
  }
40336
40741
  if (params.data.refresh === "exact") void resolution.thread.session.refreshUsage?.();
40337
40742
  const adapter = this.#externalAdapters.get(resolution.thread.harnessId);
40338
- if (adapter && isCreditsAdapter(adapter)) void adapter.refreshCredits?.();
40339
- const credits = adapter && isCreditsAdapter(adapter) ? projectAccountCredits(adapter.credits()) : null;
40743
+ if (adapter?.credits && adapter.refreshCredits)
40744
+ void Promise.resolve().then(() => adapter.refreshCredits?.()).catch(() => void 0);
40745
+ let credits = null;
40746
+ try {
40747
+ credits = parseHarnessAccountCredits(adapter?.credits?.());
40748
+ } catch {
40749
+ }
40340
40750
  const result = threadUsageInspectionSchema.parse({
40341
40751
  threadId: params.data.threadId,
40342
40752
  usage: await this.#threadUsage(resolution.thread),
40753
+ accountCreditsSupported: typeof adapter?.credits === "function",
40343
40754
  ...credits ? { accountCredits: credits } : {}
40344
40755
  });
40345
40756
  await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result) }));
@@ -40383,6 +40794,28 @@ var AppServerHost = class {
40383
40794
  );
40384
40795
  }
40385
40796
  }
40797
+ /** Read the same delivery decision that Desktop `turn/steer` will follow. */
40798
+ async #inspectThreadSteering(request) {
40799
+ const params = threadSteeringInspectParamsSchema.safeParse(request.params);
40800
+ if (!params.success) {
40801
+ await this.#writer.json(rpcError(request, -32602, "Invalid Thread steering params"));
40802
+ return;
40803
+ }
40804
+ let delivery;
40805
+ try {
40806
+ const location = await this.#locateExternalThread(params.data.threadId);
40807
+ if (await this.#writeResolutionError(request, location)) return;
40808
+ const thread = location.kind === "external" ? this.#externalRuntime.get(params.data.threadId) : void 0;
40809
+ delivery = location.kind !== "external" ? "official" : thread ? await externalSteeringDelivery(thread, params.data.text) : "newTurn";
40810
+ } catch {
40811
+ await this.#writer.json(
40812
+ rpcError(request, -32081, "Thread steering metadata could not be read")
40813
+ );
40814
+ return;
40815
+ }
40816
+ const result = threadSteeringInspectResultSchema.parse({ delivery });
40817
+ await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(result) }));
40818
+ }
40386
40819
  async #inspectThreadCommands(request) {
40387
40820
  const params = threadCommandsInspectParamsSchema.safeParse(request.params);
40388
40821
  if (!params.success) {
@@ -40546,7 +40979,7 @@ var AppServerHost = class {
40546
40979
  if (thread.running || thread.activeTurnId) {
40547
40980
  throw new ExternalCommandError(-32072, "External Thread already has an active operation");
40548
40981
  }
40549
- const turnId = requestedTurnId ?? hostTurnIdSchema.parse(randomUUID14());
40982
+ const turnId = requestedTurnId ?? hostTurnIdSchema.parse(randomUUID16());
40550
40983
  const projection = {
40551
40984
  projector: new CodexTurnProjector({
40552
40985
  threadId: thread.id,
@@ -40634,6 +41067,26 @@ var AppServerHost = class {
40634
41067
  if (!projected.effectiveModel) {
40635
41068
  throw new Error("Harness Session did not report an effective Model");
40636
41069
  }
41070
+ const transportModelId = encodeExternalTransportSelection(thread.harnessId, {
41071
+ ...decodeExternalTransportSelection(thread.harnessId, thread.transportModelId) ?? {},
41072
+ model: projected.effectiveModel
41073
+ });
41074
+ thread.transportModelId = transportModelId;
41075
+ thread.requestedModel = projected.effectiveModel;
41076
+ try {
41077
+ thread.record = await this.#repository.setTransportModelId(
41078
+ thread.record.hostThreadId,
41079
+ transportModelId
41080
+ );
41081
+ } catch (error51) {
41082
+ this.#diagnose(error51);
41083
+ }
41084
+ thread.thread = externalThreadValue({
41085
+ record: { ...thread.record, transportModelId },
41086
+ turns: thread.turns,
41087
+ sessionId: thread.sessionId,
41088
+ running: thread.running
41089
+ });
40637
41090
  await this.#writer.json(rpcEnvelope(request, { result: jsonValueSchema.parse(projected) }));
40638
41091
  } catch (error51) {
40639
41092
  await this.#writer.json(
@@ -40963,9 +41416,20 @@ var AppServerHost = class {
40963
41416
  await this.#writer.json(rpcError(request, resolution.error.code, resolution.error.message));
40964
41417
  return true;
40965
41418
  }
41419
+ /** Refresh history before Desktop acts on native Turn boundaries. */
40966
41420
  #refreshExternalThread(thread) {
40967
41421
  return this.#externalRuntime.refresh(thread);
40968
41422
  }
41423
+ #markExternalHistoryRead(thread, turns) {
41424
+ if (!thread.historyHydrated) return;
41425
+ for (const turnId of thread.steeredTurnIds) {
41426
+ if (turnId !== thread.activeTurnId && turns.some(
41427
+ (turn) => turn.id === turnId && (turn.status === "completed" || turn.status === "failed" || turn.status === "interrupted")
41428
+ )) {
41429
+ thread.steeredTurnIds.delete(turnId);
41430
+ }
41431
+ }
41432
+ }
40969
41433
  #persistTerminalIdentity(thread, event) {
40970
41434
  return this.#externalRuntime.persistTerminalIdentity(thread, event);
40971
41435
  }
@@ -41167,17 +41631,19 @@ var AppServerHost = class {
41167
41631
  return;
41168
41632
  }
41169
41633
  }
41634
+ const readTurns = includeTurns ? [...this.#externalHistoryTurns(thread)] : [];
41170
41635
  await this.#writer.json(
41171
41636
  rpcEnvelope(request, {
41172
41637
  result: {
41173
41638
  thread: {
41174
41639
  ...thread.thread,
41175
41640
  ...await this.#externalSectionFields(thread.record.hostThreadId),
41176
- turns: includeTurns ? this.#externalHistoryTurns(thread) : []
41641
+ turns: readTurns
41177
41642
  }
41178
41643
  }
41179
41644
  })
41180
41645
  );
41646
+ if (includeTurns) this.#markExternalHistoryRead(thread, readTurns);
41181
41647
  await this.#replayExternalUsage(thread);
41182
41648
  }
41183
41649
  async #listExternalHistory(request, thread, params, historyFresh) {
@@ -41194,6 +41660,9 @@ var AppServerHost = class {
41194
41660
  const turns = this.#externalHistoryTurns(thread);
41195
41661
  const result = request.method === "thread/turns/list" ? listExternalTurns(turns, params) : listExternalItems(turns, params);
41196
41662
  await this.#writer.json(rpcEnvelope(request, { result }));
41663
+ if (request.method === "thread/turns/list") {
41664
+ this.#markExternalHistoryRead(thread, result.data);
41665
+ }
41197
41666
  } catch (error51) {
41198
41667
  await this.#writer.json(
41199
41668
  rpcError(
@@ -41212,7 +41681,7 @@ var AppServerHost = class {
41212
41681
  return;
41213
41682
  }
41214
41683
  }
41215
- const turns = this.#externalHistoryTurns(thread);
41684
+ const turns = [...this.#externalHistoryTurns(thread)];
41216
41685
  const responseThread = {
41217
41686
  ...await this.#withExternalSection(thread.thread),
41218
41687
  turns: params.excludeTurns === true ? [] : turns
@@ -41244,6 +41713,8 @@ var AppServerHost = class {
41244
41713
  }
41245
41714
  })
41246
41715
  );
41716
+ if (params.excludeTurns !== true) this.#markExternalHistoryRead(thread, turns);
41717
+ else if (initialTurnsPage) this.#markExternalHistoryRead(thread, initialTurnsPage.data);
41247
41718
  } catch (error51) {
41248
41719
  await this.#writer.json(
41249
41720
  rpcError(
@@ -41386,10 +41857,15 @@ var AppServerHost = class {
41386
41857
  assertActive,
41387
41858
  typeof requestObject(request).clientUserMessageId === "string" ? requestObject(request).clientUserMessageId : void 0,
41388
41859
  requestObject(request)[THREAD_REFERENCE_SOURCE_HOST_PARAM]
41389
- )
41860
+ ),
41861
+ (input) => this.#insertExternalInput(thread, input)
41390
41862
  );
41391
41863
  try {
41392
- await this.#writer.json(rpcEnvelope(request, { result: { turnId: started.turnId } }));
41864
+ await this.#writer.json(
41865
+ rpcEnvelope(request, {
41866
+ result: { turnId: started.turnId, delivery: started.delivery }
41867
+ })
41868
+ );
41393
41869
  } finally {
41394
41870
  started.gate.resolve();
41395
41871
  }
@@ -41405,6 +41881,34 @@ var AppServerHost = class {
41405
41881
  this.#signalActiveWorkChanged();
41406
41882
  }
41407
41883
  }
41884
+ /** Native same-Turn input; `insertNativeSteer` owns the delivery and its fallback signal. */
41885
+ async #insertExternalInput(thread, input) {
41886
+ await insertNativeSteer(thread, input, {
41887
+ project: async (turnId, item, clientUserMessageId) => {
41888
+ const projection = this.#projectedTurn(thread, turnId);
41889
+ if (clientUserMessageId) {
41890
+ projection.projector.bindUserMessageClientId(item.itemId, clientUserMessageId);
41891
+ }
41892
+ await this.#projectHarnessOutput(thread, {
41893
+ kind: "event",
41894
+ event: { type: "item.started", turnId, item }
41895
+ });
41896
+ await this.#projectHarnessOutput(thread, {
41897
+ kind: "event",
41898
+ event: {
41899
+ type: "item.completed",
41900
+ turnId,
41901
+ snapshot: { item, outcome: { status: "succeeded" } }
41902
+ }
41903
+ });
41904
+ },
41905
+ diagnose: (error51) => this.#diagnose(error51)
41906
+ });
41907
+ return {
41908
+ turnId: input.turnId,
41909
+ gate: { promise: Promise.resolve(), resolve: () => void 0 }
41910
+ };
41911
+ }
41408
41912
  /** Keep command restoration and dispatch identical for start and steer submissions. */
41409
41913
  async #beginExternalInputTurn(thread, inputText, assertActive, clientUserMessageId, sourceHostId) {
41410
41914
  const text = restoreHarnessCommandMentions(inputText);
@@ -41462,7 +41966,7 @@ var AppServerHost = class {
41462
41966
  if (thread.running || thread.activeTurnId || this.#pendingExternalCommandRequests.has(thread.id)) {
41463
41967
  throw new ExternalSteerError(-32072, "External Thread already has an active Turn");
41464
41968
  }
41465
- const turnId = hostTurnIdSchema.parse(randomUUID14());
41969
+ const turnId = hostTurnIdSchema.parse(randomUUID16());
41466
41970
  const startedAtMs = Date.now();
41467
41971
  const projection = {
41468
41972
  projector: new CodexTurnProjector({
@@ -41757,6 +42261,9 @@ var AppServerHost = class {
41757
42261
  }
41758
42262
  return;
41759
42263
  }
42264
+ if (event.type === "turn.completed" && thread.pendingSteerProjections.size > 0) {
42265
+ await Promise.allSettled([...thread.pendingSteerProjections]);
42266
+ }
41760
42267
  const projection = this.#projectedTurn(thread, event.turnId);
41761
42268
  await this.#waitForTurnResponse(thread, event.turnId);
41762
42269
  if (event.type === "interaction.closed" && thread.ignoredInteractionIds.delete(event.interactionId)) {
@@ -41806,6 +42313,7 @@ var AppServerHost = class {
41806
42313
  thread.activeTurnId = null;
41807
42314
  if (!projection.projector.hasOpenDetachedItems) thread.projectedTurns.delete(event.turnId);
41808
42315
  thread.responseGates.delete(event.turnId);
42316
+ thread.pendingSteerProjections.clear();
41809
42317
  this.#signalActiveWorkChanged();
41810
42318
  const delegation = await this.#repository.getDelegationByChild(thread.record.hostThreadId);
41811
42319
  if (delegation) {
@@ -42096,7 +42604,7 @@ var AppServerHost = class {
42096
42604
  try {
42097
42605
  result = projection.projector.projectQuestion(
42098
42606
  interaction,
42099
- hostItemIdSchema.parse(randomUUID14())
42607
+ hostItemIdSchema.parse(randomUUID16())
42100
42608
  );
42101
42609
  } catch (error51) {
42102
42610
  this.#diagnose(error51);
@@ -42335,7 +42843,7 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
42335
42843
  var wrapper_default = import_websocket.default;
42336
42844
 
42337
42845
  // packages/host-runtime/src/remote-socket-lock.ts
42338
- import { randomUUID as randomUUID15 } from "node:crypto";
42846
+ import { randomUUID as randomUUID17 } from "node:crypto";
42339
42847
  import { chmod as chmod3, lstat as lstat5, mkdir as mkdir12, open as open9, readFile as readFile16, readdir as readdir5, rename as rename11, rm as rm11 } from "node:fs/promises";
42340
42848
  import { uptime } from "node:os";
42341
42849
  import path26 from "node:path";
@@ -42461,7 +42969,7 @@ async function preparePrivateLockDirectory(lockDirectory) {
42461
42969
  await chmod3(lockDirectory, 448);
42462
42970
  }
42463
42971
  async function writeLockRecordAtomic(lockDirectory, entryPath, record4) {
42464
- const temporary = path26.join(lockDirectory, `.tmp-${record4.ownerToken}-${randomUUID15()}`);
42972
+ const temporary = path26.join(lockDirectory, `.tmp-${record4.ownerToken}-${randomUUID17()}`);
42465
42973
  const handle = await open9(temporary, "wx", 384);
42466
42974
  try {
42467
42975
  await handle.writeFile(`${JSON.stringify(record4)}
@@ -42536,7 +43044,7 @@ async function withRemoteAppServerSocketInitializationLock(socketPath, action) {
42536
43044
  const legacyPath = `${socketPath}.initializing`;
42537
43045
  const lockDirectory = `${socketPath}.initializers`;
42538
43046
  await preparePrivateLockDirectory(lockDirectory);
42539
- const ownerToken = randomUUID15();
43047
+ const ownerToken = randomUUID17();
42540
43048
  const entryPath = path26.join(lockDirectory, lockEntryName(ownerToken));
42541
43049
  const baseRecord = {
42542
43050
  version: 2,
@@ -43047,12 +43555,12 @@ function createRemoteAppServerWebSocketListener(input) {
43047
43555
 
43048
43556
  // packages/host-runtime/src/remote-official-app-server.ts
43049
43557
  import { spawn as spawn5 } from "node:child_process";
43050
- import { randomUUID as randomUUID16 } from "node:crypto";
43558
+ import { randomUUID as randomUUID18 } from "node:crypto";
43051
43559
  import { lstat as lstat7, rm as rm13, stat as stat7 } from "node:fs/promises";
43052
43560
  import path28 from "node:path";
43053
43561
  var DEFAULT_CLOSE_TIMEOUT_MS = 2e3;
43054
43562
  var DEFAULT_LISTEN_TIMEOUT_MS = 1e4;
43055
- function remoteOfficialAppServerSocketPath(desktopControlSocketPath, token = randomUUID16()) {
43563
+ function remoteOfficialAppServerSocketPath(desktopControlSocketPath, token = randomUUID18()) {
43056
43564
  if (!path28.posix.isAbsolute(desktopControlSocketPath)) {
43057
43565
  throw new Error("Desktop control socket path must be absolute");
43058
43566
  }
@@ -44271,11 +44779,11 @@ async function readRemoteDelegationThread(environment, input) {
44271
44779
  }
44272
44780
 
44273
44781
  // packages/host-runtime/src/shared-thread-owner.ts
44274
- import { randomUUID as randomUUID18 } from "node:crypto";
44782
+ import { randomUUID as randomUUID20 } from "node:crypto";
44275
44783
  import { PassThrough as PassThrough3 } from "node:stream";
44276
44784
 
44277
44785
  // packages/host-runtime/src/shared-thread-peer.ts
44278
- import { randomUUID as randomUUID17 } from "node:crypto";
44786
+ import { randomUUID as randomUUID19 } from "node:crypto";
44279
44787
  function object5(value2) {
44280
44788
  return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
44281
44789
  }
@@ -44289,7 +44797,7 @@ var StreamThreadPeer = class {
44289
44797
  input;
44290
44798
  output;
44291
44799
  stop;
44292
- #prefix = `shared:${randomUUID17()}:`;
44800
+ #prefix = `shared:${randomUUID19()}:`;
44293
44801
  #pending = /* @__PURE__ */ new Map();
44294
44802
  #listeners = /* @__PURE__ */ new Set();
44295
44803
  #ended = Promise.withResolvers();
@@ -44367,7 +44875,7 @@ var SharedThreadOwner = class {
44367
44875
  input = new PassThrough3();
44368
44876
  output = new PassThrough3();
44369
44877
  #peer = new StreamThreadPeer(this.output, this.input, () => this.input.end());
44370
- #prefix = `shared-interaction:${randomUUID18()}:`;
44878
+ #prefix = `shared-interaction:${randomUUID20()}:`;
44371
44879
  #interactions = /* @__PURE__ */ new Map();
44372
44880
  #listeners = /* @__PURE__ */ new Set();
44373
44881
  #receipts = /* @__PURE__ */ new Map();
@@ -44677,7 +45185,7 @@ var SharedThreadBridge = class {
44677
45185
  };
44678
45186
 
44679
45187
  // packages/host-runtime/src/remote-control-app-server.ts
44680
- import { randomUUID as randomUUID19 } from "node:crypto";
45188
+ import { randomUUID as randomUUID21 } from "node:crypto";
44681
45189
  import { mkdir as mkdir15, open as open10, rename as rename12, rm as rm14 } from "node:fs/promises";
44682
45190
  import path33 from "node:path";
44683
45191
  var REMOTE_CONTROL_BRIDGE_PIPE_ENV = "CODEXHOST_REMOTE_CONTROL_BRIDGE_PIPE";
@@ -44698,7 +45206,7 @@ function nodeCompatibleWindowsPath(value2) {
44698
45206
  if (value2.startsWith("\\\\?\\")) return value2.slice(4);
44699
45207
  return value2;
44700
45208
  }
44701
- function remoteControlBridgePipePath(processId = process.pid, instanceId = randomUUID19()) {
45209
+ function remoteControlBridgePipePath(processId = process.pid, instanceId = randomUUID21()) {
44702
45210
  const safeInstance = instanceId.replaceAll(/[^a-zA-Z0-9-]/gu, "");
44703
45211
  if (!safeInstance) throw new Error("Remote Control bridge instance ID is invalid");
44704
45212
  return `\\\\.\\pipe\\codexhost-remote-control-${processId}-${safeInstance}`;
@@ -44713,7 +45221,7 @@ async function publishRemoteControlAppServerDescriptor(plan) {
44713
45221
  await mkdir15(directory, { recursive: true, mode: 448 });
44714
45222
  const temporaryPath = path33.join(
44715
45223
  directory,
44716
- `.${REMOTE_CONTROL_BRIDGE_DESCRIPTOR_FILE}.${plan.descriptor.ownerPid}.${randomUUID19()}.tmp`
45224
+ `.${REMOTE_CONTROL_BRIDGE_DESCRIPTOR_FILE}.${plan.descriptor.ownerPid}.${randomUUID21()}.tmp`
44717
45225
  );
44718
45226
  const handle = await open10(temporaryPath, "wx", 384);
44719
45227
  try {
@@ -45235,12 +45743,19 @@ function createHostUpdateCoordinator(options2) {
45235
45743
  }
45236
45744
 
45237
45745
  // packages/host-runtime/src/usage-statistics.ts
45238
- import { randomUUID as randomUUID20 } from "node:crypto";
45746
+ import { randomUUID as randomUUID22 } from "node:crypto";
45239
45747
  import { mkdir as mkdir17, readFile as readFile17, readdir as readdir6, rename as rename14, rm as rm16, writeFile as writeFile10 } from "node:fs/promises";
45240
45748
  import path36 from "node:path";
45241
45749
  import { setImmediate as yieldToEventLoop } from "node:timers/promises";
45242
45750
 
45243
45751
  // packages/harness-adapter/dist/usage-statistics.js
45752
+ var HarnessUsageSourceChangedError = class extends Error {
45753
+ code = "HARNESS_USAGE_SOURCE_CHANGED";
45754
+ /** Plugins may bundle their own copy of this contract, so instanceof is insufficient. */
45755
+ static is(error51) {
45756
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "HARNESS_USAGE_SOURCE_CHANGED";
45757
+ }
45758
+ };
45244
45759
  var MAX_SESSION_ID_LENGTH = 512;
45245
45760
  var MAX_CWD_LENGTH = 4096;
45246
45761
  var MAX_SESSION_TITLE_LENGTH = 200;
@@ -45252,10 +45767,13 @@ function usageSessionTitle(value2) {
45252
45767
  return void 0;
45253
45768
  if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
45254
45769
  return normalized;
45255
- const characters = [...normalized];
45256
- if (characters.length <= MAX_SESSION_TITLE_LENGTH)
45257
- return normalized;
45258
- return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
45770
+ let title = "";
45771
+ for (const character of normalized) {
45772
+ if (title.length + character.length >= MAX_SESSION_TITLE_LENGTH)
45773
+ break;
45774
+ title += character;
45775
+ }
45776
+ return `${title.trimEnd()}…`;
45259
45777
  }
45260
45778
  function sessionIdOk(value2) {
45261
45779
  return typeof value2 === "string" && value2.length > 0 && value2.length <= MAX_SESSION_ID_LENGTH;
@@ -45340,6 +45858,95 @@ function withHostSessionTitles(result, records) {
45340
45858
  };
45341
45859
  }
45342
45860
 
45861
+ // packages/host-runtime/src/usage-statistics-sources.ts
45862
+ import { setTimeout as delay6 } from "node:timers/promises";
45863
+ async function* readUsageStatisticsSources(capability, sources, signal) {
45864
+ let pending = sources;
45865
+ for (let attempt = 0; ; attempt++) {
45866
+ const changed = [];
45867
+ for (const source of pending) {
45868
+ signal.throwIfAborted();
45869
+ try {
45870
+ const entries = (await capability.readSource(source.id, signal)).map(parseHarnessUsageEntry).filter((entry) => entry !== null);
45871
+ yield { source, entries };
45872
+ } catch (error51) {
45873
+ signal.throwIfAborted();
45874
+ if (HarnessUsageSourceChangedError.is(error51) && attempt < 2) changed.push(source);
45875
+ else yield { source, error: error51 };
45876
+ }
45877
+ }
45878
+ if (!changed.length) return;
45879
+ await delay6(50 * (attempt + 1), void 0, { signal });
45880
+ let current;
45881
+ try {
45882
+ current = new Map(
45883
+ (await capability.listSources(signal)).map((source) => [source.id, source])
45884
+ );
45885
+ } catch (error51) {
45886
+ signal.throwIfAborted();
45887
+ for (const source of changed) yield { source, error: error51 };
45888
+ return;
45889
+ }
45890
+ const next = [];
45891
+ for (const source of changed) {
45892
+ const fresh = current.get(source.id);
45893
+ if (fresh) next.push(fresh);
45894
+ else yield { source, removed: true };
45895
+ }
45896
+ pending = next;
45897
+ }
45898
+ }
45899
+
45900
+ // packages/host-runtime/src/usage-statistics-calendar.ts
45901
+ function dateKey(year, month, day) {
45902
+ return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
45903
+ }
45904
+ function createUsageStatisticsCalendar(timeZone) {
45905
+ const localZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
45906
+ const zone = timeZone ?? localZone;
45907
+ if (zone === localZone) {
45908
+ return {
45909
+ timeZone: zone,
45910
+ at(ms) {
45911
+ const date5 = new Date(ms);
45912
+ return {
45913
+ date: dateKey(date5.getFullYear(), date5.getMonth() + 1, date5.getDate()),
45914
+ weekday: date5.getDay(),
45915
+ hour: date5.getHours()
45916
+ };
45917
+ }
45918
+ };
45919
+ }
45920
+ const format = new Intl.DateTimeFormat("en-US", {
45921
+ timeZone: zone,
45922
+ calendar: "gregory",
45923
+ numberingSystem: "latn",
45924
+ year: "numeric",
45925
+ month: "2-digit",
45926
+ day: "2-digit",
45927
+ hour: "2-digit",
45928
+ hourCycle: "h23"
45929
+ });
45930
+ return {
45931
+ timeZone: zone,
45932
+ at(ms) {
45933
+ const parts = format.formatToParts(ms);
45934
+ const part = (type) => Number(parts.find((value2) => value2.type === type)?.value);
45935
+ const year = part("year"), month = part("month"), day = part("day");
45936
+ return {
45937
+ date: dateKey(year, month, day),
45938
+ weekday: new Date(Date.UTC(year, month - 1, day)).getUTCDay(),
45939
+ hour: part("hour")
45940
+ };
45941
+ }
45942
+ };
45943
+ }
45944
+ function shiftUsageDate(date5, days) {
45945
+ const value2 = /* @__PURE__ */ new Date(`${date5}T12:00:00Z`);
45946
+ value2.setUTCDate(value2.getUTCDate() + days);
45947
+ return dateKey(value2.getUTCFullYear(), value2.getUTCMonth() + 1, value2.getUTCDate());
45948
+ }
45949
+
45343
45950
  // packages/host-runtime/src/usage-statistics.ts
45344
45951
  var CACHE_VERSION = 7;
45345
45952
  var STALE_AFTER_MS = 1e4;
@@ -45400,17 +46007,6 @@ function expand(value2, intern) {
45400
46007
  function message(error51) {
45401
46008
  return error51 instanceof Error ? error51.message : String(error51);
45402
46009
  }
45403
- function localDate2(ms) {
45404
- const date5 = new Date(ms);
45405
- const month = String(date5.getMonth() + 1).padStart(2, "0");
45406
- const day = String(date5.getDate()).padStart(2, "0");
45407
- return `${date5.getFullYear()}-${month}-${day}`;
45408
- }
45409
- function shiftDate(date5, days) {
45410
- const value2 = /* @__PURE__ */ new Date(`${date5}T12:00:00`);
45411
- value2.setDate(value2.getDate() + days);
45412
- return localDate2(value2.getTime());
45413
- }
45414
46010
  var SESSIONS_PER_MEASURE = 50;
45415
46011
  var MODELS_PER_SESSION = 16;
45416
46012
  function emptyTotals() {
@@ -45597,23 +46193,27 @@ var UsageStatistics = class {
45597
46193
  }
45598
46194
  for (const { harness: harness2, capability, ids } of listed) {
45599
46195
  const cache = this.#cache(harness2);
45600
- const present = /* @__PURE__ */ new Set();
46196
+ const present = new Set(ids.map(({ id: id2 }) => id2));
45601
46197
  let failed = null;
45602
- for (const { id: id2, fingerprint } of ids) {
45603
- present.add(id2);
45604
- if (cache.sources.get(id2)?.fingerprint !== fingerprint) {
45605
- try {
45606
- const entries = (await capability.readSource(id2, signal)).map(parseHarnessUsageEntry).filter((entry) => entry !== null);
45607
- cache.sources.set(id2, { fingerprint, entries });
46198
+ const pending = ids.filter(
46199
+ ({ id: id2, fingerprint }) => cache.sources.get(id2)?.fingerprint !== fingerprint
46200
+ );
46201
+ this.#progress.read += ids.length - pending.length;
46202
+ try {
46203
+ for await (const result of readUsageStatisticsSources(capability, pending, signal)) {
46204
+ const { id: id2, fingerprint } = result.source;
46205
+ if ("entries" in result) {
46206
+ cache.sources.set(id2, { fingerprint, entries: result.entries });
45608
46207
  cache.dirty = true;
45609
46208
  this.#generation += 1;
45610
- } catch (error51) {
45611
- if (signal.aborted) return;
45612
- failed ??= message(error51);
45613
- }
46209
+ } else if ("error" in result) failed ??= message(result.error);
46210
+ else present.delete(id2);
46211
+ this.#progress.read += 1;
45614
46212
  await yieldToEventLoop();
45615
46213
  }
45616
- this.#progress.read += 1;
46214
+ } catch (error51) {
46215
+ if (signal.aborted) return;
46216
+ throw error51;
45617
46217
  }
45618
46218
  for (const id2 of cache.sources.keys()) {
45619
46219
  if (!present.has(id2)) {
@@ -45678,7 +46278,7 @@ var UsageStatistics = class {
45678
46278
  async #persist(harness2, cache) {
45679
46279
  if (!cache.dirty || this.#now() - cache.persistedAtMs < PERSIST_INTERVAL_MS) return;
45680
46280
  const file2 = this.#file(harness2);
45681
- const temporary = `${file2}.${randomUUID20()}.tmp`;
46281
+ const temporary = `${file2}.${randomUUID22()}.tmp`;
45682
46282
  try {
45683
46283
  await mkdir17(this.#directory, { recursive: true });
45684
46284
  const sources = [...cache.sources].map(([id2, source]) => [
@@ -45696,9 +46296,10 @@ var UsageStatistics = class {
45696
46296
  await rm16(temporary, { force: true });
45697
46297
  }
45698
46298
  }
45699
- /** Every request once, per Harness in source order, with its local date and hour. */
45700
- #indexed() {
45701
- if (this.#index?.generation === this.#generation) return this.#index.entries;
46299
+ /** Every request once, per Harness in source order, in the requested calendar. */
46300
+ #indexed(calendar) {
46301
+ if (this.#index?.generation === this.#generation && this.#index.timeZone === calendar.timeZone)
46302
+ return this.#index.entries;
45702
46303
  const entries = [];
45703
46304
  for (const [harness2, cache] of this.#caches) {
45704
46305
  const seen = /* @__PURE__ */ new Set();
@@ -45706,26 +46307,20 @@ var UsageStatistics = class {
45706
46307
  for (const entry of parsed2) {
45707
46308
  if (seen.has(entry.id)) continue;
45708
46309
  seen.add(entry.id);
45709
- const at = new Date(entry.occurredAtMs);
45710
- entries.push({
45711
- harness: harness2,
45712
- entry,
45713
- date: localDate2(entry.occurredAtMs),
45714
- weekday: at.getDay(),
45715
- hour: at.getHours()
45716
- });
46310
+ entries.push({ harness: harness2, entry, ...calendar.at(entry.occurredAtMs) });
45717
46311
  }
45718
46312
  }
45719
46313
  }
45720
- this.#index = { generation: this.#generation, entries };
46314
+ this.#index = { generation: this.#generation, timeZone: calendar.timeZone, entries };
45721
46315
  return entries;
45722
46316
  }
45723
46317
  #aggregate(params, now, lookup) {
45724
46318
  const { range } = params;
45725
46319
  const days = RANGE_DAYS[range];
45726
- const to = localDate2(now);
46320
+ const calendar = createUsageStatisticsCalendar(params.timeZone);
46321
+ const to = calendar.at(now).date;
45727
46322
  let from = null;
45728
- if (days !== null) from = shiftDate(to, -days + 1);
46323
+ if (days !== null) from = shiftUsageDate(to, -days + 1);
45729
46324
  const inRange = (date5) => (from === null || date5 >= from) && date5 <= to;
45730
46325
  const day = params.date !== void 0 && inRange(params.date) ? params.date : null;
45731
46326
  const harnessFilter = params.harness ?? null;
@@ -45766,7 +46361,7 @@ var UsageStatistics = class {
45766
46361
  const harnessPriced = /* @__PURE__ */ new Map();
45767
46362
  const byProject = /* @__PURE__ */ new Map();
45768
46363
  const sessions = /* @__PURE__ */ new Map();
45769
- for (const indexed of this.#indexed()) {
46364
+ for (const indexed of this.#indexed(calendar)) {
45770
46365
  const { harness: harness2, entry, date: date5 } = indexed;
45771
46366
  if (!inRange(date5)) continue;
45772
46367
  const model2 = entry.model ?? null;
@@ -46294,7 +46889,7 @@ async function runHostRuntime(input) {
46294
46889
  }
46295
46890
 
46296
46891
  // packages/host-runtime/src/remote-host-install.ts
46297
- import { createHash as createHash11, randomUUID as randomUUID21 } from "node:crypto";
46892
+ import { createHash as createHash11, randomUUID as randomUUID23 } from "node:crypto";
46298
46893
  import { constants as fsConstants } from "node:fs";
46299
46894
  import {
46300
46895
  access as access3,
@@ -46393,7 +46988,7 @@ async function writeAtomic(filePath, contents, mode) {
46393
46988
  await mkdir18(path38.dirname(filePath), { recursive: true, mode: 448 });
46394
46989
  const temporary = path38.join(
46395
46990
  path38.dirname(filePath),
46396
- `.${path38.basename(filePath)}.${process.pid}.${randomUUID21()}.tmp`
46991
+ `.${path38.basename(filePath)}.${process.pid}.${randomUUID23()}.tmp`
46397
46992
  );
46398
46993
  try {
46399
46994
  if (typeof contents === "string") {
@@ -46417,7 +47012,7 @@ async function writeAtomicExecutable(filePath, sourcePath) {
46417
47012
  await mkdir18(path38.dirname(filePath), { recursive: true, mode: 448 });
46418
47013
  const temporary = path38.join(
46419
47014
  path38.dirname(filePath),
46420
- `.${path38.basename(filePath)}.${process.pid}.${randomUUID21()}.tmp`
47015
+ `.${path38.basename(filePath)}.${process.pid}.${randomUUID23()}.tmp`
46421
47016
  );
46422
47017
  try {
46423
47018
  await copyFile4(sourcePath, temporary);
@@ -47158,6 +47753,7 @@ var harnessBrokerMethodSchema = external_exports.enum([
47158
47753
  "adapter.open",
47159
47754
  "adapter.subagent.readSnapshot",
47160
47755
  "session.readSnapshot",
47756
+ "session.readCapabilities",
47161
47757
  "session.refreshUsage",
47162
47758
  "session.execute",
47163
47759
  "session.commands.list",
@@ -47340,6 +47936,11 @@ var turnStartSchema = external_exports.object({
47340
47936
  turnId: hostTurnIdSchema,
47341
47937
  input: external_exports.array(textInputSchema)
47342
47938
  }).strict();
47939
+ var turnSteerSchema = external_exports.object({
47940
+ type: external_exports.literal("turn.steer"),
47941
+ turnId: hostTurnIdSchema,
47942
+ input: external_exports.array(textInputSchema)
47943
+ }).strict();
47343
47944
  var turnCancelSchema = external_exports.object({ type: external_exports.literal("turn.cancel"), turnId: hostTurnIdSchema }).strict();
47344
47945
  var interactionResponseSchema = external_exports.union([
47345
47946
  external_exports.object({
@@ -47365,6 +47966,7 @@ var permissionSelectSchema = external_exports.object({
47365
47966
  }).strict();
47366
47967
  var brokerHostCommandSchema = external_exports.discriminatedUnion("type", [
47367
47968
  turnStartSchema,
47969
+ turnSteerSchema,
47368
47970
  turnCancelSchema,
47369
47971
  interactionRespondSchema,
47370
47972
  modelSelectSchema,
@@ -47499,7 +48101,7 @@ var harnessOutputSchema = external_exports.custom((value2) => {
47499
48101
  });
47500
48102
 
47501
48103
  // packages/harness-broker/dist/server.js
47502
- import { randomBytes as randomBytes5, randomUUID as randomUUID22 } from "node:crypto";
48104
+ import { randomBytes as randomBytes5, randomUUID as randomUUID24 } from "node:crypto";
47503
48105
  import { chmod as chmod6, lstat as lstat10, mkdir as mkdir19, open as open11, readFile as readFile19, rename as rename16, rm as rm18 } from "node:fs/promises";
47504
48106
  import net4, {} from "node:net";
47505
48107
  import path41 from "node:path";
@@ -47558,7 +48160,7 @@ async function assertNoLiveDescriptor(descriptorPath) {
47558
48160
  }
47559
48161
  }
47560
48162
  async function publishDescriptor(descriptorPath, descriptor) {
47561
- const temporary = `${descriptorPath}.${process.pid}.${randomUUID22()}.tmp`;
48163
+ const temporary = `${descriptorPath}.${process.pid}.${randomUUID24()}.tmp`;
47562
48164
  const handle = await open11(temporary, "wx", 384);
47563
48165
  try {
47564
48166
  await handle.writeFile(`${JSON.stringify(descriptor)}
@@ -47583,6 +48185,7 @@ function sessionMetadata(record4) {
47583
48185
  sessionId: record4.id,
47584
48186
  sessionGeneration: record4.generation,
47585
48187
  capabilities: record4.session.capabilities,
48188
+ readCapabilities: true,
47586
48189
  initialState: {
47587
48190
  ...initialState,
47588
48191
  ...record4.nativeRef ? { nativeRef: record4.nativeRef } : {},
@@ -47635,7 +48238,7 @@ async function startHarnessBrokerServer(input) {
47635
48238
  await assertNoLiveDescriptor(input.descriptorPath);
47636
48239
  if (process.platform !== "win32")
47637
48240
  await prepareUnixSocketPath(input.socketPath);
47638
- const generation = input.generation ?? randomUUID22();
48241
+ const generation = input.generation ?? randomUUID24();
47639
48242
  const token = input.token ?? randomBytes5(32).toString("hex");
47640
48243
  const descriptor = {
47641
48244
  schemaVersion: 1,
@@ -47676,7 +48279,7 @@ async function startHarnessBrokerServer(input) {
47676
48279
  };
47677
48280
  const server = net4.createServer((socket) => {
47678
48281
  const state = {
47679
- id: randomUUID22(),
48282
+ id: randomUUID24(),
47680
48283
  socket,
47681
48284
  authenticated: false,
47682
48285
  inputSequence: 0,
@@ -47853,8 +48456,11 @@ async function startHarnessBrokerServer(input) {
47853
48456
  retireWhenIdle = false;
47854
48457
  if (request.method === "adapter.credits") {
47855
48458
  harnessAccountListParamsSchema.parse(request.params);
47856
- const adapter = input.adapter;
47857
- return accountCreditsSnapshotSchema.nullable().parse(adapter.credits?.() ?? null);
48459
+ const value2 = input.adapter.credits?.();
48460
+ const credits = parseHarnessAccountCredits(value2);
48461
+ if (value2 != null && !credits)
48462
+ throw new Error("Invalid account credits snapshot");
48463
+ return credits;
47858
48464
  }
47859
48465
  if (request.method === "adapter.inspectAccount") {
47860
48466
  harnessAccountListParamsSchema.parse(request.params);
@@ -47911,7 +48517,7 @@ async function startHarnessBrokerServer(input) {
47911
48517
  }
47912
48518
  };
47913
48519
  }
47914
- const openReservation = randomUUID22();
48520
+ const openReservation = randomUUID24();
47915
48521
  if (sourceKey)
47916
48522
  nativeWriters.set(sourceKey, openReservation);
47917
48523
  const provisionalReservation = openInput.kind === "create" ? openReservation : void 0;
@@ -48055,6 +48661,8 @@ async function startHarnessBrokerServer(input) {
48055
48661
  let result;
48056
48662
  if (command.type === "turn.start")
48057
48663
  result = await record5.session.execute(command);
48664
+ else if (command.type === "turn.steer")
48665
+ result = await record5.session.execute(command);
48058
48666
  else if (command.type === "turn.cancel")
48059
48667
  result = await record5.session.execute(command);
48060
48668
  else if (command.type === "interaction.respond") {
@@ -48086,6 +48694,10 @@ async function startHarnessBrokerServer(input) {
48086
48694
  }
48087
48695
  return result;
48088
48696
  }
48697
+ if (request.method === "session.readCapabilities") {
48698
+ const session = requireSession(request.params).session;
48699
+ return session.readCapabilities ? session.readCapabilities() : session.capabilities;
48700
+ }
48089
48701
  if (request.method === "session.readSnapshot") {
48090
48702
  return requireSession(request.params).session.readSnapshot();
48091
48703
  }
@@ -48212,7 +48824,7 @@ async function startHarnessBrokerServer(input) {
48212
48824
  }
48213
48825
  state.authenticated = true;
48214
48826
  state.inputSequence = 1;
48215
- await send({ kind: "response", id: randomUUID22(), ok: true, value: { ready: true } });
48827
+ await send({ kind: "response", id: randomUUID24(), ok: true, value: { ready: true } });
48216
48828
  return;
48217
48829
  }
48218
48830
  const parsed2 = harnessBrokerRequestSchema.safeParse(raw);