@botiverse/raft-daemon 0.72.3 → 0.72.4-play.20260710140617

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.
@@ -1,5 +1,5 @@
1
1
  // src/core.ts
2
- import path20 from "path";
2
+ import path23 from "path";
3
3
  import os8 from "os";
4
4
  import { createRequire as createRequire3 } from "module";
5
5
  import { accessSync } from "fs";
@@ -636,12 +636,12 @@ function parseModelConfig(runtimeModels, runtime, value, fallback, usesLegacyCla
636
636
  if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
637
637
  return { kind: "custom", name: value.name.trim() };
638
638
  }
639
- if (value.kind === "preset" && typeof value.id === "string" && value.id.trim() && isPresetRuntimeModel(runtimeModels, runtime, value.id.trim())) {
640
- return { kind: "preset", id: value.id.trim() };
641
- }
642
639
  if (usesLegacyClaudeCustomProvider && runtime === "claude" && legacyCustomModel?.trim()) {
643
640
  return { kind: "custom", name: legacyCustomModel.trim() };
644
641
  }
642
+ if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
643
+ return { kind: "preset", id: value.id.trim() };
644
+ }
645
645
  return modelConfigFromLegacy(runtimeModels, runtime, fallback);
646
646
  }
647
647
  function parseModeConfig(value) {
@@ -1366,6 +1366,9 @@ var PROMOTED_IDENTITY_ATTRS = [
1366
1366
  "caller_kind"
1367
1367
  ];
1368
1368
  var PROMOTED_CLOSED_ATTRS = [
1369
+ "router_reason",
1370
+ "stale_owner_cleanup_result",
1371
+ "stale_owner_cleanup_reason",
1369
1372
  "outcome",
1370
1373
  "reason",
1371
1374
  "event_kind",
@@ -1374,6 +1377,8 @@ var PROMOTED_CLOSED_ATTRS = [
1374
1377
  "activity_write_site",
1375
1378
  "activity_source",
1376
1379
  "hint_source",
1380
+ "weak_source",
1381
+ "competing_fact",
1377
1382
  "resolved_activity",
1378
1383
  "previous_activity",
1379
1384
  "next_activity",
@@ -1459,11 +1464,20 @@ var TRACE_B0_FIELD_DEFINITIONS = defineTraceFields([
1459
1464
  { key: "served_from", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1460
1465
  { key: "authority", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1461
1466
  { key: "decided_by", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1467
+ { key: "router_reason", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1468
+ { key: "stale_owner_cleanup_result", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1469
+ { key: "stale_owner_cleanup_reason", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1462
1470
  { key: "error_class", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1463
1471
  { key: "query_name", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "query_registry" },
1464
1472
  { key: "phase", fieldClass: "query_axis", placement: "span_or_event", valueKind: "closed_enum", enumBinding: "pending_457" },
1465
1473
  { key: "eligibility_subcheck", fieldClass: "query_axis", placement: "event", valueKind: "closed_enum", enumBinding: "pending_457" },
1466
1474
  { key: "hint_source", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1475
+ { key: "candidate_activity", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1476
+ { key: "served_activity", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1477
+ { key: "write_action", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity", enumBinding: "stable", enumValues: ["none"] },
1478
+ { key: "arbitration_reason", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity", enumBinding: "stable", enumValues: ["trusted_snapshot", "owner_mirror_read_through"] },
1479
+ { key: "weak_source", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1480
+ { key: "competing_fact", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1467
1481
  { key: "resolved_activity", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1468
1482
  { key: "previous_activity", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
1469
1483
  { key: "next_activity", fieldClass: "family_query_axis", placement: "event", valueKind: "closed_enum", scope: "family:activity" },
@@ -1989,23 +2003,13 @@ var integrationUpdateAppRegistrationOperationSchema = integrationAppDraftFieldsS
1989
2003
  unsafeDemoUrlOverride: z.boolean().optional(),
1990
2004
  draftHint: draftHintSchema
1991
2005
  });
1992
- var migrationExportOperationSchema = z.object({
1993
- type: z.literal("migration:export"),
1994
- targetComputer: idOrHandleSchema,
1995
- mode: z.enum(["cooperative", "forensic"]).default("cooperative"),
1996
- prepDeadlineMs: z.number().int().positive().optional(),
1997
- transferDeadlineMs: z.number().int().positive().optional(),
1998
- arrivalDeadlineMs: z.number().int().positive().optional(),
1999
- draftHint: draftHintSchema
2000
- });
2001
2006
  var actionCardActionSchema = z.discriminatedUnion("type", [
2002
2007
  channelCreateOperationSchema,
2003
2008
  agentCreateOperationSchema,
2004
2009
  channelAddMemberOperationSchema,
2005
2010
  integrationApproveAgentLoginOperationSchema,
2006
2011
  integrationRegisterAppOperationSchema,
2007
- integrationUpdateAppRegistrationOperationSchema,
2008
- migrationExportOperationSchema
2012
+ integrationUpdateAppRegistrationOperationSchema
2009
2013
  ]);
2010
2014
 
2011
2015
  // ../shared/src/agentApiContract.ts
@@ -2027,8 +2031,8 @@ var optionalIsoTimestampSchema = z2.string().datetime().optional();
2027
2031
  var agentStatusSchema = z2.enum(["active", "inactive", "stopped"]);
2028
2032
  var reminderStatusSchema = z2.enum(["scheduled", "fired", "canceled"]);
2029
2033
  var reminderEventTypeSchema = z2.enum(["scheduled", "fired", "snoozed", "updated", "canceled"]);
2030
- var reasoningEffortSchema = z2.enum(["low", "medium", "high", "xhigh"]);
2031
- var profileVisibilityMembershipStatusSchema = z2.enum(["active", "removed"]);
2034
+ var reasoningEffortSchema = z2.enum(["low", "medium", "high", "xhigh", "max", "ultra"]);
2035
+ var profileVisibilityMembershipStatusSchema = z2.enum(["active", "left", "removed"]);
2032
2036
  var passthroughObject = (shape) => z2.object(shape).passthrough();
2033
2037
  var taskStatusSchema = z2.enum(["todo", "in_progress", "in_review", "done", "closed"]);
2034
2038
  var agentApiEventsQuerySchema = passthroughObject({
@@ -2098,6 +2102,10 @@ var agentApiSendBodySchema = passthroughObject({
2098
2102
  draftReplacedExisting: optionalBooleanSchema,
2099
2103
  seenUpToSeq: optionalNumberSchema
2100
2104
  });
2105
+ var legacyAgentSendBodySchema = agentApiSendBodySchema.extend({
2106
+ channel: z2.string().optional(),
2107
+ dm_to: z2.string().optional()
2108
+ });
2101
2109
  var agentApiMessageResolveParamsSchema = passthroughObject({
2102
2110
  msgId: z2.string().trim().min(1).transform(asMessageId)
2103
2111
  });
@@ -2704,6 +2712,51 @@ var agentApiReminderResponseSchema = passthroughObject({
2704
2712
  var agentApiReminderLogResponseSchema = passthroughObject({
2705
2713
  events: z2.array(agentApiReminderEventSummarySchema)
2706
2714
  });
2715
+ var agentApiMigrationStateSchema = z2.enum(["provisioning", "prep", "ready", "in_transit", "arriving", "completed", "aborted", "failed"]);
2716
+ var agentApiMigrationSummarySchema = passthroughObject({
2717
+ id: z2.string(),
2718
+ agentId: z2.string(),
2719
+ sourceMachineId: z2.string(),
2720
+ targetMachineId: z2.string(),
2721
+ state: agentApiMigrationStateSchema,
2722
+ manifestPath: nullableStringSchema,
2723
+ manifestSha256: nullableStringSchema,
2724
+ arrivalReportPath: nullableStringSchema,
2725
+ arrivalReportSha256: nullableStringSchema,
2726
+ abortReason: nullableStringSchema,
2727
+ failureReason: nullableStringSchema,
2728
+ prepDeadlineAt: z2.string().datetime(),
2729
+ transferDeadlineAt: z2.string().datetime(),
2730
+ arrivalDeadlineAt: z2.string().datetime(),
2731
+ readyAt: nullableStringSchema,
2732
+ flippedAt: nullableStringSchema,
2733
+ arrivedAt: nullableStringSchema,
2734
+ completedAt: nullableStringSchema,
2735
+ abortedAt: nullableStringSchema,
2736
+ revision: z2.number().int().positive(),
2737
+ createdAt: z2.string().datetime(),
2738
+ updatedAt: z2.string().datetime()
2739
+ });
2740
+ var agentApiMigrationBeginBodySchema = passthroughObject({
2741
+ targetMachineId: z2.string().trim().min(1),
2742
+ prepDeadlineMs: z2.number().int().positive().optional(),
2743
+ transferDeadlineMs: z2.number().int().positive().optional(),
2744
+ arrivalDeadlineMs: z2.number().int().positive().optional()
2745
+ });
2746
+ var agentApiMigrationReadyBodySchema = passthroughObject({
2747
+ manifestPath: z2.string().trim().min(1),
2748
+ manifestSha256: optionalStringSchema
2749
+ });
2750
+ var agentApiMigrationArrivedBodySchema = passthroughObject({
2751
+ reportPath: optionalStringSchema,
2752
+ reportSha256: optionalStringSchema
2753
+ });
2754
+ var agentApiMigrationResponseSchema = passthroughObject({
2755
+ migration: agentApiMigrationSummarySchema
2756
+ });
2757
+ var agentApiMigrationStatusResponseSchema = passthroughObject({
2758
+ migration: agentApiMigrationSummarySchema.nullable()
2759
+ });
2707
2760
  function route(input) {
2708
2761
  return {
2709
2762
  ...input,
@@ -2951,6 +3004,46 @@ var agentApiContract = {
2951
3004
  request: { body: agentApiTaskUpdateStatusBodySchema },
2952
3005
  response: { body: agentApiTaskUpdateStatusResponseSchema }
2953
3006
  }),
3007
+ migrationBegin: route({
3008
+ key: "migrationBegin",
3009
+ method: "POST",
3010
+ path: "/migrations",
3011
+ client: { resource: "migrations", method: "begin" },
3012
+ capability: "server",
3013
+ description: "Begin a migration for the bound agent credential.",
3014
+ request: { body: agentApiMigrationBeginBodySchema },
3015
+ response: { body: agentApiMigrationResponseSchema }
3016
+ }),
3017
+ migrationStatus: route({
3018
+ key: "migrationStatus",
3019
+ method: "GET",
3020
+ path: "/migrations/current",
3021
+ client: { resource: "migrations", method: "status" },
3022
+ capability: "read",
3023
+ description: "Read the active migration for the bound agent credential, if any.",
3024
+ request: {},
3025
+ response: { body: agentApiMigrationStatusResponseSchema }
3026
+ }),
3027
+ migrationReady: route({
3028
+ key: "migrationReady",
3029
+ method: "POST",
3030
+ path: "/migrations/ready",
3031
+ client: { resource: "migrations", method: "ready" },
3032
+ capability: "read",
3033
+ description: "Mark the bound agent's active migration prep phase ready.",
3034
+ request: { body: agentApiMigrationReadyBodySchema },
3035
+ response: { body: agentApiMigrationResponseSchema }
3036
+ }),
3037
+ migrationArrived: route({
3038
+ key: "migrationArrived",
3039
+ method: "POST",
3040
+ path: "/migrations/arrived",
3041
+ client: { resource: "migrations", method: "arrived" },
3042
+ capability: "read",
3043
+ description: "Mark the bound agent's active migration arrival phase complete.",
3044
+ request: { body: agentApiMigrationArrivedBodySchema },
3045
+ response: { body: agentApiMigrationResponseSchema }
3046
+ }),
2954
3047
  reminderList: route({
2955
3048
  key: "reminderList",
2956
3049
  method: "GET",
@@ -3258,6 +3351,14 @@ var AUTH_REFRESH_ROTATION_RETRY_RTT_BUDGET_MS = 1e3;
3258
3351
  var AUTH_REFRESH_ROTATION_LOSER_WORST_CHAIN_MS = AUTH_REFRESH_ROTATION_DISCOVERY_LAG_BUDGET_MS + AUTH_REFRESH_ROTATED_TOKEN_WAIT_MS + AUTH_REFRESH_ROTATION_RETRY_RTT_BUDGET_MS;
3259
3352
  var AUTH_REFRESH_ROTATION_REPLAY_GRACE_MARGIN_MS = AUTH_REFRESH_ROTATED_REPLAY_GRACE_MS - AUTH_REFRESH_ROTATION_LOSER_WORST_CHAIN_MS;
3260
3353
 
3354
+ // ../shared/src/clock.ts
3355
+ function currentTimeMs() {
3356
+ return Date.now();
3357
+ }
3358
+ function currentDate() {
3359
+ return new Date(currentTimeMs());
3360
+ }
3361
+
3261
3362
  // ../shared/src/agentInbox.ts
3262
3363
  function formatAgentInboxDelta(rows, options = {}) {
3263
3364
  const totalPendingMessages = options.totalPendingMessages;
@@ -3640,7 +3741,7 @@ var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
3640
3741
  var RUNTIMES = [
3641
3742
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
3642
3743
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
3643
- { id: "builtin", displayName: "Builtin Pi", binary: "", supported: true },
3744
+ { id: "builtin", displayName: "Built-in Pi", binary: "", supported: true },
3644
3745
  { id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
3645
3746
  // Kimi: prefer the in-process SDK (`kimi-sdk` → "Kimi Code") for new agents.
3646
3747
  // The legacy `kimi` (kimi-cli child-process) entry stays for backward compat
@@ -3672,6 +3773,27 @@ var RUNTIME_MODELS = {
3672
3773
  { id: "claude-haiku-4-5", label: "Claude Haiku 4.5" }
3673
3774
  ],
3674
3775
  codex: [
3776
+ {
3777
+ id: "gpt-5.6-sol",
3778
+ label: "GPT-5.6 Sol",
3779
+ supportedReasoningEfforts: ["low", "medium", "high", "xhigh", "max", "ultra"],
3780
+ defaultReasoningEffort: "medium",
3781
+ verified: "launchable"
3782
+ },
3783
+ {
3784
+ id: "gpt-5.6-terra",
3785
+ label: "GPT-5.6 Terra",
3786
+ supportedReasoningEfforts: ["low", "medium", "high", "xhigh", "max", "ultra"],
3787
+ defaultReasoningEffort: "medium",
3788
+ verified: "launchable"
3789
+ },
3790
+ {
3791
+ id: "gpt-5.6-luna",
3792
+ label: "GPT-5.6 Luna",
3793
+ supportedReasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
3794
+ defaultReasoningEffort: "medium",
3795
+ verified: "launchable"
3796
+ },
3675
3797
  { id: "gpt-5.5", label: "GPT-5.5" },
3676
3798
  { id: "gpt-5.4", label: "GPT-5.4" },
3677
3799
  { id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
@@ -4338,8 +4460,8 @@ async function executeResponseRequest(url, init, {
4338
4460
  }
4339
4461
 
4340
4462
  // src/directUploadCapability.ts
4341
- function joinUrl(base, path21) {
4342
- return `${base.replace(/\/+$/, "")}${path21}`;
4463
+ function joinUrl(base, path24) {
4464
+ return `${base.replace(/\/+$/, "")}${path24}`;
4343
4465
  }
4344
4466
  function jsonHeaders(apiKey) {
4345
4467
  return {
@@ -5029,6 +5151,15 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
5029
5151
  return candidates.filter((candidate) => existsSync(candidate.path));
5030
5152
  }
5031
5153
 
5154
+ // src/authEnv.ts
5155
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
5156
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
5157
+ function scrubDaemonChildEnv(env) {
5158
+ delete env[DAEMON_API_KEY_ENV];
5159
+ delete env[SLOCK_AGENT_TOKEN_ENV];
5160
+ return env;
5161
+ }
5162
+
5032
5163
  // src/agentCredentialProxy.ts
5033
5164
  import { randomBytes } from "crypto";
5034
5165
  import http from "http";
@@ -6610,7 +6741,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
6610
6741
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
6611
6742
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
6612
6743
  var RAW_CREDENTIAL_ENV_DENYLIST = [
6613
- "SLOCK_AGENT_CREDENTIAL_KEY"
6744
+ "SLOCK_AGENT_TOKEN",
6745
+ "SLOCK_AGENT_CREDENTIAL_KEY",
6746
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
6614
6747
  ];
6615
6748
  var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
6616
6749
  "agent-token",
@@ -6965,7 +7098,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6965
7098
  SLOCK_SERVER_URL: ctx.config.serverUrl,
6966
7099
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
6967
7100
  };
6968
- delete spawnEnv.SLOCK_AGENT_TOKEN;
7101
+ scrubDaemonChildEnv(spawnEnv);
6969
7102
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
6970
7103
  delete spawnEnv[key];
6971
7104
  }
@@ -7533,7 +7666,7 @@ function requiresWindowsShell(command, platform = process.platform) {
7533
7666
  }
7534
7667
  function resolveCommandOnPath(command, deps = {}) {
7535
7668
  const platform = deps.platform ?? process.platform;
7536
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7669
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7537
7670
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7538
7671
  const existsSyncFn = deps.existsSyncFn ?? existsSync3;
7539
7672
  if (platform === "win32") {
@@ -7559,7 +7692,7 @@ function firstExistingPath(candidates, deps = {}) {
7559
7692
  return null;
7560
7693
  }
7561
7694
  function readCommandVersion(command, args = [], deps = {}) {
7562
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7695
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7563
7696
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7564
7697
  try {
7565
7698
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -7985,7 +8118,7 @@ function codexNotificationDiagnosticEvent(message) {
7985
8118
  return null;
7986
8119
  }
7987
8120
  const sessionId = codexMessageThreadId(message);
7988
- const path21 = boundedString(params.path);
8121
+ const path24 = boundedString(params.path);
7989
8122
  return {
7990
8123
  kind: "runtime_diagnostic",
7991
8124
  severity: "warning",
@@ -7993,7 +8126,7 @@ function codexNotificationDiagnosticEvent(message) {
7993
8126
  itemType: message.method,
7994
8127
  message: diagnosticMessage,
7995
8128
  ...details ? { details } : {},
7996
- ...path21 ? { path: path21 } : {},
8129
+ ...path24 ? { path: path24 } : {},
7997
8130
  ...params.range !== void 0 ? { range: params.range } : {},
7998
8131
  payloadBytes: payloadBytes(params),
7999
8132
  ...sessionId ? { sessionId } : {}
@@ -9643,11 +9776,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
9643
9776
  return parseCursorModelsOutput(String(result.stdout || ""));
9644
9777
  }
9645
9778
  function buildCursorModelProbeEnv(deps = {}) {
9646
- return withWindowsUserEnvironment({
9779
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
9647
9780
  ...deps.env ?? process.env,
9648
9781
  FORCE_COLOR: "0",
9649
9782
  NO_COLOR: "1"
9650
- }, deps);
9783
+ }, deps));
9651
9784
  }
9652
9785
  function runCursorModelsCommand() {
9653
9786
  return spawnSync("cursor-agent", ["models"], {
@@ -9703,7 +9836,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
9703
9836
  }
9704
9837
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
9705
9838
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
9706
- const env = deps.env ?? process.env;
9839
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
9707
9840
  const winPath = path8.win32;
9708
9841
  let geminiEntry = null;
9709
9842
  try {
@@ -9840,12 +9973,15 @@ var GeminiDriver = class {
9840
9973
  // src/drivers/kimi.ts
9841
9974
  import { randomUUID as randomUUID2 } from "crypto";
9842
9975
  import { spawn as spawn7 } from "child_process";
9843
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
9976
+ import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
9844
9977
  import os4 from "os";
9845
9978
  import path9 from "path";
9846
9979
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
9847
9980
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
9848
9981
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
9982
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
9983
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
9984
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
9849
9985
  function parseToolArguments(raw) {
9850
9986
  if (typeof raw !== "string") return raw;
9851
9987
  try {
@@ -9854,6 +9990,73 @@ function parseToolArguments(raw) {
9854
9990
  return raw;
9855
9991
  }
9856
9992
  }
9993
+ function readKimiConfigSource(home = os4.homedir(), env = process.env) {
9994
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
9995
+ if (inlineConfig && inlineConfig.trim()) {
9996
+ return {
9997
+ raw: inlineConfig,
9998
+ explicitPath: null,
9999
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
10000
+ };
10001
+ }
10002
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
10003
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
10004
+ try {
10005
+ return {
10006
+ raw: readFileSync3(configPath, "utf8"),
10007
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
10008
+ sourcePath: configPath
10009
+ };
10010
+ } catch {
10011
+ return {
10012
+ raw: null,
10013
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
10014
+ sourcePath: configPath
10015
+ };
10016
+ }
10017
+ }
10018
+ function buildKimiSpawnEnv(env = process.env) {
10019
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
10020
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
10021
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
10022
+ return scrubDaemonChildEnv(spawnEnv);
10023
+ }
10024
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
10025
+ return {
10026
+ ...process.env,
10027
+ ...ctx.config.envVars || {},
10028
+ ...overrideEnv || {}
10029
+ };
10030
+ }
10031
+ function buildKimiLaunchOptions(ctx, opts = {}) {
10032
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
10033
+ const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
10034
+ const args = [];
10035
+ let configFilePath = null;
10036
+ let configContent = null;
10037
+ if (source.explicitPath) {
10038
+ configFilePath = source.explicitPath;
10039
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
10040
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
10041
+ configContent = source.raw;
10042
+ if (opts.writeGeneratedConfig !== false) {
10043
+ writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
10044
+ chmodSync(configFilePath, 384);
10045
+ }
10046
+ }
10047
+ if (configFilePath) {
10048
+ args.push("--config-file", configFilePath);
10049
+ }
10050
+ if (ctx.config.model && ctx.config.model !== "default") {
10051
+ args.push("--model", ctx.config.model);
10052
+ }
10053
+ return {
10054
+ args,
10055
+ env: buildKimiSpawnEnv(env),
10056
+ configFilePath,
10057
+ configContent
10058
+ };
10059
+ }
9857
10060
  function resolveKimiSpawn(commandArgs, deps = {}) {
9858
10061
  return {
9859
10062
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -9877,7 +10080,25 @@ var KimiDriver = class {
9877
10080
  };
9878
10081
  model = {
9879
10082
  detectedModelsVerifiedAs: "launchable",
9880
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
10083
+ toLaunchSpec: (modelId, ctx, opts) => {
10084
+ if (!ctx) return { args: ["--model", modelId] };
10085
+ const launchCtx = {
10086
+ ...ctx,
10087
+ config: {
10088
+ ...ctx.config,
10089
+ model: modelId
10090
+ }
10091
+ };
10092
+ const launch = buildKimiLaunchOptions(launchCtx, {
10093
+ home: opts?.home,
10094
+ writeGeneratedConfig: false
10095
+ });
10096
+ return {
10097
+ args: launch.args,
10098
+ env: launch.env,
10099
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
10100
+ };
10101
+ }
9881
10102
  };
9882
10103
  supportsStdinNotification = true;
9883
10104
  busyDeliveryMode = "direct";
@@ -9901,21 +10122,23 @@ var KimiDriver = class {
9901
10122
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
9902
10123
  ""
9903
10124
  ].join("\n"), "utf8");
10125
+ const launch = buildKimiLaunchOptions(ctx);
9904
10126
  const args = [
9905
10127
  "--wire",
9906
10128
  "--yolo",
9907
10129
  "--agent-file",
9908
10130
  agentFilePath,
9909
10131
  "--session",
9910
- this.sessionId
10132
+ this.sessionId,
10133
+ ...launch.args
9911
10134
  ];
9912
10135
  const launchRuntimeFields = runtimeConfigToLaunchFields(hydrateRuntimeConfig(ctx.config));
9913
10136
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
9914
10137
  args.push("--model", launchRuntimeFields.model);
9915
10138
  }
9916
10139
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
9917
- const launch = resolveKimiSpawn(args);
9918
- const proc = spawn7(launch.command, launch.args, {
10140
+ const spawnTarget = resolveKimiSpawn(args);
10141
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
9919
10142
  cwd: ctx.workingDirectory,
9920
10143
  stdio: ["pipe", "pipe", "pipe"],
9921
10144
  env: spawnEnv,
@@ -9923,7 +10146,7 @@ var KimiDriver = class {
9923
10146
  // and has an 8191-character command-line limit. Kimi's official
9924
10147
  // installer/uv entrypoint is an executable, so launch it directly and
9925
10148
  // keep prompts on stdin / files instead of routing through cmd.exe.
9926
- shell: launch.shell
10149
+ shell: spawnTarget.shell
9927
10150
  });
9928
10151
  proc.stdin?.write(JSON.stringify({
9929
10152
  jsonrpc: "2.0",
@@ -10036,14 +10259,9 @@ var KimiDriver = class {
10036
10259
  return detectKimiModels();
10037
10260
  }
10038
10261
  };
10039
- function detectKimiModels(home = os4.homedir()) {
10040
- const configPath = path9.join(home, ".kimi", "config.toml");
10041
- let raw;
10042
- try {
10043
- raw = readFileSync3(configPath, "utf8");
10044
- } catch {
10045
- return null;
10046
- }
10262
+ function detectKimiModels(home = os4.homedir(), opts = {}) {
10263
+ const raw = readKimiConfigSource(home, opts.env).raw;
10264
+ if (raw === null) return null;
10047
10265
  const models = [];
10048
10266
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
10049
10267
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -10303,6 +10521,12 @@ var KimiSdkRuntimeSession = class {
10303
10521
  get closed() {
10304
10522
  return this.didClose;
10305
10523
  }
10524
+ // In-process SDK session: there is no OS pid to probe with signal-0, so
10525
+ // liveness is unknowable via the pid-probe boundary. Return undefined, which
10526
+ // callers treat the same as the historic "no pid" probe result (RS-011).
10527
+ isAlive() {
10528
+ return void 0;
10529
+ }
10306
10530
  on(event, cb) {
10307
10531
  this.events.on(event, cb);
10308
10532
  }
@@ -10722,7 +10946,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
10722
10946
  const platform = deps.platform ?? process.platform;
10723
10947
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
10724
10948
  const result = spawnSyncFn("opencode", ["models"], {
10725
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
10949
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
10726
10950
  encoding: "utf8",
10727
10951
  timeout: 5e3,
10728
10952
  shell: platform === "win32"
@@ -11018,7 +11242,9 @@ function createPiSdkEventMappingState(sessionId = null) {
11018
11242
  return {
11019
11243
  sessionId,
11020
11244
  sessionAnnounced: false,
11021
- sawTextDelta: false
11245
+ sawTextDelta: false,
11246
+ thinkingBuffers: /* @__PURE__ */ new Map(),
11247
+ announcedThinkingIndexes: /* @__PURE__ */ new Set()
11022
11248
  };
11023
11249
  }
11024
11250
  function buildPiSessionDir(workingDirectory) {
@@ -11233,10 +11459,40 @@ function pushSessionInitIfNeeded2(state, events) {
11233
11459
  state.sessionAnnounced = true;
11234
11460
  }
11235
11461
  }
11462
+ function piAssistantContentIndex(assistantEvent) {
11463
+ return "contentIndex" in assistantEvent && typeof assistantEvent.contentIndex === "number" ? assistantEvent.contentIndex : 0;
11464
+ }
11465
+ function announcePiThinkingIfNeeded(index, state) {
11466
+ if (state.announcedThinkingIndexes.has(index)) return [];
11467
+ state.announcedThinkingIndexes.add(index);
11468
+ return [{ kind: "thinking", text: "" }];
11469
+ }
11470
+ function resetPiThinking(index, state) {
11471
+ state.thinkingBuffers.delete(index);
11472
+ state.announcedThinkingIndexes.delete(index);
11473
+ }
11236
11474
  function mapPiAssistantMessageEvent(assistantEvent, state) {
11237
11475
  switch (assistantEvent.type) {
11238
- case "thinking_delta":
11239
- return typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0 ? [{ kind: "thinking", text: assistantEvent.delta }] : [];
11476
+ case "thinking_start": {
11477
+ const index = piAssistantContentIndex(assistantEvent);
11478
+ resetPiThinking(index, state);
11479
+ return announcePiThinkingIfNeeded(index, state);
11480
+ }
11481
+ case "thinking_delta": {
11482
+ const index = piAssistantContentIndex(assistantEvent);
11483
+ const events = announcePiThinkingIfNeeded(index, state);
11484
+ if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
11485
+ state.thinkingBuffers.set(index, `${state.thinkingBuffers.get(index) ?? ""}${assistantEvent.delta}`);
11486
+ }
11487
+ return events;
11488
+ }
11489
+ case "thinking_end": {
11490
+ const index = piAssistantContentIndex(assistantEvent);
11491
+ const buffered = state.thinkingBuffers.get(index) ?? "";
11492
+ const text = typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? assistantEvent.content : buffered;
11493
+ resetPiThinking(index, state);
11494
+ return text ? [{ kind: "thinking", text }] : [];
11495
+ }
11240
11496
  case "text_delta":
11241
11497
  if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
11242
11498
  state.sawTextDelta = true;
@@ -11247,8 +11503,6 @@ function mapPiAssistantMessageEvent(assistantEvent, state) {
11247
11503
  return !state.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? [{ kind: "text", text: assistantEvent.content }] : [];
11248
11504
  case "error":
11249
11505
  return [{ kind: "error", message: piErrorMessage(assistantEvent.error.errorMessage || assistantEvent.error) }];
11250
- case "thinking_start":
11251
- case "thinking_end":
11252
11506
  case "text_start":
11253
11507
  case "toolcall_start":
11254
11508
  case "toolcall_delta":
@@ -11292,6 +11546,8 @@ function mapPiSdkEventToParsedEvents(event, state) {
11292
11546
  case "message_start":
11293
11547
  if (event.message.role === "assistant") {
11294
11548
  state.sawTextDelta = false;
11549
+ state.thinkingBuffers.clear();
11550
+ state.announcedThinkingIndexes.clear();
11295
11551
  }
11296
11552
  return events;
11297
11553
  case "message_update":
@@ -11316,10 +11572,8 @@ function mapPiSdkEventToParsedEvents(event, state) {
11316
11572
  case "agent_end":
11317
11573
  events.push({ kind: "turn_end", sessionId: state.sessionId || void 0 });
11318
11574
  return events;
11319
- default: {
11320
- const _exhaustive = event;
11321
- return _exhaustive;
11322
- }
11575
+ default:
11576
+ return events;
11323
11577
  }
11324
11578
  }
11325
11579
  var PI_RUNTIME_SESSION_DESCRIPTOR = {
@@ -11504,15 +11758,21 @@ async function createPiAgentSessionForContext(ctx, sessionId, opts = {}) {
11504
11758
  }
11505
11759
  }
11506
11760
  var PiSdkRuntimeSession = class {
11507
- constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext) {
11761
+ constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext, opts = {}) {
11508
11762
  this.ctx = ctx;
11509
11763
  this.setCurrentSessionId = setCurrentSessionId;
11510
11764
  this.sessionFactory = sessionFactory;
11511
11765
  this.mappingState = createPiSdkEventMappingState(ctx.config.sessionId || null);
11766
+ const runtimeConfig = hydrateRuntimeConfig(ctx.config);
11767
+ const launchRuntimeFields = runtimeConfigToLaunchFields(runtimeConfig);
11768
+ this.sdkCallEnvPatch = buildPiSessionCreateEnvPatch(runtimeConfig, launchRuntimeFields.envVars);
11769
+ this.sdkCallEnvRemoveFirst = opts.isolateHostProviderEnv ? BUILTIN_BLOCKED_HOST_PROVIDER_ENV_KEYS : void 0;
11512
11770
  }
11513
11771
  descriptor = PI_RUNTIME_SESSION_DESCRIPTOR;
11514
11772
  events = new EventEmitter2();
11515
11773
  mappingState;
11774
+ sdkCallEnvPatch;
11775
+ sdkCallEnvRemoveFirst;
11516
11776
  session = null;
11517
11777
  unsubscribe = null;
11518
11778
  started = false;
@@ -11534,6 +11794,12 @@ var PiSdkRuntimeSession = class {
11534
11794
  get closed() {
11535
11795
  return this.didClose;
11536
11796
  }
11797
+ // In-process SDK session: there is no OS pid to probe with signal-0, so
11798
+ // liveness is unknowable via the pid-probe boundary. Return undefined, which
11799
+ // callers treat the same as the historic "no pid" probe result (RS-011).
11800
+ isAlive() {
11801
+ return void 0;
11802
+ }
11537
11803
  on(event, cb) {
11538
11804
  this.events.on(event, cb);
11539
11805
  }
@@ -11642,7 +11908,7 @@ var PiSdkRuntimeSession = class {
11642
11908
  setImmediate(() => {
11643
11909
  if (this.didClose) return;
11644
11910
  try {
11645
- void invoke().catch((error) => {
11911
+ void withProcessEnvPatch(this.sdkCallEnvPatch, invoke, { removeFirst: this.sdkCallEnvRemoveFirst }).catch((error) => {
11646
11912
  if (this.didClose) return;
11647
11913
  this.events.emit("runtime_event", {
11648
11914
  kind: "error",
@@ -11775,7 +12041,7 @@ var BuiltInDriver = class extends PiDriver {
11775
12041
  exposeLaunchTraceEvidence: true,
11776
12042
  exposeLaunchEnvToTools: false,
11777
12043
  isolateHostProviderEnv: true
11778
- }));
12044
+ }), { isolateHostProviderEnv: true });
11779
12045
  }
11780
12046
  buildSystemPrompt(config, _agentId) {
11781
12047
  return buildCliTransportSystemPrompt(config, {
@@ -11852,6 +12118,25 @@ var ChildProcessRuntimeSession = class {
11852
12118
  get closed() {
11853
12119
  return this.process ? this.process.exitCode != null || this.process.signalCode != null : false;
11854
12120
  }
12121
+ /**
12122
+ * Read-only OS liveness introspection (signal-0), NOT control IO — exposed so
12123
+ * managers query liveness through the session boundary (RS-011) instead of
12124
+ * poking the raw pid. Preserves the historic `probeRuntimeProcessLiveness`
12125
+ * semantics exactly: undefined when there is no pid, true on success or EPERM,
12126
+ * false otherwise. Deliberately does NOT short-circuit on `this.closed` — the
12127
+ * original probe checked only pid + signal-0.
12128
+ */
12129
+ isAlive() {
12130
+ const pid = this.process?.pid;
12131
+ if (typeof pid !== "number") return void 0;
12132
+ try {
12133
+ process.kill(pid, 0);
12134
+ return true;
12135
+ } catch (err) {
12136
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
12137
+ return code === "EPERM" ? true : false;
12138
+ }
12139
+ }
11855
12140
  on(event, cb) {
11856
12141
  this.events.on(event, cb);
11857
12142
  }
@@ -11965,6 +12250,61 @@ function getDriver(runtimeId) {
11965
12250
  return driver;
11966
12251
  }
11967
12252
 
12253
+ // src/daemonOrphanReaper.ts
12254
+ async function reapOrphanProcesses(pids, logger2, recordTrace) {
12255
+ const survivors = pids.filter((pid) => {
12256
+ try {
12257
+ process.kill(pid, 0);
12258
+ return true;
12259
+ } catch {
12260
+ return false;
12261
+ }
12262
+ });
12263
+ if (survivors.length === 0) return false;
12264
+ for (const pid of survivors) {
12265
+ logger2.warn(`[Daemon] Agent subprocess ${pid} survived stopAll; sending SIGKILL`);
12266
+ try {
12267
+ process.kill(pid, "SIGKILL");
12268
+ } catch {
12269
+ }
12270
+ }
12271
+ recordTrace("daemon.agent.stop_all.survivor_reaped", {
12272
+ survivor_count: survivors.length,
12273
+ survivor_pids: survivors.join(","),
12274
+ reason: "shutdown_survivor",
12275
+ signal: "SIGKILL"
12276
+ });
12277
+ const deadline = Date.now() + 2e3;
12278
+ while (Date.now() < deadline) {
12279
+ const alive = survivors.filter((pid) => {
12280
+ try {
12281
+ process.kill(pid, 0);
12282
+ return true;
12283
+ } catch {
12284
+ return false;
12285
+ }
12286
+ });
12287
+ if (alive.length === 0) break;
12288
+ await new Promise((r) => setTimeout(r, 50));
12289
+ }
12290
+ const stillAlive = survivors.filter((pid) => {
12291
+ try {
12292
+ process.kill(pid, 0);
12293
+ return true;
12294
+ } catch {
12295
+ return false;
12296
+ }
12297
+ });
12298
+ const outcome = stillAlive.length > 0 ? "survivors_still_alive" : "survivors_killed";
12299
+ recordTrace("daemon.agent.stop_all.completed", {
12300
+ survivor_count: survivors.length,
12301
+ reaped_count: survivors.length - stillAlive.length,
12302
+ still_alive_count: stillAlive.length,
12303
+ outcome
12304
+ }, stillAlive.length > 0 ? "error" : "ok");
12305
+ return true;
12306
+ }
12307
+
11968
12308
  // src/workspaces.ts
11969
12309
  import { readdir, rm, stat } from "fs/promises";
11970
12310
  import path13 from "path";
@@ -13391,6 +13731,88 @@ var RuntimeNotificationState = class {
13391
13731
  }
13392
13732
  };
13393
13733
 
13734
+ // src/proxyFailureTrace.ts
13735
+ function failureReason(input) {
13736
+ if (input.lifecycleInvalidContext) return "local_daemon_state_invalid";
13737
+ const text = `${input.errorCause ?? ""} ${input.errorMessage}`.toLowerCase();
13738
+ if (/enotfound|eai_again|\bdns\b/.test(text)) return "dns";
13739
+ if (/econnrefused|econnreset|epipe|und_err_socket|\bsocket\b|other side closed|terminated/.test(text)) return "tcp";
13740
+ if (/certificate|\btls\b/.test(text)) return "tls";
13741
+ if (/und_err_headers_timeout|und_err_body_timeout|\btimeout\b/.test(text)) return "read_timeout";
13742
+ if (/\bproxy\b/.test(text)) return "proxy_connect";
13743
+ if (/\bfly\b/.test(text)) return "fly_edge";
13744
+ return "unknown";
13745
+ }
13746
+ function boundedErrorClass(value) {
13747
+ if (value === "TypeError") return "TypeError";
13748
+ if (value === "AbortError") return "AbortError";
13749
+ if (value === "Error") return "Error";
13750
+ return "OtherError";
13751
+ }
13752
+ function boundedMethod(value) {
13753
+ const method = value.toUpperCase();
13754
+ return ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"].includes(method) ? method : "OTHER";
13755
+ }
13756
+ function cannedExcerpt(reason) {
13757
+ switch (reason) {
13758
+ case "local_daemon_state_invalid":
13759
+ return "Local daemon lifecycle state rejected the proxy request";
13760
+ case "dns":
13761
+ return "Upstream DNS resolution failed";
13762
+ case "tcp":
13763
+ return "Upstream TCP connection failed";
13764
+ case "tls":
13765
+ return "Upstream TLS negotiation failed";
13766
+ case "read_timeout":
13767
+ return "Upstream response timed out";
13768
+ case "proxy_connect":
13769
+ return "Upstream proxy connection failed";
13770
+ case "fly_edge":
13771
+ return "Upstream edge transport failed";
13772
+ case "unknown":
13773
+ return "Proxy request failed with an unclassified cause";
13774
+ }
13775
+ }
13776
+ function daemonProxyFailureTraceAttrs(input) {
13777
+ const reason = failureReason(input);
13778
+ return {
13779
+ route_family: routeFamilyForPath(input.pathname),
13780
+ method: boundedMethod(input.method),
13781
+ outcome: "error",
13782
+ reason,
13783
+ error_class: boundedErrorClass(input.errorName),
13784
+ error_excerpt: cannedExcerpt(reason),
13785
+ ...typeof input.responseStatusCode === "number" ? { response_status_code: input.responseStatusCode } : {},
13786
+ response_code_present: Boolean(input.responseCode)
13787
+ };
13788
+ }
13789
+ function daemonTransportErrorExcerpt(input) {
13790
+ if (input.normalizedCode === "local_daemon_state_invalid") {
13791
+ return "Local daemon lifecycle state rejected the proxy request";
13792
+ }
13793
+ if (input.normalizedCode === "server_5xx") return "Upstream server returned a 5xx response";
13794
+ switch (input.upstreamLayer) {
13795
+ case "dns":
13796
+ return "Upstream DNS resolution failed";
13797
+ case "tcp":
13798
+ return "Upstream TCP connection failed";
13799
+ case "tls":
13800
+ return "Upstream TLS negotiation failed";
13801
+ case "read_timeout":
13802
+ return "Upstream response timed out";
13803
+ case "proxy_connect":
13804
+ return "Upstream proxy connection failed";
13805
+ case "fly_edge":
13806
+ return "Upstream edge transport failed";
13807
+ case "body_decode_failure":
13808
+ return "Upstream response body could not be decoded";
13809
+ case "http_status":
13810
+ return "Upstream HTTP request failed";
13811
+ case "unknown":
13812
+ return "Proxy transport failed with an unclassified cause";
13813
+ }
13814
+ }
13815
+
13394
13816
  // src/agentProcessManager.ts
13395
13817
  var DEFAULT_MAX_CONCURRENT_AGENT_STARTS = 5;
13396
13818
  var DEFAULT_AGENT_START_INTERVAL_MS = 500;
@@ -14768,8 +15190,9 @@ function classifyTerminalFailure(ap) {
14768
15190
  if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || lower.includes("model is not supported") || lower.includes("unsupported model") || /\bmodel\b.*\bnot supported\b/i.test(text) || diagnostics.spanAttrs.runtime_error_action_required === true || isProviderStreamFailureText(text) || isRuntimeStartTimeoutText(text)) {
14769
15191
  const actionRequired = diagnostics.spanAttrs.runtime_error_action_required === true;
14770
15192
  return {
14771
- detail: actionRequired ? formatRuntimeLoginRequiredMessage(ap.driver.id) : text,
14772
- actionRequired
15193
+ detail: actionRequired ? formatRuntimeActionRequiredMessage(ap) : text,
15194
+ actionRequired,
15195
+ ...actionRequired ? { entries: buildRuntimeActionRequiredEntries(ap, text, diagnostics) } : {}
14773
15196
  };
14774
15197
  }
14775
15198
  }
@@ -14785,6 +15208,52 @@ function classifyStickyTerminalFailure(ap) {
14785
15208
  if (isRuntimeStartTimeoutText(terminalFailure.detail)) return null;
14786
15209
  return null;
14787
15210
  }
15211
+ function formatRuntimeActionRequiredMessage(ap) {
15212
+ if (ap.driver.id === "claude" && isClaudeCustomProviderConfig(ap.config)) {
15213
+ return "Claude Code custom provider authentication failed. Check this agent's custom Claude provider API key/API URL, then retry starting this agent.";
15214
+ }
15215
+ return formatRuntimeLoginRequiredMessage(ap.driver.id);
15216
+ }
15217
+ function buildRuntimeActionRequiredEntries(ap, text, diagnostics) {
15218
+ const excerpt = String(diagnostics.eventAttrs.runtime_error_message_excerpt ?? "").trim();
15219
+ const subtype = classifyRuntimeActionRequiredSubtype(ap, text);
15220
+ const detail = formatRuntimeActionRequiredMessage(ap);
15221
+ const entries = [
15222
+ { kind: "text", text: `Error: ${detail}` },
15223
+ { kind: "text", text: `Runtime auth diagnostic: ${subtype}` }
15224
+ ];
15225
+ if (excerpt) {
15226
+ entries.push({ kind: "text", text: `Raw error excerpt (redacted): ${excerpt}` });
15227
+ }
15228
+ if (ap.driver.id === "claude") {
15229
+ entries.push({
15230
+ kind: "text",
15231
+ text: isClaudeCustomProviderConfig(ap.config) ? "Claude auth mode: custom provider. Local Claude Code login does not fix this branch; check this agent's provider API key/API URL." : "Claude auth mode: default host login. If Terminal works, set this agent's Claude command to the absolute path from `which claude` and restart Raft Computer."
15232
+ });
15233
+ }
15234
+ return entries;
15235
+ }
15236
+ function classifyRuntimeActionRequiredSubtype(ap, text) {
15237
+ const lower = text.toLowerCase();
15238
+ const customClaudeProvider = ap.driver.id === "claude" && isClaudeCustomProviderConfig(ap.config);
15239
+ if (customClaudeProvider) {
15240
+ if (/\bmissing\b.*\b(?:api\s*)?key\b|\b(?:api\s*)?key\b.*\bnot set\b|\bmissing\b.*\btoken\b|\bno\b.*\btoken\b/i.test(text)) {
15241
+ return "custom_provider_missing_key";
15242
+ }
15243
+ if (/\binvalid\b.*\b(?:api\s*)?key\b|\bunauthorized\b|\b401\b|\b403\b|auth(?:entication)? failed/i.test(text)) {
15244
+ return "custom_provider_invalid_key";
15245
+ }
15246
+ return "custom_provider_auth_error";
15247
+ }
15248
+ if (ap.driver.id === "claude") {
15249
+ if (lower.includes("not logged in") || lower.includes("not signed in") || lower.includes("please log in") || lower.includes("login required")) {
15250
+ return "host_claude_login_required";
15251
+ }
15252
+ return "runtime_auth_error";
15253
+ }
15254
+ if (ap.driver.id === "builtin") return "builtin_provider_auth_error";
15255
+ return "runtime_auth_error";
15256
+ }
14788
15257
  function isAuthClassTerminalLine(text) {
14789
15258
  return buildRuntimeErrorDiagnosticEnvelope(text).spanAttrs.runtime_error_action_required === true;
14790
15259
  }
@@ -15809,17 +16278,9 @@ var AgentProcessManager = class _AgentProcessManager {
15809
16278
  };
15810
16279
  }
15811
16280
  recordAgentProxyFailure(agentId, input) {
15812
- this.recordDaemonTrace("daemon.agent.proxy.failure", {
16281
+ this.recordDaemonTrace("daemon.proxy.failed", {
15813
16282
  agentId,
15814
- method: input.method,
15815
- path: input.pathname,
15816
- query_keys: input.queryKeys,
15817
- error_name: input.errorName,
15818
- error_message: input.errorMessage,
15819
- response_status_code: input.responseStatusCode,
15820
- response_code: input.responseCode,
15821
- lifecycle_invalid_agent_id: input.lifecycleInvalidAgentId,
15822
- lifecycle_invalid_context: input.lifecycleInvalidContext
16283
+ ...daemonProxyFailureTraceAttrs(input)
15823
16284
  }, "error");
15824
16285
  }
15825
16286
  recordAgentProxyTransportNormalizedError(agentId, input) {
@@ -15831,7 +16292,7 @@ var AgentProcessManager = class _AgentProcessManager {
15831
16292
  response_started: input.responseStarted,
15832
16293
  upstream_layer: input.upstreamLayer,
15833
16294
  ...typeof input.upstreamStatus === "number" ? { upstream_status: input.upstreamStatus } : {},
15834
- ...input.originalMessage ? { original_message: input.originalMessage } : {},
16295
+ error_excerpt: daemonTransportErrorExcerpt(input),
15835
16296
  launchId: input.launchId,
15836
16297
  target_host_class: input.targetHostClass,
15837
16298
  downstream_caller: input.downstreamCaller,
@@ -16986,7 +17447,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16986
17447
  agentId,
16987
17448
  "error",
16988
17449
  terminalFailureDetail.detail,
16989
- [{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
17450
+ terminalFailureDetail.entries ?? [{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
16990
17451
  ap.launchId,
16991
17452
  "runtime_error"
16992
17453
  );
@@ -17017,9 +17478,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17017
17478
  agentProcess.decisionErrorWindow.recordRuntimeError(startResult.error);
17018
17479
  }
17019
17480
  if (startResult.error && diagnostics?.spanAttrs.runtime_error_action_required === true) {
17020
- const visibleErrorMessage = formatRuntimeLoginRequiredMessage(agentProcess.driver.id);
17481
+ const terminalFailure = classifyTerminalFailure(agentProcess);
17482
+ const visibleErrorMessage = terminalFailure?.detail ?? formatRuntimeActionRequiredMessage(agentProcess);
17021
17483
  this.broadcastActivity(agentId, "error", visibleErrorMessage, [
17022
- { kind: "text", text: `Error: ${visibleErrorMessage}` }
17484
+ ...terminalFailure?.entries ?? [{ kind: "text", text: `Error: ${visibleErrorMessage}` }]
17023
17485
  ], agentProcess.launchId, "runtime_error");
17024
17486
  }
17025
17487
  throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
@@ -17990,57 +18452,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17990
18452
  pids: pids.join(",")
17991
18453
  });
17992
18454
  await Promise.all(ids.map((id) => this.stopAgent(id, { wait: true, silent: true })));
17993
- const survivors = pids.filter((pid) => {
17994
- try {
17995
- process.kill(pid, 0);
17996
- return true;
17997
- } catch {
17998
- return false;
17999
- }
18000
- });
18001
- if (survivors.length > 0) {
18002
- for (const pid of survivors) {
18003
- logger.warn(`[Daemon] Agent subprocess ${pid} survived stopAll; sending SIGKILL`);
18004
- try {
18005
- process.kill(pid, "SIGKILL");
18006
- } catch {
18007
- }
18008
- }
18009
- this.recordDaemonTrace("daemon.agent.stop_all.survivor_reaped", {
18010
- survivor_count: survivors.length,
18011
- survivor_pids: survivors.join(","),
18012
- reason: "shutdown_survivor",
18013
- signal: "SIGKILL"
18014
- });
18015
- const deadline = Date.now() + 2e3;
18016
- while (Date.now() < deadline) {
18017
- const alive = survivors.filter((pid) => {
18018
- try {
18019
- process.kill(pid, 0);
18020
- return true;
18021
- } catch {
18022
- return false;
18023
- }
18024
- });
18025
- if (alive.length === 0) break;
18026
- await new Promise((r) => setTimeout(r, 50));
18027
- }
18028
- const stillAlive = survivors.filter((pid) => {
18029
- try {
18030
- process.kill(pid, 0);
18031
- return true;
18032
- } catch {
18033
- return false;
18034
- }
18035
- });
18036
- const outcome = stillAlive.length > 0 ? "survivors_still_alive" : "survivors_killed";
18037
- this.recordDaemonTrace("daemon.agent.stop_all.completed", {
18038
- survivor_count: survivors.length,
18039
- reaped_count: survivors.length - stillAlive.length,
18040
- still_alive_count: stillAlive.length,
18041
- outcome
18042
- }, stillAlive.length > 0 ? "error" : "ok");
18043
- } else {
18455
+ const reapedSurvivors = await reapOrphanProcesses(
18456
+ pids,
18457
+ logger,
18458
+ (name, attrs, status) => this.recordDaemonTrace(name, attrs, status)
18459
+ );
18460
+ if (!reapedSurvivors) {
18044
18461
  this.recordDaemonTrace("daemon.agent.stop_all.completed", {
18045
18462
  agent_count: ids.length,
18046
18463
  survivor_count: 0,
@@ -18782,7 +19199,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18782
19199
  const ap = this.agents.get(agentId);
18783
19200
  const entries = [...extraTrajectory];
18784
19201
  const hasToolStart = entries.some((e) => e.kind === "tool_start");
18785
- if (!hasToolStart) {
19202
+ const isThinkingProgressFrame = activityKind === "thinking" && detail === "" && detailKind === "none" && entries.every((e) => e.kind === "thinking" || e.kind === "text");
19203
+ if (!hasToolStart && !isThinkingProgressFrame) {
18786
19204
  entries.push({
18787
19205
  kind: "status",
18788
19206
  activity: activityKind,
@@ -19794,15 +20212,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19794
20212
  return true;
19795
20213
  }
19796
20214
  probeRuntimeProcessLiveness(ap) {
19797
- const pid = ap.runtime.pid;
19798
- if (typeof pid !== "number") return void 0;
19799
- try {
19800
- process.kill(pid, 0);
19801
- return true;
19802
- } catch (err) {
19803
- const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
19804
- return code === "EPERM" ? true : false;
19805
- }
20215
+ return ap.runtime.isAlive();
19806
20216
  }
19807
20217
  recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap) {
19808
20218
  const staleForMs = ap.runtimeProgress.ageMs();
@@ -20182,11 +20592,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20182
20592
  ap.decisionErrorWindow.recordRuntimeError(event.message);
20183
20593
  }
20184
20594
  let visibleErrorMessage = event.message;
20595
+ let visibleErrorEntries;
20185
20596
  if (ap) {
20186
20597
  const runtimeErrorDiagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
20187
20598
  const runtimeErrorFingerprint = typeof runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint === "string" ? runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint : null;
20188
20599
  if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
20189
- visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
20600
+ visibleErrorMessage = formatRuntimeActionRequiredMessage(ap);
20190
20601
  }
20191
20602
  const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
20192
20603
  const backoffFailPoint = this.runtimeErrorDeliveryBackoffFailPointForTesting?.({
@@ -20212,6 +20623,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20212
20623
  if (fingerprintFence) {
20213
20624
  visibleErrorMessage = fingerprintFence.detail;
20214
20625
  }
20626
+ visibleErrorEntries = terminalFailure?.entries;
20215
20627
  this.noteRuntimeErrorDeliveryBackoff(agentId, ap, event.message, terminalFailure, stickyTerminalFailure, backoffReasonOverride);
20216
20628
  if (reduction.shouldDisableToolBoundaryFlush) {
20217
20629
  this.recordGatedSteeringEvent(agentId, ap, "disabled", {
@@ -20258,9 +20670,14 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20258
20670
  }
20259
20671
  }
20260
20672
  }
20261
- this.broadcastActivity(agentId, "error", visibleErrorMessage, [
20262
- { kind: "text", text: `Error: ${visibleErrorMessage}` }
20263
- ], void 0, "runtime_error");
20673
+ this.broadcastActivity(
20674
+ agentId,
20675
+ "error",
20676
+ visibleErrorMessage,
20677
+ visibleErrorEntries ?? [{ kind: "text", text: `Error: ${visibleErrorMessage}` }],
20678
+ void 0,
20679
+ "runtime_error"
20680
+ );
20264
20681
  break;
20265
20682
  }
20266
20683
  }
@@ -20927,8 +21344,11 @@ var DaemonConnection = class {
20927
21344
  doConnect() {
20928
21345
  if (!this.shouldConnect) return;
20929
21346
  if (this.ws && this.ws.readyState !== WebSocket.CLOSED) return;
20930
- const wsUrl = this.options.serverUrl.replace(/^http/, "ws") + `/daemon/connect?key=${this.options.apiKey}`;
20931
- const wsOptions = buildWebSocketOptions(wsUrl, this.options.proxyEnv ?? process.env);
21347
+ const wsUrl = this.options.serverUrl.replace(/^http/, "ws") + "/daemon/connect";
21348
+ const wsOptions = {
21349
+ ...buildWebSocketOptions(wsUrl, this.options.proxyEnv ?? process.env),
21350
+ headers: { Authorization: `Bearer ${this.options.apiKey}` }
21351
+ };
20932
21352
  logger.info(`[Daemon] Connecting to ${this.options.serverUrl}...`);
20933
21353
  if (wsOptions?.agent) {
20934
21354
  logger.info("[Daemon] Using configured proxy for WebSocket connection");
@@ -21865,45 +22285,212 @@ function readPositiveIntegerEnv2(name, fallback) {
21865
22285
  }
21866
22286
 
21867
22287
  // src/computerMigrationGuard.ts
21868
- import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync8 } from "fs";
22288
+ import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "fs";
21869
22289
  import path18 from "path";
21870
- var LegacyDaemonKeyAdoptedByComputerError = class extends Error {
21871
- code = "LEGACY_DAEMON_KEY_ADOPTED_BY_COMPUTER";
21872
- attachmentPath;
21873
- serverId;
21874
- constructor(match) {
21875
- const serverDisplay = match.serverSlug ? `/${match.serverSlug}` : match.serverId;
21876
- const startCommand = match.serverSlug ? `raft-computer start /${match.serverSlug}` : "raft-computer start";
21877
- super(
21878
- `This legacy daemon key was already adopted by Raft Computer for ${serverDisplay}. Start the Computer supervisor instead: ${startCommand}. Legacy daemon startup is blocked to avoid relaunching a migrated machine key.`
21879
- );
21880
- this.name = "LegacyDaemonKeyAdoptedByComputerError";
21881
- this.attachmentPath = match.attachmentPath;
21882
- this.serverId = match.serverId;
22290
+
22291
+ // src/legacySupervisor.ts
22292
+ import { execFileSync as execFileSync4 } from "child_process";
22293
+ import { readFileSync as readFileSync8 } from "fs";
22294
+ function execFileText(file, args) {
22295
+ try {
22296
+ return execFileSync4(file, args, {
22297
+ encoding: "utf8",
22298
+ timeout: 1e3,
22299
+ maxBuffer: 256 * 1024,
22300
+ stdio: ["ignore", "pipe", "ignore"]
22301
+ });
22302
+ } catch {
22303
+ return void 0;
21883
22304
  }
21884
- };
21885
- function daemonApiKeyFingerprint(apiKey) {
21886
- return getDaemonMachineLockId(apiKey).slice("machine-".length);
21887
22305
  }
21888
- function findAdoptedComputerForLegacyFingerprint(slockHome, legacyApiKeyFingerprint) {
21889
- const serversDir = path18.join(slockHome, "computer", "servers");
21890
- if (!existsSync9(serversDir)) return null;
21891
- let serverDirs;
22306
+ function readFileText(file) {
21892
22307
  try {
21893
- serverDirs = readdirSync6(serversDir);
22308
+ return readFileSync8(file, "utf8");
21894
22309
  } catch {
21895
- return null;
22310
+ return void 0;
21896
22311
  }
21897
- for (const serverId of serverDirs) {
21898
- const attachmentPath = path18.join(serversDir, serverId, "runner.state.json");
21899
- let raw;
22312
+ }
22313
+ function defaultProbe(pid) {
22314
+ return {
22315
+ pid,
22316
+ platform: process.platform,
22317
+ env: process.env,
22318
+ execFile: execFileText,
22319
+ readFile: readFileText
22320
+ };
22321
+ }
22322
+ function shellQuote(value) {
22323
+ return `'${value.replace(/'/g, `'\\''`)}'`;
22324
+ }
22325
+ function parentPid(probe, pid) {
22326
+ const stdout = probe.execFile("ps", ["-o", "ppid=", "-p", String(pid)]);
22327
+ const parsed = Number.parseInt(stdout?.trim() ?? "", 10);
22328
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
22329
+ }
22330
+ function processCommand(probe, pid) {
22331
+ return probe.execFile("ps", ["-o", "command=", "-p", String(pid)])?.trim().toLowerCase() ?? "";
22332
+ }
22333
+ function relatedProcessIds(probe) {
22334
+ const ids = [probe.pid];
22335
+ let current = probe.pid;
22336
+ for (let depth = 0; depth < 12; depth += 1) {
22337
+ const parent = parentPid(probe, current);
22338
+ if (!parent || ids.includes(parent)) break;
22339
+ ids.push(parent);
22340
+ if (parent === 1) break;
22341
+ current = parent;
22342
+ }
22343
+ return ids;
22344
+ }
22345
+ function detectPm2(probe, relatedPids) {
22346
+ const hasPm2Ancestor = relatedPids.slice(1).some((pid) => processCommand(probe, pid).includes("pm2"));
22347
+ const hasPm2Environment = Boolean(probe.env.PM2_HOME || probe.env.pm_id);
22348
+ if (!hasPm2Ancestor && !hasPm2Environment) return void 0;
22349
+ const pm2Json = probe.execFile("pm2", ["jlist"]);
22350
+ if (pm2Json) {
21900
22351
  try {
21901
- raw = readFileSync8(attachmentPath, "utf8");
22352
+ const entries = JSON.parse(pm2Json);
22353
+ const entry = entries.find(
22354
+ (candidate) => typeof candidate.pid === "number" && relatedPids.includes(candidate.pid)
22355
+ );
22356
+ if (entry) {
22357
+ const name = typeof entry.name === "string" && entry.name.length > 0 ? entry.name : String(entry.pid);
22358
+ return {
22359
+ kind: "pm2",
22360
+ detail: `PM2 app ${JSON.stringify(name)} is supervising this legacy daemon.`,
22361
+ cleanupCommands: [`pm2 delete ${shellQuote(name)}`, "pm2 save"]
22362
+ };
22363
+ }
21902
22364
  } catch {
21903
- continue;
21904
22365
  }
21905
- try {
21906
- const attachment = JSON.parse(raw);
22366
+ }
22367
+ return {
22368
+ kind: "pm2",
22369
+ detail: "PM2 is supervising this legacy daemon, but its app name could not be resolved.",
22370
+ cleanupCommands: ["pm2 delete <legacy-daemon-app-name>", "pm2 save"]
22371
+ };
22372
+ }
22373
+ function systemdUnitFromCgroup(cgroup) {
22374
+ if (!cgroup) return void 0;
22375
+ for (const line of cgroup.split("\n")) {
22376
+ const pathPart = line.slice(line.lastIndexOf(":") + 1);
22377
+ let unit;
22378
+ for (const segment of pathPart.split("/")) {
22379
+ if (segment.endsWith(".service") && segment.length > ".service".length && !/^user@\d+\.service$/.test(segment)) {
22380
+ unit = segment;
22381
+ }
22382
+ }
22383
+ if (unit) return { unit, user: pathPart.includes("/user.slice/") };
22384
+ }
22385
+ return void 0;
22386
+ }
22387
+ function detectSystemd(probe) {
22388
+ if (probe.platform !== "linux") return void 0;
22389
+ const cgroupUnit = systemdUnitFromCgroup(
22390
+ probe.readFile(`/proc/${probe.pid}/cgroup`)
22391
+ );
22392
+ const hasSystemdEvidence = cgroupUnit !== void 0 || probe.env.INVOCATION_ID !== void 0;
22393
+ if (!hasSystemdEvidence) return void 0;
22394
+ if (cgroupUnit) {
22395
+ const prefix = cgroupUnit.user ? "systemctl --user" : "sudo systemctl";
22396
+ return {
22397
+ kind: "systemd",
22398
+ detail: `systemd unit ${JSON.stringify(cgroupUnit.unit)} is supervising this legacy daemon.`,
22399
+ cleanupCommands: [
22400
+ `${prefix} disable --now ${shellQuote(cgroupUnit.unit)}`
22401
+ ]
22402
+ };
22403
+ }
22404
+ return {
22405
+ kind: "systemd",
22406
+ detail: "systemd is supervising this legacy daemon, but its unit name could not be resolved.",
22407
+ cleanupCommands: [
22408
+ "systemctl --user disable --now <legacy-daemon-unit>.service"
22409
+ ]
22410
+ };
22411
+ }
22412
+ function detectLaunchd(probe) {
22413
+ if (probe.platform !== "darwin") return void 0;
22414
+ const label = probe.env.LAUNCH_JOBKEY_LABEL || probe.env.XPC_SERVICE_NAME;
22415
+ if (!label || label === "0") return void 0;
22416
+ return {
22417
+ kind: "launchd",
22418
+ detail: `launchd job ${JSON.stringify(label)} is supervising this legacy daemon.`,
22419
+ cleanupCommands: [
22420
+ `launchctl bootout gui/$(id -u)/${shellQuote(label)}`,
22421
+ `rm -f ~/Library/LaunchAgents/${shellQuote(`${label}.plist`)}`
22422
+ ]
22423
+ };
22424
+ }
22425
+ function detectLegacyDaemonSupervisor(pid = process.pid) {
22426
+ return detectLegacyDaemonSupervisorWithProbe(defaultProbe(pid));
22427
+ }
22428
+ function detectLegacyDaemonSupervisorWithProbe(probe) {
22429
+ const relatedPids = relatedProcessIds(probe);
22430
+ return detectPm2(probe, relatedPids) ?? detectSystemd(probe) ?? detectLaunchd(probe);
22431
+ }
22432
+ var legacyDaemonSupervisorFallbackCommands = [
22433
+ "PM2: pm2 delete <legacy-daemon-app-name> && pm2 save",
22434
+ "systemd: systemctl --user disable --now <legacy-daemon-unit>.service",
22435
+ "launchd: launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/<legacy-daemon-label>.plist && rm -f ~/Library/LaunchAgents/<legacy-daemon-label>.plist"
22436
+ ];
22437
+
22438
+ // src/computerMigrationGuard.ts
22439
+ var LegacyDaemonKeyAdoptedByComputerError = class extends Error {
22440
+ code = "LEGACY_DAEMON_KEY_ADOPTED_BY_COMPUTER";
22441
+ attachmentPath;
22442
+ serverId;
22443
+ constructor(match, supervisor = detectLegacyDaemonSupervisor() ?? null) {
22444
+ const serverDisplay = match.serverSlug ? `/${match.serverSlug}` : match.serverId;
22445
+ const startCommand = match.serverSlug ? `raft-computer start /${match.serverSlug}` : "raft-computer start";
22446
+ const statusCommand = match.serverSlug ? `raft-computer status /${match.serverSlug}` : "raft-computer status";
22447
+ const supervisorGuidance = formatSupervisorGuidance(
22448
+ supervisor ?? void 0
22449
+ );
22450
+ super(
22451
+ `Legacy Raft daemon startup refused: this machine key was already migrated to Raft Computer for ${serverDisplay}.
22452
+ Raft Computer now owns this connection; do not restart raft-daemon with the migrated key.
22453
+ ${supervisorGuidance}
22454
+ Then start and verify Raft Computer:
22455
+ ${startCommand}
22456
+ ${statusCommand}`
22457
+ );
22458
+ this.name = "LegacyDaemonKeyAdoptedByComputerError";
22459
+ this.attachmentPath = match.attachmentPath;
22460
+ this.serverId = match.serverId;
22461
+ }
22462
+ };
22463
+ function formatSupervisorGuidance(supervisor) {
22464
+ if (supervisor) {
22465
+ return `${supervisor.detail}
22466
+ Remove the old supervisor entry so it cannot auto-restart:
22467
+ ` + supervisor.cleanupCommands.map((command) => ` ${command}`).join("\n");
22468
+ }
22469
+ return `If a process manager keeps restarting the old daemon, remove the entry you configured:
22470
+ ` + legacyDaemonSupervisorFallbackCommands.map((command) => ` ${command}`).join("\n");
22471
+ }
22472
+ function daemonApiKeyFingerprint(apiKey) {
22473
+ return getDaemonMachineLockId(apiKey).slice("machine-".length);
22474
+ }
22475
+ function findAdoptedComputerForLegacyFingerprint(slockHome, legacyApiKeyFingerprint) {
22476
+ const serversDir = path18.join(slockHome, "computer", "servers");
22477
+ if (!existsSync9(serversDir)) return null;
22478
+ let serverDirs;
22479
+ try {
22480
+ serverDirs = readdirSync6(serversDir);
22481
+ } catch {
22482
+ return null;
22483
+ }
22484
+ for (const serverId of serverDirs) {
22485
+ const attachmentPath = path18.join(serversDir, serverId, "runner.state.json");
22486
+ let raw;
22487
+ try {
22488
+ raw = readFileSync9(attachmentPath, "utf8");
22489
+ } catch {
22490
+ continue;
22491
+ }
22492
+ try {
22493
+ const attachment = JSON.parse(raw);
21907
22494
  if (attachment.kind === "computer-attachment" && attachment.adoptedFromLegacy === true && attachment.legacyApiKeyFingerprint === legacyApiKeyFingerprint) {
21908
22495
  return {
21909
22496
  attachmentPath,
@@ -21912,20 +22499,543 @@ function findAdoptedComputerForLegacyFingerprint(slockHome, legacyApiKeyFingerpr
21912
22499
  serverMachineId: typeof attachment.serverMachineId === "string" && attachment.serverMachineId.length > 0 ? attachment.serverMachineId : void 0
21913
22500
  };
21914
22501
  }
21915
- } catch {
21916
- continue;
22502
+ } catch {
22503
+ continue;
22504
+ }
22505
+ }
22506
+ return null;
22507
+ }
22508
+ function assertLegacyDaemonKeyNotAdoptedByComputer(options) {
22509
+ const legacyApiKeyFingerprint = daemonApiKeyFingerprint(options.apiKey);
22510
+ const match = findAdoptedComputerForLegacyFingerprint(
22511
+ options.slockHome,
22512
+ legacyApiKeyFingerprint
22513
+ );
22514
+ if (match) throw new LegacyDaemonKeyAdoptedByComputerError(match);
22515
+ }
22516
+
22517
+ // src/agentMigrationHttpTransport.ts
22518
+ import { createHash as createHash7, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
22519
+ import http2 from "http";
22520
+ import { Readable } from "stream";
22521
+ var AGENT_MIGRATION_TRANSPORT_HOST_ENV = "SLOCK_AGENT_MIGRATION_TRANSPORT_HOST";
22522
+ var AGENT_MIGRATION_TRANSPORT_PORT_ENV = "SLOCK_AGENT_MIGRATION_TRANSPORT_PORT";
22523
+ var AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV = "SLOCK_AGENT_MIGRATION_TRANSPORT_PUBLIC_URL";
22524
+ var AGENT_MIGRATION_CONTROL_SEAM_ENV = "SLOCK_AGENT_MIGRATION_CONTROL_SEAM";
22525
+ var CONTROL_REQUEST_MAX_BYTES = 8 * 1024 * 1024;
22526
+ var MigrationBundleStreamError = class extends Error {
22527
+ constructor(code) {
22528
+ super(code);
22529
+ this.code = code;
22530
+ }
22531
+ };
22532
+ var AgentMigrationGrantRegistry = class {
22533
+ grants = /* @__PURE__ */ new Map();
22534
+ now;
22535
+ constructor(now = currentDate) {
22536
+ this.now = now;
22537
+ }
22538
+ createGrant(input) {
22539
+ const token = input.token ?? randomBytes2(32).toString("base64url");
22540
+ const manifestSha256 = sha256Buffer(canonicalJsonBuffer(input.manifest));
22541
+ const grant = {
22542
+ grantId: input.grantId ?? randomBytes2(16).toString("hex"),
22543
+ tokenHash: hashToken(token),
22544
+ expiresAt: input.expiresAt,
22545
+ oneTimeUse: input.oneTimeUse ?? true,
22546
+ state: "issued",
22547
+ manifest: input.manifest,
22548
+ manifestSha256,
22549
+ bundleEtag: `"agent-migration-${manifestSha256}"`,
22550
+ bundleSizeBytes: normalizeBundleSizeBytes(input.bundleSizeBytes)
22551
+ };
22552
+ this.grants.set(grant.grantId, grant);
22553
+ return { grant, token };
22554
+ }
22555
+ get(grantId) {
22556
+ return this.grants.get(grantId);
22557
+ }
22558
+ authenticate(grantId, token) {
22559
+ const grant = this.grants.get(grantId);
22560
+ if (!grant || !token || !verifyTokenHash(token, grant.tokenHash)) {
22561
+ return { ok: false, status: 401, code: "migration_grant_auth_failed" };
22562
+ }
22563
+ if (grant.state === "revoked" || grant.state === "consumed") {
22564
+ return { ok: false, status: 410, code: "migration_grant_unavailable" };
22565
+ }
22566
+ if (this.now().getTime() >= grant.expiresAt.getTime()) {
22567
+ grant.state = "expired";
22568
+ return { ok: false, status: 410, code: "migration_grant_unavailable" };
22569
+ }
22570
+ return { ok: true, grant };
22571
+ }
22572
+ revokeAll() {
22573
+ for (const grant of this.grants.values()) {
22574
+ if (grant.state !== "consumed" && grant.state !== "expired") {
22575
+ grant.state = "revoked";
22576
+ }
22577
+ }
22578
+ }
22579
+ beginBundleStream(grant) {
22580
+ const current = this.grants.get(grant.grantId);
22581
+ if (current !== grant) return false;
22582
+ if (current.state !== "issued" && current.state !== "interrupted") return false;
22583
+ current.state = "streaming";
22584
+ return true;
22585
+ }
22586
+ };
22587
+ function createAgentMigrationHttpTransport(options = {}) {
22588
+ const now = options.now ?? currentDate;
22589
+ const registry = new AgentMigrationGrantRegistry(now);
22590
+ const controlBundles = /* @__PURE__ */ new Map();
22591
+ const fallbackBundleStreamFactory = options.bundleStreamFactory ?? defaultBundleStream;
22592
+ const bundleStreamFactory = (grant, request) => {
22593
+ const bundle = controlBundles.get(grant.grantId);
22594
+ if (bundle) {
22595
+ const endExclusive = request.lengthBytes === null ? void 0 : request.offsetBytes + request.lengthBytes;
22596
+ return Readable.from([bundle.subarray(request.offsetBytes, endExclusive)]);
22597
+ }
22598
+ return fallbackBundleStreamFactory(grant, request);
22599
+ };
22600
+ const listenDefaults = options.listen ?? resolveAgentMigrationHttpTransportListenOptions();
22601
+ const controlSeamEnabled = options.controlSeam ?? resolveAgentMigrationControlSeamEnabled();
22602
+ const server = http2.createServer((req, res) => {
22603
+ const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22604
+ if (parsed.pathname.startsWith("/migration-control")) {
22605
+ void handleMigrationControlRequest(req, res, parsed, {
22606
+ enabled: controlSeamEnabled,
22607
+ now,
22608
+ registry,
22609
+ controlBundles
22610
+ });
22611
+ return;
22612
+ }
22613
+ void handleMigrationRequest(req, res, registry, bundleStreamFactory);
22614
+ });
22615
+ return {
22616
+ grants: registry,
22617
+ server,
22618
+ listen: (port = listenDefaults.port ?? 0, host = listenDefaults.host ?? "127.0.0.1") => new Promise((resolve, reject) => {
22619
+ server.once("error", reject);
22620
+ server.listen(port, host, () => {
22621
+ server.off("error", reject);
22622
+ const address = server.address();
22623
+ if (!address || typeof address === "string") {
22624
+ reject(new Error("migration transport listen did not produce a TCP address"));
22625
+ return;
22626
+ }
22627
+ resolve({ url: listenDefaults.publicUrl ?? `http://${formatListenAddress(address.address)}:${address.port}` });
22628
+ });
22629
+ }),
22630
+ close: () => new Promise((resolve, reject) => {
22631
+ registry.revokeAll();
22632
+ server.close((err) => {
22633
+ if (err) reject(err);
22634
+ else resolve();
22635
+ });
22636
+ })
22637
+ };
22638
+ }
22639
+ function resolveAgentMigrationHttpTransportListenOptions(env = process.env) {
22640
+ const host = env[AGENT_MIGRATION_TRANSPORT_HOST_ENV]?.trim();
22641
+ const port = parseOptionalPort(env[AGENT_MIGRATION_TRANSPORT_PORT_ENV]);
22642
+ const publicUrl = env[AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV]?.trim();
22643
+ return {
22644
+ ...host ? { host } : {},
22645
+ ...port !== void 0 ? { port } : {},
22646
+ ...publicUrl ? { publicUrl } : {}
22647
+ };
22648
+ }
22649
+ function resolveAgentMigrationControlSeamEnabled(env = process.env) {
22650
+ return env[AGENT_MIGRATION_CONTROL_SEAM_ENV]?.trim() !== "0";
22651
+ }
22652
+ async function handleMigrationControlRequest(req, res, parsed, context) {
22653
+ if (!context.enabled) {
22654
+ sendJson(res, 404, { code: "migration_route_not_found" });
22655
+ return;
22656
+ }
22657
+ if (parsed.pathname !== "/migration-control/grants") {
22658
+ sendJson(res, 404, { code: "migration_route_not_found" });
22659
+ return;
22660
+ }
22661
+ if (req.method !== "POST") {
22662
+ sendJson(res, 405, { code: "migration_method_not_allowed" });
22663
+ return;
22664
+ }
22665
+ let rawInput;
22666
+ try {
22667
+ rawInput = await readJsonRequestBody(req, CONTROL_REQUEST_MAX_BYTES);
22668
+ } catch (err) {
22669
+ if (err instanceof MigrationBundleStreamError) {
22670
+ sendJson(res, err.code === "migration_control_payload_too_large" ? 413 : 400, { code: err.code });
22671
+ return;
22672
+ }
22673
+ sendJson(res, 400, { code: "migration_control_invalid_json" });
22674
+ return;
22675
+ }
22676
+ const input = parseControlGrantInput(rawInput, context.now);
22677
+ if (!input.ok) {
22678
+ sendJson(res, 400, { code: input.code });
22679
+ return;
22680
+ }
22681
+ const { bundle, createGrantInput } = input;
22682
+ const { grant, token } = context.registry.createGrant({
22683
+ ...createGrantInput,
22684
+ bundleSizeBytes: bundle.byteLength
22685
+ });
22686
+ context.controlBundles.set(grant.grantId, bundle);
22687
+ sendJson(res, 201, {
22688
+ grantId: grant.grantId,
22689
+ token,
22690
+ manifestSha256: grant.manifestSha256,
22691
+ bundleSizeBytes: grant.bundleSizeBytes,
22692
+ expiresAt: grant.expiresAt.toISOString(),
22693
+ manifestUrl: `/migration/${encodeURIComponent(grant.grantId)}/manifest`,
22694
+ bundleUrl: `/migration/${encodeURIComponent(grant.grantId)}/bundle.tar`
22695
+ });
22696
+ }
22697
+ async function handleMigrationRequest(req, res, registry, bundleStreamFactory) {
22698
+ const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22699
+ const match = /^\/migration\/([^/]+)\/([^/]+)(?:\/([^/]+))?$/.exec(parsed.pathname);
22700
+ if (!match) {
22701
+ sendJson(res, 404, { code: "migration_route_not_found" });
22702
+ return;
22703
+ }
22704
+ const [, grantId, resource, resourceId] = match;
22705
+ const auth = registry.authenticate(decodeURIComponent(grantId), extractBearerToken(req));
22706
+ if (!auth.ok) {
22707
+ sendJson(res, auth.status, { code: auth.code });
22708
+ return;
22709
+ }
22710
+ if (resource === "manifest" && req.method === "GET" && !resourceId) {
22711
+ sendJson(res, 200, {
22712
+ manifestSha256: auth.grant.manifestSha256,
22713
+ manifest: auth.grant.manifest
22714
+ }, {
22715
+ "X-Raft-Manifest-Sha": auth.grant.manifestSha256,
22716
+ ETag: `"manifest-${auth.grant.manifestSha256}"`
22717
+ });
22718
+ return;
22719
+ }
22720
+ if (resource === "bundle.tar" && !resourceId) {
22721
+ if (req.method === "HEAD") {
22722
+ sendHead(res, auth.grant);
22723
+ return;
22724
+ }
22725
+ if (req.method === "GET") {
22726
+ await streamBundle(req, res, auth.grant, registry, bundleStreamFactory);
22727
+ return;
22728
+ }
22729
+ }
22730
+ if (resource === "chunk" && req.method === "GET" && resourceId) {
22731
+ sendJson(res, 501, { code: "migration_chunk_not_implemented" });
22732
+ return;
22733
+ }
22734
+ sendJson(res, 405, { code: "migration_method_not_allowed" });
22735
+ }
22736
+ function sendHead(res, grant) {
22737
+ res.statusCode = 200;
22738
+ res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22739
+ res.setHeader("ETag", grant.bundleEtag);
22740
+ res.setHeader("Accept-Ranges", grant.bundleSizeBytes === null ? "none" : "bytes");
22741
+ res.setHeader("X-Raft-Bundle-Size", grant.bundleSizeBytes === null ? "unknown" : grant.bundleSizeBytes.toString());
22742
+ if (grant.bundleSizeBytes !== null) {
22743
+ res.setHeader("Content-Length", grant.bundleSizeBytes.toString());
22744
+ }
22745
+ res.end();
22746
+ }
22747
+ async function streamBundle(req, res, grant, registry, bundleStreamFactory) {
22748
+ if (grant.state === "streaming") {
22749
+ sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22750
+ return;
22751
+ }
22752
+ if (grant.oneTimeUse && grant.state !== "issued" && grant.state !== "interrupted") {
22753
+ sendJson(res, 410, { code: "migration_grant_unavailable" });
22754
+ return;
22755
+ }
22756
+ const range = planRange(req, grant);
22757
+ if (!range.ok) {
22758
+ sendJson(res, 416, { code: range.code }, range.headers);
22759
+ return;
22760
+ }
22761
+ const previousState = grant.state;
22762
+ if (!registry.beginBundleStream(grant)) {
22763
+ sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22764
+ return;
22765
+ }
22766
+ let stream;
22767
+ try {
22768
+ stream = bundleStreamFactory(grant, range.request);
22769
+ } catch (err) {
22770
+ grant.state = previousState;
22771
+ const code = err instanceof MigrationBundleStreamError ? err.code : "migration_bundle_stream_failed";
22772
+ sendJson(res, 500, { code });
22773
+ return;
22774
+ }
22775
+ let completed = false;
22776
+ res.on("close", () => {
22777
+ if (!completed && grant.state === "streaming") {
22778
+ grant.state = "interrupted";
22779
+ }
22780
+ });
22781
+ try {
22782
+ res.statusCode = 200;
22783
+ res.setHeader("Content-Type", "application/x-tar");
22784
+ res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22785
+ res.setHeader("ETag", grant.bundleEtag);
22786
+ res.setHeader("Accept-Ranges", grant.bundleSizeBytes === null ? "none" : "bytes");
22787
+ if (range.request.range) {
22788
+ res.statusCode = 206;
22789
+ res.setHeader("Content-Range", `bytes ${range.request.range.start}-${range.request.range.end}/${grant.bundleSizeBytes}`);
22790
+ }
22791
+ if (range.request.lengthBytes !== null) {
22792
+ res.setHeader("Content-Length", range.request.lengthBytes.toString());
22793
+ }
22794
+ for await (const chunk of stream) {
22795
+ if (!res.write(chunk)) {
22796
+ await onceDrain(res);
22797
+ }
22798
+ }
22799
+ completed = true;
22800
+ grant.state = range.consumesGrant ? "consumed" : "interrupted";
22801
+ res.end();
22802
+ } catch {
22803
+ if (!completed) {
22804
+ grant.state = "interrupted";
22805
+ }
22806
+ if (!res.headersSent) {
22807
+ sendJson(res, 500, { code: "migration_bundle_stream_failed" });
22808
+ } else {
22809
+ res.destroy();
22810
+ }
22811
+ }
22812
+ }
22813
+ function planRange(req, grant) {
22814
+ const header = singleHeader(req.headers.range);
22815
+ if (!header) {
22816
+ return {
22817
+ ok: true,
22818
+ request: { range: null, offsetBytes: 0, lengthBytes: grant.bundleSizeBytes },
22819
+ consumesGrant: true
22820
+ };
22821
+ }
22822
+ if (grant.bundleSizeBytes === null) {
22823
+ return {
22824
+ ok: false,
22825
+ code: "migration_range_not_available",
22826
+ headers: { "Accept-Ranges": "none" }
22827
+ };
22828
+ }
22829
+ const parsed = parseSingleByteRange(header, grant.bundleSizeBytes);
22830
+ if (!parsed.ok) {
22831
+ return {
22832
+ ok: false,
22833
+ code: parsed.code,
22834
+ headers: {
22835
+ "Accept-Ranges": "bytes",
22836
+ "Content-Range": `bytes */${grant.bundleSizeBytes}`
22837
+ }
22838
+ };
22839
+ }
22840
+ return {
22841
+ ok: true,
22842
+ request: {
22843
+ range: parsed.range,
22844
+ offsetBytes: parsed.range.start,
22845
+ lengthBytes: parsed.range.end - parsed.range.start + 1
22846
+ },
22847
+ consumesGrant: parsed.range.end === grant.bundleSizeBytes - 1
22848
+ };
22849
+ }
22850
+ function parseSingleByteRange(header, sizeBytes) {
22851
+ const match = /^bytes=(\d+)-(\d*)$/.exec(header.trim());
22852
+ if (!match) {
22853
+ return { ok: false, code: "migration_range_not_satisfiable" };
22854
+ }
22855
+ const start = Number(match[1]);
22856
+ const requestedEnd = match[2] ? Number(match[2]) : sizeBytes - 1;
22857
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd) || start < 0 || requestedEnd < start || start >= sizeBytes) {
22858
+ return { ok: false, code: "migration_range_not_satisfiable" };
22859
+ }
22860
+ return {
22861
+ ok: true,
22862
+ range: {
22863
+ start,
22864
+ end: Math.min(requestedEnd, sizeBytes - 1)
22865
+ }
22866
+ };
22867
+ }
22868
+ function defaultBundleStream(grant) {
22869
+ void grant;
22870
+ throw new MigrationBundleStreamError("migration_bundle_stream_not_wired");
22871
+ }
22872
+ function parseControlGrantInput(rawInput, now) {
22873
+ if (!rawInput || typeof rawInput !== "object" || Array.isArray(rawInput)) {
22874
+ return { ok: false, code: "migration_control_invalid_payload" };
22875
+ }
22876
+ const input = rawInput;
22877
+ const manifest = input.manifest;
22878
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
22879
+ return { ok: false, code: "migration_control_manifest_required" };
22880
+ }
22881
+ const bundle = decodeControlBundle(input);
22882
+ if (!bundle.ok) return bundle;
22883
+ const expiresAt = parseControlGrantExpiry(input, now);
22884
+ if (!expiresAt.ok) return expiresAt;
22885
+ const grantId = optionalNonEmptyString(input.grantId);
22886
+ const token = optionalNonEmptyString(input.token);
22887
+ const oneTimeUse = typeof input.oneTimeUse === "boolean" ? input.oneTimeUse : void 0;
22888
+ return {
22889
+ ok: true,
22890
+ bundle: bundle.bundle,
22891
+ createGrantInput: {
22892
+ ...grantId ? { grantId } : {},
22893
+ ...token ? { token } : {},
22894
+ expiresAt: expiresAt.expiresAt,
22895
+ manifest,
22896
+ ...oneTimeUse === void 0 ? {} : { oneTimeUse }
21917
22897
  }
22898
+ };
22899
+ }
22900
+ function decodeControlBundle(input) {
22901
+ if (typeof input.bundleBase64 === "string") {
22902
+ const normalized = input.bundleBase64.trim();
22903
+ if (!normalized) return { ok: false, code: "migration_control_bundle_required" };
22904
+ const bundle = Buffer.from(normalized, "base64");
22905
+ if (bundle.byteLength === 0) return { ok: false, code: "migration_control_bundle_required" };
22906
+ return { ok: true, bundle };
21918
22907
  }
21919
- return null;
22908
+ if (typeof input.bundleText === "string") {
22909
+ const bundle = Buffer.from(input.bundleText, "utf8");
22910
+ if (bundle.byteLength === 0) return { ok: false, code: "migration_control_bundle_required" };
22911
+ return { ok: true, bundle };
22912
+ }
22913
+ return { ok: false, code: "migration_control_bundle_required" };
21920
22914
  }
21921
- function assertLegacyDaemonKeyNotAdoptedByComputer(options) {
21922
- const legacyApiKeyFingerprint = daemonApiKeyFingerprint(options.apiKey);
21923
- const match = findAdoptedComputerForLegacyFingerprint(options.slockHome, legacyApiKeyFingerprint);
21924
- if (match) throw new LegacyDaemonKeyAdoptedByComputerError(match);
22915
+ function parseControlGrantExpiry(input, now) {
22916
+ if (typeof input.expiresAt === "string" && input.expiresAt.trim()) {
22917
+ const expiresAt = new Date(input.expiresAt);
22918
+ if (Number.isNaN(expiresAt.getTime())) {
22919
+ return { ok: false, code: "migration_control_invalid_expiry" };
22920
+ }
22921
+ return { ok: true, expiresAt };
22922
+ }
22923
+ if (input.expiresInSeconds !== void 0) {
22924
+ const expiresInSeconds = input.expiresInSeconds;
22925
+ if (typeof expiresInSeconds !== "number" || !Number.isSafeInteger(expiresInSeconds) || expiresInSeconds <= 0) {
22926
+ return { ok: false, code: "migration_control_invalid_expiry" };
22927
+ }
22928
+ return { ok: true, expiresAt: new Date(now().getTime() + expiresInSeconds * 1e3) };
22929
+ }
22930
+ return { ok: true, expiresAt: new Date(now().getTime() + 15 * 60 * 1e3) };
22931
+ }
22932
+ function optionalNonEmptyString(value) {
22933
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
22934
+ }
22935
+ function normalizeBundleSizeBytes(size) {
22936
+ if (size === null || size === void 0) return null;
22937
+ if (!Number.isSafeInteger(size) || size < 0) return null;
22938
+ return size;
22939
+ }
22940
+ function parseOptionalPort(raw) {
22941
+ const value = raw?.trim();
22942
+ if (!value) return void 0;
22943
+ const port = Number(value);
22944
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
22945
+ throw new Error(`${AGENT_MIGRATION_TRANSPORT_PORT_ENV} must be an integer from 0 to 65535`);
22946
+ }
22947
+ return port;
22948
+ }
22949
+ function formatListenAddress(address) {
22950
+ if (address.includes(":") && !address.startsWith("[")) return `[${address}]`;
22951
+ return address;
22952
+ }
22953
+ function extractBearerToken(req) {
22954
+ const directHeader = singleHeader(req.headers["x-raft-migration-token"]);
22955
+ if (directHeader) return directHeader;
22956
+ const authorization = singleHeader(req.headers.authorization);
22957
+ if (!authorization) return null;
22958
+ const match = /^Bearer\s+(.+)$/i.exec(authorization);
22959
+ return match?.[1] ?? null;
22960
+ }
22961
+ function singleHeader(value) {
22962
+ if (!value) return null;
22963
+ return Array.isArray(value) ? value[0] ?? null : value;
22964
+ }
22965
+ function sendJson(res, status, body, headers = {}) {
22966
+ const payload = JSON.stringify(body);
22967
+ res.writeHead(status, {
22968
+ "Content-Type": "application/json",
22969
+ "Content-Length": Buffer.byteLength(payload).toString(),
22970
+ ...headers
22971
+ });
22972
+ res.end(payload);
22973
+ }
22974
+ function readJsonRequestBody(req, maxBytes) {
22975
+ return new Promise((resolve, reject) => {
22976
+ const chunks = [];
22977
+ let totalBytes = 0;
22978
+ let rejected = false;
22979
+ req.on("data", (chunk) => {
22980
+ if (rejected) return;
22981
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
22982
+ totalBytes += buffer.byteLength;
22983
+ if (totalBytes > maxBytes) {
22984
+ rejected = true;
22985
+ reject(new MigrationBundleStreamError("migration_control_payload_too_large"));
22986
+ return;
22987
+ }
22988
+ chunks.push(buffer);
22989
+ });
22990
+ req.on("error", reject);
22991
+ req.on("end", () => {
22992
+ if (rejected) return;
22993
+ try {
22994
+ const text = Buffer.concat(chunks).toString("utf8");
22995
+ resolve(JSON.parse(text));
22996
+ } catch {
22997
+ reject(new MigrationBundleStreamError("migration_control_invalid_json"));
22998
+ }
22999
+ });
23000
+ });
23001
+ }
23002
+ function hashToken(token) {
23003
+ return sha256Buffer(Buffer.from(token, "utf8"));
23004
+ }
23005
+ function verifyTokenHash(token, expectedHashHex) {
23006
+ const actual = Buffer.from(hashToken(token), "hex");
23007
+ const expected = Buffer.from(expectedHashHex, "hex");
23008
+ if (actual.byteLength !== expected.byteLength) return false;
23009
+ return timingSafeEqual(actual, expected);
23010
+ }
23011
+ function sha256Buffer(buffer) {
23012
+ return createHash7("sha256").update(buffer).digest("hex");
23013
+ }
23014
+ function canonicalJsonBuffer(value) {
23015
+ return Buffer.from(canonicalJson(value), "utf8");
21925
23016
  }
23017
+ function canonicalJson(value) {
23018
+ return JSON.stringify(sortJsonValue(value));
23019
+ }
23020
+ function sortJsonValue(value) {
23021
+ if (Array.isArray(value)) return value.map(sortJsonValue);
23022
+ if (!value || typeof value !== "object") return value;
23023
+ return Object.keys(value).sort().reduce((result, key) => {
23024
+ result[key] = sortJsonValue(value[key]);
23025
+ return result;
23026
+ }, {});
23027
+ }
23028
+ function onceDrain(res) {
23029
+ return new Promise((resolve) => res.once("drain", resolve));
23030
+ }
23031
+
23032
+ // src/agentMigrationObjectStoreBundle.ts
23033
+ import { createHash as createHash9 } from "crypto";
23034
+ import { lstat as lstat3, mkdir as mkdir3, readFile as readFile4, readlink as readlink2, rm as rm3, symlink, writeFile as writeFile3 } from "fs/promises";
23035
+ import path20 from "path";
21926
23036
 
21927
23037
  // src/agentMigrationExport.ts
21928
- import { createHash as createHash7 } from "crypto";
23038
+ import { createHash as createHash8 } from "crypto";
21929
23039
  import { createReadStream } from "fs";
21930
23040
  import { lstat as lstat2, readdir as readdir4, readFile as readFile3, readlink } from "fs/promises";
21931
23041
  import path19 from "path";
@@ -22139,7 +23249,7 @@ async function buildCrossTreeRefs(refs) {
22139
23249
  return result;
22140
23250
  }
22141
23251
  async function sha256File(filePath) {
22142
- const hash = createHash7("sha256");
23252
+ const hash = createHash8("sha256");
22143
23253
  await new Promise((resolve, reject) => {
22144
23254
  const stream = createReadStream(filePath);
22145
23255
  stream.on("data", (chunk) => hash.update(chunk));
@@ -22245,240 +23355,362 @@ function sortByPath(entries) {
22245
23355
  return [...entries].sort((a, b) => a.path.localeCompare(b.path));
22246
23356
  }
22247
23357
 
22248
- // src/agentMigrationHttpTransport.ts
22249
- import { createHash as createHash8, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
22250
- import http2 from "http";
22251
- var MigrationBundleStreamError = class extends Error {
22252
- constructor(code) {
22253
- super(code);
22254
- this.code = code;
22255
- }
22256
- };
22257
- var AgentMigrationGrantRegistry = class {
22258
- grants = /* @__PURE__ */ new Map();
22259
- now;
22260
- constructor(now = () => /* @__PURE__ */ new Date()) {
22261
- this.now = now;
22262
- }
22263
- createGrant(input) {
22264
- const token = input.token ?? randomBytes2(32).toString("base64url");
22265
- const manifestSha256 = sha256Buffer(canonicalJsonBuffer(input.manifest));
22266
- const grant = {
22267
- grantId: input.grantId ?? randomBytes2(16).toString("hex"),
22268
- tokenHash: hashToken(token),
22269
- expiresAt: input.expiresAt,
22270
- oneTimeUse: input.oneTimeUse ?? true,
22271
- state: "issued",
22272
- manifest: input.manifest,
22273
- manifestSha256,
22274
- bundleEtag: `"agent-migration-${manifestSha256}"`
22275
- };
22276
- this.grants.set(grant.grantId, grant);
22277
- return { grant, token };
23358
+ // src/agentMigrationObjectStoreBundle.ts
23359
+ var AGENT_MIGRATION_OBJECT_STORE_BUNDLE_SCHEMA_VERSION = "agent-object-store-bundle/v1";
23360
+ var AGENT_MIGRATION_OBJECT_STORE_CONTENT_TYPE = "application/vnd.slock.agent-migration-bundle";
23361
+ async function buildAgentMigrationObjectStoreBundle(input) {
23362
+ const workspacePath = path20.resolve(input.workspacePath);
23363
+ const manifest = await buildAgentMigrationExportManifest({
23364
+ agentId: input.agentId,
23365
+ slockHome: input.slockHome,
23366
+ workspacePath,
23367
+ mode: "forensic"
23368
+ });
23369
+ const files = [];
23370
+ for (const entry of manifest.files) {
23371
+ if (entry.source !== "workspace" || !entry.workspaceRelativePath) {
23372
+ throw new Error("MIGRATION_OBJECT_STORE_UNSUPPORTED_ENTRY");
23373
+ }
23374
+ const sourcePath = path20.resolve(entry.sourcePath);
23375
+ if (entry.kind === "symlink") {
23376
+ files.push({
23377
+ kind: "symlink",
23378
+ workspaceRelativePath: normalizeWorkspaceRelativePath(entry.workspaceRelativePath),
23379
+ linkTarget: entry.linkTarget ?? await readlink2(sourcePath)
23380
+ });
23381
+ continue;
23382
+ }
23383
+ const stat4 = await lstat3(sourcePath);
23384
+ if (!stat4.isFile()) throw new Error("MIGRATION_OBJECT_STORE_FILE_KIND_MISMATCH");
23385
+ files.push({
23386
+ kind: "file",
23387
+ workspaceRelativePath: normalizeWorkspaceRelativePath(entry.workspaceRelativePath),
23388
+ contentBase64: (await readFile4(sourcePath)).toString("base64")
23389
+ });
22278
23390
  }
22279
- get(grantId) {
22280
- return this.grants.get(grantId);
23391
+ const payload = {
23392
+ schemaVersion: AGENT_MIGRATION_OBJECT_STORE_BUNDLE_SCHEMA_VERSION,
23393
+ manifest,
23394
+ files
23395
+ };
23396
+ const bundle = Buffer.from(`${JSON.stringify(sortJsonValue2(payload))}
23397
+ `, "utf8");
23398
+ if (!Number.isSafeInteger(input.maxBytes) || input.maxBytes <= 0 || bundle.byteLength > input.maxBytes) {
23399
+ throw new Error("MIGRATION_OBJECT_STORE_BUNDLE_TOO_LARGE");
22281
23400
  }
22282
- authenticate(grantId, token) {
22283
- const grant = this.grants.get(grantId);
22284
- if (!grant || !token || !verifyTokenHash(token, grant.tokenHash)) {
22285
- return { ok: false, status: 401, code: "migration_grant_auth_failed" };
22286
- }
22287
- if (grant.state === "revoked" || grant.state === "consumed") {
22288
- return { ok: false, status: 410, code: "migration_grant_unavailable" };
22289
- }
22290
- if (this.now().getTime() >= grant.expiresAt.getTime()) {
22291
- grant.state = "expired";
22292
- return { ok: false, status: 410, code: "migration_grant_unavailable" };
23401
+ return {
23402
+ bundle,
23403
+ manifestSha256: sha256Buffer2(Buffer.from(canonicalJson2(manifest), "utf8"))
23404
+ };
23405
+ }
23406
+ async function stageAgentMigrationObjectStoreBundle(input) {
23407
+ const parsed = parseObjectStoreBundle(input.bundle);
23408
+ const stagingWorkspacePath = path20.join(
23409
+ path20.resolve(input.slockHome),
23410
+ "migrations",
23411
+ sanitizePathSegment(input.sessionId),
23412
+ "workspace"
23413
+ );
23414
+ await rm3(stagingWorkspacePath, { recursive: true, force: true });
23415
+ await mkdir3(stagingWorkspacePath, { recursive: true });
23416
+ for (const file of parsed.files) {
23417
+ const relativePath = normalizeWorkspaceRelativePath(file.workspaceRelativePath);
23418
+ const targetPath = path20.join(stagingWorkspacePath, relativePath);
23419
+ await mkdir3(path20.dirname(targetPath), { recursive: true });
23420
+ if (file.kind === "symlink") {
23421
+ if (!file.linkTarget) throw new Error("MIGRATION_OBJECT_STORE_LINK_TARGET_MISSING");
23422
+ await symlink(file.linkTarget, targetPath);
23423
+ continue;
22293
23424
  }
22294
- return { ok: true, grant };
22295
- }
22296
- revokeAll() {
22297
- for (const grant of this.grants.values()) {
22298
- if (grant.state !== "consumed" && grant.state !== "expired") {
22299
- grant.state = "revoked";
22300
- }
23425
+ if (file.kind !== "file" || typeof file.contentBase64 !== "string") {
23426
+ throw new Error("MIGRATION_OBJECT_STORE_FILE_CONTENT_MISSING");
22301
23427
  }
23428
+ await writeFile3(targetPath, Buffer.from(file.contentBase64, "base64"), { mode: 384 });
22302
23429
  }
22303
- };
22304
- function createAgentMigrationHttpTransport(options = {}) {
22305
- const registry = new AgentMigrationGrantRegistry(options.now);
22306
- const bundleStreamFactory = options.bundleStreamFactory ?? defaultBundleStream;
22307
- const server = http2.createServer((req, res) => {
22308
- void handleMigrationRequest(req, res, registry, bundleStreamFactory);
22309
- });
22310
23430
  return {
22311
- grants: registry,
22312
- server,
22313
- listen: (port = 0, host = "127.0.0.1") => new Promise((resolve, reject) => {
22314
- server.once("error", reject);
22315
- server.listen(port, host, () => {
22316
- server.off("error", reject);
22317
- const address = server.address();
22318
- if (!address || typeof address === "string") {
22319
- reject(new Error("migration transport listen did not produce a TCP address"));
22320
- return;
22321
- }
22322
- resolve({ url: `http://${address.address}:${address.port}` });
22323
- });
22324
- }),
22325
- close: () => new Promise((resolve, reject) => {
22326
- registry.revokeAll();
22327
- server.close((err) => {
22328
- if (err) reject(err);
22329
- else resolve();
22330
- });
22331
- })
23431
+ manifest: parsed.manifest,
23432
+ manifestSha256: sha256Buffer2(Buffer.from(canonicalJson2(parsed.manifest), "utf8")),
23433
+ stagingWorkspacePath
22332
23434
  };
22333
23435
  }
22334
- async function handleMigrationRequest(req, res, registry, bundleStreamFactory) {
22335
- const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22336
- const match = /^\/migration\/([^/]+)\/([^/]+)(?:\/([^/]+))?$/.exec(parsed.pathname);
22337
- if (!match) {
22338
- sendJson(res, 404, { code: "migration_route_not_found" });
22339
- return;
23436
+ function parseObjectStoreBundle(buffer) {
23437
+ let parsed;
23438
+ try {
23439
+ parsed = JSON.parse(buffer.toString("utf8"));
23440
+ } catch {
23441
+ throw new Error("MIGRATION_OBJECT_STORE_BUNDLE_INVALID_JSON");
22340
23442
  }
22341
- const [, grantId, resource, resourceId] = match;
22342
- const auth = registry.authenticate(decodeURIComponent(grantId), extractBearerToken(req));
22343
- if (!auth.ok) {
22344
- sendJson(res, auth.status, { code: auth.code });
22345
- return;
23443
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
23444
+ throw new Error("MIGRATION_OBJECT_STORE_BUNDLE_INVALID");
22346
23445
  }
22347
- if (resource === "manifest" && req.method === "GET" && !resourceId) {
22348
- sendJson(res, 200, {
22349
- manifestSha256: auth.grant.manifestSha256,
22350
- manifest: auth.grant.manifest
22351
- }, {
22352
- "X-Raft-Manifest-Sha": auth.grant.manifestSha256,
22353
- ETag: `"manifest-${auth.grant.manifestSha256}"`
23446
+ const bundle = parsed;
23447
+ if (bundle.schemaVersion !== AGENT_MIGRATION_OBJECT_STORE_BUNDLE_SCHEMA_VERSION) {
23448
+ throw new Error("MIGRATION_OBJECT_STORE_BUNDLE_SCHEMA_UNSUPPORTED");
23449
+ }
23450
+ if (!bundle.manifest || typeof bundle.manifest !== "object" || Array.isArray(bundle.manifest)) {
23451
+ throw new Error("MIGRATION_OBJECT_STORE_MANIFEST_MISSING");
23452
+ }
23453
+ if (!Array.isArray(bundle.files)) throw new Error("MIGRATION_OBJECT_STORE_FILES_MISSING");
23454
+ return bundle;
23455
+ }
23456
+ function normalizeWorkspaceRelativePath(relativePath) {
23457
+ const normalized = path20.posix.normalize(relativePath.replaceAll("\\", "/"));
23458
+ if (normalized === "." || normalized.startsWith("../") || normalized === ".." || path20.isAbsolute(normalized)) {
23459
+ throw new Error("MIGRATION_OBJECT_STORE_UNSAFE_PATH");
23460
+ }
23461
+ return normalized;
23462
+ }
23463
+ function canonicalJson2(value) {
23464
+ return JSON.stringify(sortJsonValue2(value));
23465
+ }
23466
+ function sortJsonValue2(value) {
23467
+ if (Array.isArray(value)) return value.map(sortJsonValue2);
23468
+ if (!value || typeof value !== "object") return value;
23469
+ return Object.keys(value).sort().reduce((result, key) => {
23470
+ result[key] = sortJsonValue2(value[key]);
23471
+ return result;
23472
+ }, {});
23473
+ }
23474
+ function sha256Buffer2(buffer) {
23475
+ return createHash9("sha256").update(buffer).digest("hex");
23476
+ }
23477
+ function sanitizePathSegment(value) {
23478
+ return value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 128) || "migration";
23479
+ }
23480
+
23481
+ // src/agentMigrationImport.ts
23482
+ import { createHash as createHash10 } from "crypto";
23483
+ import { createReadStream as createReadStream2 } from "fs";
23484
+ import { access as access2, cp, lstat as lstat4, mkdir as mkdir4, readlink as readlink3, rename, writeFile as writeFile4 } from "fs/promises";
23485
+ import path21 from "path";
23486
+ async function buildAgentMigrationAdoptPlan(input) {
23487
+ const slockHome = path21.resolve(input.slockHome);
23488
+ let manifest;
23489
+ if (input.sourceKind === "orphan_directory" && !input.manifest) {
23490
+ manifest = await buildAgentMigrationExportManifest({
23491
+ agentId: input.agentId,
23492
+ slockHome,
23493
+ workspacePath: path21.resolve(input.orphanWorkspacePath),
23494
+ mode: "forensic",
23495
+ now: input.now
22354
23496
  });
22355
- return;
23497
+ } else if (input.manifest) {
23498
+ manifest = input.manifest;
23499
+ } else {
23500
+ throw new Error("MIGRATION_MANIFEST_MISSING");
22356
23501
  }
22357
- if (resource === "bundle.tar" && !resourceId) {
22358
- if (req.method === "HEAD") {
22359
- sendHead(res, auth.grant);
22360
- return;
22361
- }
22362
- if (req.method === "GET") {
22363
- await streamBundle(res, auth.grant, bundleStreamFactory);
22364
- return;
22365
- }
23502
+ assertManifestIdentity({
23503
+ manifest,
23504
+ expectedAgentId: input.sourceKind === "orphan_directory" ? input.agentId : manifest.agentId
23505
+ });
23506
+ const sourceWorkspacePath = path21.resolve(
23507
+ input.sourceKind === "staged_bundle" ? input.stagingWorkspacePath : input.orphanWorkspacePath
23508
+ );
23509
+ const finalWorkspacePath = path21.resolve(input.finalWorkspacePath ?? path21.join(slockHome, "agents", manifest.agentId));
23510
+ const reportPath = path21.resolve(input.reportPath ?? path21.join(
23511
+ slockHome,
23512
+ "migrations",
23513
+ sanitizePathSegment2(input.generation.grantKey),
23514
+ "arrival-report.json"
23515
+ ));
23516
+ const manifestSha256 = hashAgentMigrationManifest(manifest);
23517
+ if (input.manifestSha256 && input.manifestSha256 !== manifestSha256) {
23518
+ throw new Error("MIGRATION_MANIFEST_SHA_MISMATCH");
23519
+ }
23520
+ return {
23521
+ sourceKind: input.sourceKind,
23522
+ agentId: manifest.agentId,
23523
+ slockHome,
23524
+ sourceWorkspacePath,
23525
+ finalWorkspacePath,
23526
+ reportPath,
23527
+ manifest,
23528
+ manifestSha256,
23529
+ generation: input.generation,
23530
+ attestationNonce: input.attestationNonce,
23531
+ observedAttestationNonce: input.observedAttestationNonce
23532
+ };
23533
+ }
23534
+ async function verifyAgentMigrationAdoptPlan(plan) {
23535
+ assertGeneration(plan.generation);
23536
+ assertManifestIdentity({ manifest: plan.manifest, expectedAgentId: plan.agentId });
23537
+ const actualManifestSha256 = hashAgentMigrationManifest(plan.manifest);
23538
+ if (actualManifestSha256 !== plan.manifestSha256) {
23539
+ throw new Error("MIGRATION_MANIFEST_SHA_MISMATCH");
23540
+ }
23541
+ let totalBytes = 0;
23542
+ let fileCount = 0;
23543
+ for (const entry of plan.manifest.files) {
23544
+ await verifyManifestFileEntry(plan.sourceWorkspacePath, entry);
23545
+ if (entry.kind === "file") {
23546
+ fileCount += 1;
23547
+ totalBytes += entry.sizeBytes ?? 0;
23548
+ }
23549
+ }
23550
+ return { fileCount, totalBytes };
23551
+ }
23552
+ async function executeAgentMigrationAdoptPlan(plan, rebind, now = currentDate()) {
23553
+ assertGeneration(plan.generation);
23554
+ await assertFinalWorkspaceAvailable(plan.sourceWorkspacePath, plan.finalWorkspacePath);
23555
+ const verified = await verifyAgentMigrationAdoptPlan(plan);
23556
+ let migrationGeneration = plan.generation.migrationGeneration;
23557
+ migrationGeneration = await assertRebindResult(plan, migrationGeneration, await rebind.startTransfer({
23558
+ grantKey: plan.generation.grantKey,
23559
+ migrationGeneration
23560
+ }));
23561
+ await placeWorkspace(plan.sourceWorkspacePath, plan.finalWorkspacePath);
23562
+ migrationGeneration = await assertRebindResult(plan, migrationGeneration, await rebind.flipMachine({
23563
+ grantKey: plan.generation.grantKey,
23564
+ migrationGeneration
23565
+ }));
23566
+ const report = {
23567
+ schemaVersion: "agent-arrival/v1",
23568
+ agentId: plan.agentId,
23569
+ sourceKind: plan.sourceKind,
23570
+ grantKey: plan.generation.grantKey,
23571
+ migrationGeneration,
23572
+ sourceMachineId: plan.generation.sourceMachineId,
23573
+ targetMachineId: plan.generation.targetMachineId,
23574
+ manifestSha256: plan.manifestSha256,
23575
+ finalWorkspacePath: plan.finalWorkspacePath,
23576
+ verified,
23577
+ attestation: {
23578
+ noncePresent: Boolean(plan.attestationNonce),
23579
+ nonceVerified: Boolean(plan.attestationNonce && plan.observedAttestationNonce === plan.attestationNonce)
23580
+ },
23581
+ arrivedAt: now.toISOString()
23582
+ };
23583
+ const reportSha256 = await writeArrivalReport(plan.reportPath, report);
23584
+ await assertRebindResult(plan, migrationGeneration, await rebind.markArrived({
23585
+ grantKey: plan.generation.grantKey,
23586
+ migrationGeneration,
23587
+ reportPath: plan.reportPath,
23588
+ reportSha256
23589
+ }));
23590
+ return { report, reportPath: plan.reportPath, reportSha256 };
23591
+ }
23592
+ function hashAgentMigrationManifest(manifest) {
23593
+ return sha256Buffer3(Buffer.from(canonicalJson3(manifest), "utf8"));
23594
+ }
23595
+ function assertManifestIdentity(input) {
23596
+ if (input.manifest.schemaVersion !== AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION) {
23597
+ throw new Error("MIGRATION_MANIFEST_SCHEMA_UNSUPPORTED");
22366
23598
  }
22367
- if (resource === "chunk" && req.method === "GET" && resourceId) {
22368
- sendJson(res, 501, { code: "migration_chunk_not_implemented" });
22369
- return;
23599
+ if (input.manifest.agentId !== input.expectedAgentId) {
23600
+ throw new Error("MIGRATION_MANIFEST_AGENT_MISMATCH");
22370
23601
  }
22371
- sendJson(res, 405, { code: "migration_method_not_allowed" });
22372
23602
  }
22373
- function sendHead(res, grant) {
22374
- res.statusCode = 200;
22375
- res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22376
- res.setHeader("ETag", grant.bundleEtag);
22377
- res.setHeader("Accept-Ranges", "none");
22378
- res.setHeader("X-Raft-Bundle-Size", "unknown");
22379
- res.end();
23603
+ function assertGeneration(generation) {
23604
+ if (generation.localMachineId !== generation.targetMachineId) {
23605
+ throw new Error("MIGRATION_TARGET_MACHINE_MISMATCH");
23606
+ }
23607
+ if (generation.sourceMachineId === generation.targetMachineId) {
23608
+ throw new Error("MIGRATION_SOURCE_TARGET_MACHINE_MATCH");
23609
+ }
23610
+ if (!generation.grantKey || !generation.migrationGeneration) {
23611
+ throw new Error("MIGRATION_GENERATION_MISSING");
23612
+ }
22380
23613
  }
22381
- async function streamBundle(res, grant, bundleStreamFactory) {
22382
- if (grant.state === "streaming") {
22383
- sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22384
- return;
23614
+ async function assertRebindResult(plan, expectedGeneration, result) {
23615
+ if (!result) return expectedGeneration;
23616
+ if (result.grantKey && result.grantKey !== plan.generation.grantKey) {
23617
+ throw new Error("MIGRATION_REBIND_GRANT_MISMATCH");
22385
23618
  }
22386
- if (grant.oneTimeUse && grant.state !== "issued" && grant.state !== "interrupted") {
22387
- sendJson(res, 410, { code: "migration_grant_unavailable" });
22388
- return;
23619
+ if (result.migrationGeneration !== void 0 && result.migrationGeneration.length === 0) {
23620
+ throw new Error("MIGRATION_REBIND_GENERATION_MISMATCH");
22389
23621
  }
22390
- let stream;
22391
- try {
22392
- stream = bundleStreamFactory(grant);
22393
- } catch (err) {
22394
- const code = err instanceof MigrationBundleStreamError ? err.code : "migration_bundle_stream_failed";
22395
- sendJson(res, 500, { code });
22396
- return;
23622
+ if (result.sourceMachineId && result.sourceMachineId !== plan.generation.sourceMachineId) {
23623
+ throw new Error("MIGRATION_REBIND_SOURCE_MACHINE_MISMATCH");
22397
23624
  }
22398
- grant.state = "streaming";
22399
- let completed = false;
22400
- res.on("close", () => {
22401
- if (!completed && grant.state === "streaming") {
22402
- grant.state = "interrupted";
22403
- }
22404
- });
22405
- try {
22406
- res.statusCode = 200;
22407
- res.setHeader("Content-Type", "application/x-tar");
22408
- res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22409
- res.setHeader("ETag", grant.bundleEtag);
22410
- for await (const chunk of stream) {
22411
- if (!res.write(chunk)) {
22412
- await onceDrain(res);
22413
- }
22414
- }
22415
- completed = true;
22416
- grant.state = "consumed";
22417
- res.end();
22418
- } catch {
22419
- if (!completed && grant.state === "streaming") {
22420
- grant.state = "interrupted";
22421
- }
22422
- if (!res.headersSent) {
22423
- sendJson(res, 500, { code: "migration_bundle_stream_failed" });
22424
- } else {
22425
- res.destroy();
22426
- }
23625
+ if (result.targetMachineId && result.targetMachineId !== plan.generation.targetMachineId) {
23626
+ throw new Error("MIGRATION_REBIND_TARGET_MACHINE_MISMATCH");
22427
23627
  }
23628
+ return result.migrationGeneration ?? expectedGeneration;
22428
23629
  }
22429
- function defaultBundleStream(grant) {
22430
- void grant;
22431
- throw new MigrationBundleStreamError("migration_bundle_stream_not_wired");
22432
- }
22433
- function extractBearerToken(req) {
22434
- const directHeader = singleHeader(req.headers["x-raft-migration-token"]);
22435
- if (directHeader) return directHeader;
22436
- const authorization = singleHeader(req.headers.authorization);
22437
- if (!authorization) return null;
22438
- const match = /^Bearer\s+(.+)$/i.exec(authorization);
22439
- return match?.[1] ?? null;
23630
+ async function verifyManifestFileEntry(sourceWorkspacePath, entry) {
23631
+ if (entry.source !== "workspace" || !entry.workspaceRelativePath) {
23632
+ throw new Error("MIGRATION_MANIFEST_ENTRY_NOT_WORKSPACE");
23633
+ }
23634
+ const relative = normalizeWorkspaceRelativePath2(entry.workspaceRelativePath);
23635
+ const entryPath = path21.join(sourceWorkspacePath, relative);
23636
+ const info = await lstat4(entryPath).catch(() => null);
23637
+ if (!info) throw new Error("MIGRATION_MANIFEST_FILE_MISSING");
23638
+ if (entry.kind === "symlink") {
23639
+ if (!info.isSymbolicLink()) throw new Error("MIGRATION_MANIFEST_KIND_MISMATCH");
23640
+ const linkTarget = await readlink3(entryPath);
23641
+ if (entry.linkTarget !== linkTarget) throw new Error("MIGRATION_MANIFEST_LINK_MISMATCH");
23642
+ return;
23643
+ }
23644
+ if (!info.isFile()) throw new Error("MIGRATION_MANIFEST_KIND_MISMATCH");
23645
+ if (entry.sizeBytes !== void 0 && entry.sizeBytes !== info.size) {
23646
+ throw new Error("MIGRATION_MANIFEST_FILE_SIZE_MISMATCH");
23647
+ }
23648
+ if (entry.sha256) {
23649
+ const actual = await sha256File2(entryPath);
23650
+ if (actual !== entry.sha256) throw new Error("MIGRATION_MANIFEST_FILE_SHA_MISMATCH");
23651
+ }
22440
23652
  }
22441
- function singleHeader(value) {
22442
- if (!value) return null;
22443
- return Array.isArray(value) ? value[0] ?? null : value;
23653
+ function normalizeWorkspaceRelativePath2(relativePath) {
23654
+ const normalized = path21.posix.normalize(relativePath.replaceAll("\\", "/"));
23655
+ if (normalized === "." || normalized.startsWith("../") || normalized === ".." || path21.isAbsolute(normalized)) {
23656
+ throw new Error("MIGRATION_MANIFEST_UNSAFE_PATH");
23657
+ }
23658
+ return normalized;
22444
23659
  }
22445
- function sendJson(res, status, body, headers = {}) {
22446
- const payload = JSON.stringify(body);
22447
- res.writeHead(status, {
22448
- "Content-Type": "application/json",
22449
- "Content-Length": Buffer.byteLength(payload).toString(),
22450
- ...headers
22451
- });
22452
- res.end(payload);
23660
+ async function assertFinalWorkspaceAvailable(sourceWorkspacePath, finalWorkspacePath) {
23661
+ if (path21.resolve(sourceWorkspacePath) === path21.resolve(finalWorkspacePath)) return;
23662
+ try {
23663
+ await access2(finalWorkspacePath);
23664
+ throw new Error("MIGRATION_WORKSPACE_ALREADY_EXISTS");
23665
+ } catch (err) {
23666
+ if (err instanceof Error && err.message === "MIGRATION_WORKSPACE_ALREADY_EXISTS") throw err;
23667
+ }
22453
23668
  }
22454
- function hashToken(token) {
22455
- return sha256Buffer(Buffer.from(token, "utf8"));
23669
+ async function placeWorkspace(sourceWorkspacePath, finalWorkspacePath) {
23670
+ if (path21.resolve(sourceWorkspacePath) === path21.resolve(finalWorkspacePath)) return;
23671
+ const stagingPath = `${finalWorkspacePath}.migration-${process.pid}-${Date.now()}`;
23672
+ await mkdir4(path21.dirname(finalWorkspacePath), { recursive: true });
23673
+ await cp(sourceWorkspacePath, stagingPath, { recursive: true, dereference: false, errorOnExist: true, force: false });
23674
+ await rename(stagingPath, finalWorkspacePath);
22456
23675
  }
22457
- function verifyTokenHash(token, expectedHashHex) {
22458
- const actual = Buffer.from(hashToken(token), "hex");
22459
- const expected = Buffer.from(expectedHashHex, "hex");
22460
- if (actual.byteLength !== expected.byteLength) return false;
22461
- return timingSafeEqual(actual, expected);
23676
+ async function writeArrivalReport(reportPath, report) {
23677
+ const payload = `${canonicalJson3(report)}
23678
+ `;
23679
+ await mkdir4(path21.dirname(reportPath), { recursive: true });
23680
+ await writeFile4(reportPath, payload, { mode: 384 });
23681
+ return sha256Buffer3(Buffer.from(payload, "utf8"));
22462
23682
  }
22463
- function sha256Buffer(buffer) {
22464
- return createHash8("sha256").update(buffer).digest("hex");
23683
+ async function sha256File2(filePath) {
23684
+ const hash = createHash10("sha256");
23685
+ const stream = createReadStream2(filePath);
23686
+ for await (const chunk of stream) {
23687
+ hash.update(chunk);
23688
+ }
23689
+ return hash.digest("hex");
22465
23690
  }
22466
- function canonicalJsonBuffer(value) {
22467
- return Buffer.from(canonicalJson(value), "utf8");
23691
+ function sha256Buffer3(buffer) {
23692
+ return createHash10("sha256").update(buffer).digest("hex");
22468
23693
  }
22469
- function canonicalJson(value) {
22470
- return JSON.stringify(sortJsonValue(value));
23694
+ function canonicalJson3(value) {
23695
+ return JSON.stringify(sortJsonValue3(value));
22471
23696
  }
22472
- function sortJsonValue(value) {
22473
- if (Array.isArray(value)) return value.map(sortJsonValue);
23697
+ function sortJsonValue3(value) {
23698
+ if (Array.isArray(value)) return value.map(sortJsonValue3);
22474
23699
  if (!value || typeof value !== "object") return value;
22475
23700
  return Object.keys(value).sort().reduce((result, key) => {
22476
- result[key] = sortJsonValue(value[key]);
23701
+ result[key] = sortJsonValue3(value[key]);
22477
23702
  return result;
22478
23703
  }, {});
22479
23704
  }
22480
- function onceDrain(res) {
22481
- return new Promise((resolve) => res.once("drain", resolve));
23705
+ function sanitizePathSegment2(value) {
23706
+ return value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 128) || "migration";
23707
+ }
23708
+
23709
+ // src/secretFile.ts
23710
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
23711
+ import path22 from "path";
23712
+ function readSecretFileSync(filePath) {
23713
+ return readFileSync10(filePath, "utf8").trim();
22482
23714
  }
22483
23715
 
22484
23716
  // src/core.ts
@@ -22486,6 +23718,8 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
22486
23718
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
22487
23719
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
22488
23720
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
23721
+ var MIGRATION_OBJECT_STORE_DOWNLOAD_RETRY_INITIAL_MS = 25;
23722
+ var MIGRATION_OBJECT_STORE_DOWNLOAD_RETRY_MAX_MS = 250;
22489
23723
  var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
22490
23724
  "daemon.lifecycle.start": {
22491
23725
  spanAttrs: ["machine_dir_present", "local_trace_enabled"],
@@ -22607,7 +23841,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
22607
23841
  spanAttrs: ["agentId", "event_kind", "runtime"]
22608
23842
  }
22609
23843
  };
22610
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
23844
+ var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key-file <path>";
22611
23845
  var RunnerCredentialMintError2 = class extends Error {
22612
23846
  code;
22613
23847
  retryable;
@@ -22640,20 +23874,50 @@ function runnerCredentialErrorDetail2(error) {
22640
23874
  retryable: true
22641
23875
  };
22642
23876
  }
23877
+ async function waitForAmbientBackoff(delayMs) {
23878
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
23879
+ }
22643
23880
  async function waitForRunnerCredentialRetry2() {
22644
- await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
23881
+ await waitForAmbientBackoff(RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2);
23882
+ }
23883
+ function isRetryableMigrationObjectStoreDownloadStatus(status) {
23884
+ return status === 404 || status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
23885
+ }
23886
+ function migrationTransferFailureMessage(err) {
23887
+ const message = err instanceof Error ? err.message : String(err);
23888
+ return message.slice(0, 500);
23889
+ }
23890
+ async function migrationStepErrorSuffix(response) {
23891
+ try {
23892
+ const body = await response.clone().json();
23893
+ if (typeof body.code === "string" && body.code.length > 0) return body.code.slice(0, 120);
23894
+ if (typeof body.error === "string" && body.error.length > 0) return body.error.slice(0, 120);
23895
+ } catch {
23896
+ }
23897
+ return response.statusText || "unknown";
22645
23898
  }
22646
23899
  function parseDaemonCliArgs(args) {
22647
23900
  let serverUrl = "";
22648
23901
  let apiKey = "";
23902
+ let apiKeyFile = process.env.SLOCK_DAEMON_API_KEY_FILE ?? "";
22649
23903
  for (let i = 0; i < args.length; i++) {
22650
23904
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
22651
23905
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
23906
+ if (args[i] === "--api-key-file" && args[i + 1]) apiKeyFile = args[++i];
23907
+ }
23908
+ if (!apiKey && apiKeyFile) {
23909
+ try {
23910
+ apiKey = readSecretFileSync(apiKeyFile);
23911
+ } catch {
23912
+ apiKey = "";
23913
+ }
22652
23914
  }
22653
23915
  if (!serverUrl || !apiKey) return null;
22654
23916
  return { serverUrl, apiKey };
22655
23917
  }
22656
23918
  function readDaemonVersion(moduleUrl = import.meta.url) {
23919
+ const baked = process.env.RAFT_DAEMON_VERSION;
23920
+ if (typeof baked === "string" && baked.length > 0) return baked;
22657
23921
  try {
22658
23922
  const require2 = createRequire3(moduleUrl);
22659
23923
  return require2("../package.json").version;
@@ -22662,13 +23926,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
22662
23926
  }
22663
23927
  }
22664
23928
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
22665
- const thisDir = path20.dirname(fileURLToPath(moduleUrl));
22666
- const bundledDistPath = path20.resolve(thisDir, "cli", "index.js");
23929
+ const thisDir = path23.dirname(fileURLToPath(moduleUrl));
23930
+ const bundledDistPath = path23.resolve(thisDir, "cli", "index.js");
22667
23931
  try {
22668
23932
  accessSync(bundledDistPath);
22669
23933
  return bundledDistPath;
22670
23934
  } catch {
22671
- const workspaceDistPath = path20.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
23935
+ const workspaceDistPath = path23.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
22672
23936
  accessSync(workspaceDistPath);
22673
23937
  return workspaceDistPath;
22674
23938
  }
@@ -22682,7 +23946,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
22682
23946
  }
22683
23947
  async function runBundledSlockCli(argv) {
22684
23948
  process.argv = [process.execPath, "slock", ...argv];
22685
- await import("./dist-YOKZZD4F.js");
23949
+ await import("./dist-CRXPFW5L.js");
22686
23950
  }
22687
23951
  function detectRuntimes(tracer = noopTracer) {
22688
23952
  const ids = [];
@@ -22774,6 +24038,11 @@ function readPositiveIntegerEnv3(name, fallback) {
22774
24038
  if (!Number.isFinite(parsed) || parsed < 1) return fallback;
22775
24039
  return Math.floor(parsed);
22776
24040
  }
24041
+ function hasAgentMigrationHttpTransportListenConfig(env = process.env) {
24042
+ return Boolean(
24043
+ env[AGENT_MIGRATION_TRANSPORT_HOST_ENV]?.trim() || env[AGENT_MIGRATION_TRANSPORT_PORT_ENV]?.trim() || env[AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV]?.trim()
24044
+ );
24045
+ }
22777
24046
  function formatChannelTarget(msg) {
22778
24047
  return msg.message.channel_type === "dm" ? `dm:@${msg.message.channel_name}` : `#${msg.message.channel_name}`;
22779
24048
  }
@@ -22809,6 +24078,8 @@ function summarizeIncomingMessage(msg) {
22809
24078
  return `(directory=${msg.directoryName})`;
22810
24079
  case "machine:runtime_models:detect":
22811
24080
  return `(runtime=${msg.runtime}, req=${msg.requestId})`;
24081
+ case "machine:migration_transport:lease":
24082
+ return `(agent=${msg.agentId}, migration=${msg.migrationId}, session=${msg.sessionId}, provider=${msg.provider}, role=${msg.role}, kind=${msg.transferKind}, url=${msg.url ? "set" : "missing"})`;
22812
24083
  case "reminder.upsert":
22813
24084
  return `(agent=${msg.agentId}, id=${msg.reminder.reminderId}, v${msg.reminder.version}, fireAt=${msg.reminder.fireAt})`;
22814
24085
  case "reminder.cancel":
@@ -22838,6 +24109,12 @@ var DaemonCore = class {
22838
24109
  agentManager;
22839
24110
  connection;
22840
24111
  reminderCache;
24112
+ migrationTransport;
24113
+ migrationTransportListenPromise = null;
24114
+ migrationTransportListening = false;
24115
+ migrationTransportUrl = null;
24116
+ migrationTransferLease = null;
24117
+ migrationTransferRuns = /* @__PURE__ */ new Set();
22841
24118
  tracer;
22842
24119
  injectedTracer;
22843
24120
  machineLock = null;
@@ -22861,6 +24138,7 @@ var DaemonCore = class {
22861
24138
  clock: options.reminderClock,
22862
24139
  onFire: (job) => this.onReminderFire(job)
22863
24140
  });
24141
+ this.migrationTransport = options.migrationTransport === void 0 ? hasAgentMigrationHttpTransportListenConfig() ? createAgentMigrationHttpTransport() : null : options.migrationTransport;
22864
24142
  let connection;
22865
24143
  this.agentsDataDir = options.dataDir ?? resolveSlockHomePath("agents", this.slockHome);
22866
24144
  const traceUploadDisabled = process.env.SLOCK_DAEMON_TRACE_UPLOAD_DISABLED === "1";
@@ -22891,7 +24169,7 @@ var DaemonCore = class {
22891
24169
  }
22892
24170
  resolveMachineStateRoot() {
22893
24171
  if (this.options.machineStateDir) return this.options.machineStateDir;
22894
- if (this.options.dataDir) return path20.join(path20.dirname(this.options.dataDir), "machines");
24172
+ if (this.options.dataDir) return path23.join(path23.dirname(this.options.dataDir), "machines");
22895
24173
  return resolveDefaultMachineStateRoot();
22896
24174
  }
22897
24175
  shouldEnableLocalTrace() {
@@ -22919,7 +24197,7 @@ var DaemonCore = class {
22919
24197
  sinks: [this.localTraceSink]
22920
24198
  }));
22921
24199
  this.agentManager.setTracer(this.tracer);
22922
- this.agentManager.setCliTransportTraceDir(path20.join(machineDir, "traces"));
24200
+ this.agentManager.setCliTransportTraceDir(path23.join(machineDir, "traces"));
22923
24201
  }
22924
24202
  installTraceBundleUploader(machineDir) {
22925
24203
  if (!this.shouldEnableLocalTrace()) return;
@@ -22969,9 +24247,13 @@ var DaemonCore = class {
22969
24247
  span.addEvent("daemon.machine_lock.acquired", { machine_dir_present: true });
22970
24248
  span.end("ok");
22971
24249
  }
24250
+ if (this.migrationTransport) {
24251
+ this.startMigrationTransport();
24252
+ }
22972
24253
  try {
22973
24254
  this.connection.connect();
22974
24255
  } catch (err) {
24256
+ void this.stopMigrationTransport();
22975
24257
  this.traceBundleUploader?.stop();
22976
24258
  this.traceBundleUploader = null;
22977
24259
  this.machineLock.release();
@@ -23010,6 +24292,7 @@ var DaemonCore = class {
23010
24292
  shutdown_reason: shutdownReason
23011
24293
  });
23012
24294
  }
24295
+ await this.stopMigrationTransport();
23013
24296
  this.connection.disconnect();
23014
24297
  span.addEvent("daemon.connection.disconnect_requested");
23015
24298
  this.machineLock?.release();
@@ -23035,6 +24318,432 @@ var DaemonCore = class {
23035
24318
  });
23036
24319
  span.end(status);
23037
24320
  }
24321
+ getMigrationTransportReady() {
24322
+ const transferLease = this.getActiveMigrationTransferLease();
24323
+ if (transferLease) {
24324
+ return {
24325
+ provisioned: true,
24326
+ endpoint: transferLease.url,
24327
+ leaseSource: "server",
24328
+ provider: transferLease.provider,
24329
+ role: transferLease.role,
24330
+ transferKind: transferLease.transferKind,
24331
+ url: transferLease.url,
24332
+ expiresAt: transferLease.expiresAt,
24333
+ maxBytes: transferLease.maxBytes,
24334
+ observedAt: currentDate().toISOString()
24335
+ };
24336
+ }
24337
+ const endpoint = this.migrationTransportListening ? this.migrationTransportUrl : null;
24338
+ return {
24339
+ provisioned: Boolean(endpoint),
24340
+ endpoint,
24341
+ leaseSource: endpoint ? "env" : null,
24342
+ observedAt: currentDate().toISOString()
24343
+ };
24344
+ }
24345
+ startMigrationTransport() {
24346
+ if (!this.migrationTransport || this.migrationTransportListenPromise) return;
24347
+ this.migrationTransportListenPromise = this.migrationTransport.listen().then(({ url }) => {
24348
+ this.migrationTransportListening = true;
24349
+ this.migrationTransportUrl = url;
24350
+ logger.info(`[Slock Daemon] Agent migration HTTP transport listening: ${url}`);
24351
+ this.recordDaemonTrace("daemon.migration_transport.listen", {
24352
+ outcome: "listening",
24353
+ url_present: Boolean(url)
24354
+ });
24355
+ }).catch((err) => {
24356
+ this.migrationTransportListening = false;
24357
+ this.migrationTransportUrl = null;
24358
+ logger.error("[Slock Daemon] Agent migration HTTP transport failed to listen", err);
24359
+ this.recordDaemonTrace("daemon.migration_transport.listen", {
24360
+ outcome: "failed",
24361
+ error_class: err instanceof Error ? err.name : typeof err
24362
+ }, "error");
24363
+ });
24364
+ }
24365
+ async stopMigrationTransport() {
24366
+ if (!this.migrationTransport) return;
24367
+ const listenPromise = this.migrationTransportListenPromise;
24368
+ this.migrationTransportListenPromise = null;
24369
+ if (listenPromise) await listenPromise;
24370
+ if (!this.migrationTransportListening) return;
24371
+ try {
24372
+ await this.migrationTransport.close();
24373
+ this.migrationTransportUrl = null;
24374
+ logger.info("[Slock Daemon] Agent migration HTTP transport stopped");
24375
+ this.recordDaemonTrace("daemon.migration_transport.stop", { outcome: "stopped" });
24376
+ } catch (err) {
24377
+ logger.error("[Slock Daemon] Agent migration HTTP transport failed to stop", err);
24378
+ this.recordDaemonTrace("daemon.migration_transport.stop", {
24379
+ outcome: "failed",
24380
+ error_class: err instanceof Error ? err.name : typeof err
24381
+ }, "error");
24382
+ } finally {
24383
+ this.migrationTransportListening = false;
24384
+ this.migrationTransportUrl = null;
24385
+ }
24386
+ }
24387
+ getActiveMigrationTransferLease() {
24388
+ const lease = this.migrationTransferLease;
24389
+ if (!lease) return null;
24390
+ const expiresAtMs = Date.parse(lease.expiresAt);
24391
+ if (!Number.isFinite(expiresAtMs) || expiresAtMs <= currentTimeMs()) return null;
24392
+ return lease;
24393
+ }
24394
+ validateMigrationTransferLease(lease) {
24395
+ if (!lease.agentId.trim()) throw new Error("migration transfer lease agent id is required");
24396
+ if (!lease.migrationId.trim()) throw new Error("migration transfer lease migration id is required");
24397
+ if (!lease.migrationGeneration.trim()) throw new Error("migration transfer lease migrationGeneration is required");
24398
+ if (!lease.sessionId.trim()) throw new Error("migration transfer lease session id is required");
24399
+ if (lease.provider !== "object_store") throw new Error(`unsupported migration transfer provider: ${lease.provider}`);
24400
+ if (lease.leaseSource !== "server") throw new Error(`unsupported migration transfer lease source: ${lease.leaseSource}`);
24401
+ if (lease.role !== "source" && lease.role !== "target") throw new Error(`unsupported migration transfer role: ${lease.role}`);
24402
+ if (lease.transferKind !== "upload" && lease.transferKind !== "download") {
24403
+ throw new Error(`unsupported migration transfer kind: ${lease.transferKind}`);
24404
+ }
24405
+ if (!lease.url.trim()) throw new Error("migration transfer lease url is required");
24406
+ if (!lease.bearerToken.trim()) throw new Error("migration transfer lease bearer token is required");
24407
+ const expiresAtMs = Date.parse(lease.expiresAt);
24408
+ if (!lease.expiresAt.trim() || !Number.isFinite(expiresAtMs) || expiresAtMs <= currentTimeMs()) {
24409
+ throw new Error("migration transfer lease expiresAt must be a future timestamp");
24410
+ }
24411
+ if (!Number.isInteger(lease.maxBytes) || lease.maxBytes <= 0) {
24412
+ throw new Error("migration transfer lease maxBytes must be a positive integer");
24413
+ }
24414
+ }
24415
+ handleMigrationTransportLease(lease) {
24416
+ try {
24417
+ this.applyMigrationTransferLease(lease);
24418
+ } catch (err) {
24419
+ logger.error("[Slock Daemon] Failed to apply migration transport lease", err);
24420
+ this.recordDaemonTrace("daemon.migration_transport.lease", {
24421
+ outcome: "failed",
24422
+ lease_present: true,
24423
+ error_class: err instanceof Error ? err.name : typeof err
24424
+ }, "error");
24425
+ }
24426
+ }
24427
+ applyMigrationTransferLease(lease) {
24428
+ this.validateMigrationTransferLease(lease);
24429
+ if (this.migrationTransferLease?.agentId === lease.agentId && this.migrationTransferLease?.migrationId === lease.migrationId && this.migrationTransferLease?.migrationGeneration === lease.migrationGeneration && this.migrationTransferLease?.sessionId === lease.sessionId && this.migrationTransferLease?.role === lease.role) {
24430
+ this.migrationTransferLease = lease;
24431
+ this.emitReadyIfConnected();
24432
+ this.recordDaemonTrace("daemon.migration_transport.lease", {
24433
+ outcome: "unchanged",
24434
+ agent_id_present: true,
24435
+ migration_id_present: true,
24436
+ session_id_present: true,
24437
+ migration_generation: lease.migrationGeneration,
24438
+ provider: lease.provider,
24439
+ role: lease.role,
24440
+ transfer_kind: lease.transferKind
24441
+ });
24442
+ this.startMigrationTransferRun(lease);
24443
+ return;
24444
+ }
24445
+ this.migrationTransferLease = lease;
24446
+ this.emitReadyIfConnected();
24447
+ this.recordDaemonTrace("daemon.migration_transport.lease", {
24448
+ outcome: "applied",
24449
+ agent_id_present: true,
24450
+ migration_id_present: true,
24451
+ session_id_present: true,
24452
+ migration_generation: lease.migrationGeneration,
24453
+ provider: lease.provider,
24454
+ role: lease.role,
24455
+ transfer_kind: lease.transferKind,
24456
+ url_present: Boolean(lease.url),
24457
+ bearer_token_present: Boolean(lease.bearerToken)
24458
+ });
24459
+ this.startMigrationTransferRun(lease);
24460
+ }
24461
+ startMigrationTransferRun(lease) {
24462
+ const key = [
24463
+ lease.agentId,
24464
+ lease.migrationId,
24465
+ lease.migrationGeneration,
24466
+ lease.sessionId,
24467
+ lease.role
24468
+ ].join(":");
24469
+ if (this.migrationTransferRuns.has(key)) return;
24470
+ this.migrationTransferRuns.add(key);
24471
+ void this.runMigrationTransferLease(lease).catch(async (err) => {
24472
+ logger.error("[Slock Daemon] Migration object-store transfer failed", err);
24473
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24474
+ outcome: "failed",
24475
+ role: lease.role,
24476
+ transfer_kind: lease.transferKind,
24477
+ agent_id_present: Boolean(lease.agentId),
24478
+ migration_id_present: Boolean(lease.migrationId),
24479
+ session_id_present: Boolean(lease.sessionId),
24480
+ error_class: err instanceof Error ? err.name : typeof err
24481
+ }, "error");
24482
+ try {
24483
+ await this.reportMigrationTransportLost(lease, err);
24484
+ } catch (reportErr) {
24485
+ logger.error("[Slock Daemon] Failed to report migration transport loss", reportErr);
24486
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24487
+ outcome: "transport_lost_report_failed",
24488
+ role: lease.role,
24489
+ transfer_kind: lease.transferKind,
24490
+ migration_id_present: Boolean(lease.migrationId),
24491
+ error_class: reportErr instanceof Error ? reportErr.name : typeof reportErr
24492
+ }, "error");
24493
+ }
24494
+ }).finally(() => {
24495
+ this.migrationTransferRuns.delete(key);
24496
+ });
24497
+ }
24498
+ async runMigrationTransferLease(lease) {
24499
+ if (lease.provider !== "object_store") return;
24500
+ if (lease.role === "source") {
24501
+ await this.uploadMigrationObjectStoreBundle(lease);
24502
+ return;
24503
+ }
24504
+ await this.downloadAndImportMigrationObjectStoreBundle(lease);
24505
+ }
24506
+ migrationObjectStoreHeaders(lease) {
24507
+ return {
24508
+ "X-Raft-Migration-Token": lease.bearerToken
24509
+ };
24510
+ }
24511
+ async uploadMigrationObjectStoreBundle(lease) {
24512
+ if (lease.transferKind !== "upload") {
24513
+ throw new Error("MIGRATION_OBJECT_STORE_SOURCE_KIND_MISMATCH");
24514
+ }
24515
+ const built = await buildAgentMigrationObjectStoreBundle({
24516
+ agentId: lease.agentId,
24517
+ slockHome: this.slockHome,
24518
+ workspacePath: path23.join(this.agentsDataDir, lease.agentId),
24519
+ maxBytes: lease.maxBytes
24520
+ });
24521
+ const response = await daemonFetch(lease.url, {
24522
+ method: "PUT",
24523
+ headers: {
24524
+ ...this.migrationObjectStoreHeaders(lease),
24525
+ "Content-Type": AGENT_MIGRATION_OBJECT_STORE_CONTENT_TYPE
24526
+ },
24527
+ body: new Uint8Array(built.bundle)
24528
+ });
24529
+ if (!response.ok) {
24530
+ throw new Error(`MIGRATION_OBJECT_STORE_UPLOAD_FAILED:${response.status}`);
24531
+ }
24532
+ await this.reportMigrationSourceReady(lease, built.manifestSha256);
24533
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24534
+ outcome: "uploaded",
24535
+ role: lease.role,
24536
+ transfer_kind: lease.transferKind,
24537
+ bundle_bytes: built.bundle.byteLength,
24538
+ max_bytes: lease.maxBytes
24539
+ });
24540
+ }
24541
+ async downloadAndImportMigrationObjectStoreBundle(lease) {
24542
+ if (lease.transferKind !== "download") {
24543
+ throw new Error("MIGRATION_OBJECT_STORE_TARGET_KIND_MISMATCH");
24544
+ }
24545
+ const bundle = await this.downloadMigrationObjectStoreBundleWithRetry(lease);
24546
+ if (bundle.byteLength > lease.maxBytes) {
24547
+ throw new Error("MIGRATION_OBJECT_STORE_BUNDLE_TOO_LARGE");
24548
+ }
24549
+ const staged = await stageAgentMigrationObjectStoreBundle({
24550
+ bundle,
24551
+ slockHome: this.slockHome,
24552
+ sessionId: lease.sessionId
24553
+ });
24554
+ const targetImport = await this.fetchMigrationTargetImportView(lease.migrationId);
24555
+ if (targetImport.agentId !== lease.agentId) {
24556
+ throw new Error("MIGRATION_TARGET_IMPORT_AGENT_MISMATCH");
24557
+ }
24558
+ if (targetImport.manifestSha256 && targetImport.manifestSha256 !== staged.manifestSha256) {
24559
+ throw new Error("MIGRATION_TARGET_IMPORT_MANIFEST_SHA_MISMATCH");
24560
+ }
24561
+ const plan = await buildAgentMigrationAdoptPlan({
24562
+ sourceKind: "staged_bundle",
24563
+ slockHome: this.slockHome,
24564
+ stagingWorkspacePath: staged.stagingWorkspacePath,
24565
+ finalWorkspacePath: path23.join(this.agentsDataDir, lease.agentId),
24566
+ manifest: staged.manifest,
24567
+ manifestSha256: staged.manifestSha256,
24568
+ generation: {
24569
+ grantKey: targetImport.grantKey,
24570
+ migrationGeneration: targetImport.migrationGeneration,
24571
+ sourceMachineId: targetImport.sourceMachineId,
24572
+ targetMachineId: targetImport.targetMachineId,
24573
+ localMachineId: targetImport.targetMachineId
24574
+ }
24575
+ });
24576
+ await executeAgentMigrationAdoptPlan(plan, this.migrationRebindClient(targetImport.grantKey));
24577
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24578
+ outcome: "imported",
24579
+ role: lease.role,
24580
+ transfer_kind: lease.transferKind,
24581
+ bundle_bytes: bundle.byteLength,
24582
+ manifest_sha_present: true
24583
+ });
24584
+ }
24585
+ async downloadMigrationObjectStoreBundleWithRetry(lease) {
24586
+ const expiresAtMs = Date.parse(lease.expiresAt);
24587
+ let attempt = 0;
24588
+ let delayMs = MIGRATION_OBJECT_STORE_DOWNLOAD_RETRY_INITIAL_MS;
24589
+ let lastStatus = null;
24590
+ let lastErrorClass = null;
24591
+ while (currentTimeMs() < expiresAtMs) {
24592
+ attempt += 1;
24593
+ try {
24594
+ const response = await daemonFetch(lease.url, {
24595
+ method: "GET",
24596
+ headers: this.migrationObjectStoreHeaders(lease)
24597
+ });
24598
+ if (response.ok) {
24599
+ return Buffer.from(await response.arrayBuffer());
24600
+ }
24601
+ lastStatus = response.status;
24602
+ lastErrorClass = null;
24603
+ if (!isRetryableMigrationObjectStoreDownloadStatus(response.status)) {
24604
+ throw new Error(`MIGRATION_OBJECT_STORE_DOWNLOAD_FAILED:${response.status}`);
24605
+ }
24606
+ } catch (err) {
24607
+ if (err instanceof Error && err.message.startsWith("MIGRATION_OBJECT_STORE_DOWNLOAD_FAILED:")) {
24608
+ throw err;
24609
+ }
24610
+ lastErrorClass = err instanceof Error ? err.name : typeof err;
24611
+ }
24612
+ const remainingMs = expiresAtMs - currentTimeMs();
24613
+ if (remainingMs <= 0) break;
24614
+ const sleepMs = Math.min(delayMs, remainingMs);
24615
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24616
+ outcome: "download_retry",
24617
+ role: lease.role,
24618
+ transfer_kind: lease.transferKind,
24619
+ attempt,
24620
+ status: lastStatus,
24621
+ error_class: lastErrorClass,
24622
+ retry_delay_ms: sleepMs
24623
+ });
24624
+ await waitForAmbientBackoff(sleepMs);
24625
+ delayMs = Math.min(delayMs * 2, MIGRATION_OBJECT_STORE_DOWNLOAD_RETRY_MAX_MS);
24626
+ }
24627
+ const suffix = lastStatus !== null ? String(lastStatus) : lastErrorClass ?? "unknown";
24628
+ throw new Error(`MIGRATION_OBJECT_STORE_DOWNLOAD_RETRY_EXHAUSTED:${suffix}`);
24629
+ }
24630
+ async reportMigrationTransportLost(lease, err) {
24631
+ const url = new URL(`/internal/computer/agent-migrations/by-id/${encodeURIComponent(lease.migrationId)}/transport-lost`, this.options.serverUrl);
24632
+ const response = await daemonFetch(url, {
24633
+ method: "POST",
24634
+ headers: {
24635
+ ...this.internalComputerHeaders(),
24636
+ "Content-Type": "application/json"
24637
+ },
24638
+ body: JSON.stringify({
24639
+ role: lease.role,
24640
+ transferKind: lease.transferKind,
24641
+ message: migrationTransferFailureMessage(err)
24642
+ })
24643
+ });
24644
+ if (!response.ok) {
24645
+ throw new Error(`MIGRATION_TRANSPORT_LOST_REPORT_FAILED:${response.status}`);
24646
+ }
24647
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24648
+ outcome: "transport_lost_reported",
24649
+ role: lease.role,
24650
+ transfer_kind: lease.transferKind,
24651
+ migration_id_present: Boolean(lease.migrationId)
24652
+ });
24653
+ }
24654
+ async reportMigrationSourceReady(lease, manifestSha256) {
24655
+ const url = new URL(`/internal/computer/agent-migrations/by-id/${encodeURIComponent(lease.migrationId)}/source-ready`, this.options.serverUrl);
24656
+ const response = await daemonFetch(url, {
24657
+ method: "POST",
24658
+ headers: {
24659
+ ...this.internalComputerHeaders(),
24660
+ "Content-Type": "application/json"
24661
+ },
24662
+ body: JSON.stringify({
24663
+ manifestPath: `object-store:${lease.sessionId}/manifest.json`,
24664
+ manifestSha256
24665
+ })
24666
+ });
24667
+ if (!response.ok) {
24668
+ throw new Error(`MIGRATION_SOURCE_READY_REPORT_FAILED:${response.status}:${await migrationStepErrorSuffix(response)}`);
24669
+ }
24670
+ this.recordDaemonTrace("daemon.migration_transport.object_store", {
24671
+ outcome: "source_ready_reported",
24672
+ role: lease.role,
24673
+ transfer_kind: lease.transferKind,
24674
+ migration_id_present: Boolean(lease.migrationId)
24675
+ });
24676
+ }
24677
+ async fetchMigrationTargetImportView(migrationId) {
24678
+ const url = new URL(`/internal/computer/agent-migrations/by-id/${encodeURIComponent(migrationId)}`, this.options.serverUrl);
24679
+ const response = await daemonFetch(url, {
24680
+ method: "GET",
24681
+ headers: this.internalComputerHeaders()
24682
+ });
24683
+ if (!response.ok) {
24684
+ throw new Error(`MIGRATION_TARGET_IMPORT_LOOKUP_FAILED:${response.status}`);
24685
+ }
24686
+ const body = await response.json();
24687
+ if (!body.migration) throw new Error("MIGRATION_TARGET_IMPORT_VIEW_MISSING");
24688
+ return body.migration;
24689
+ }
24690
+ async fetchMigrationTargetImportViewByGrantKey(grantKey) {
24691
+ const url = new URL(`/internal/computer/agent-migrations/${encodeURIComponent(grantKey)}`, this.options.serverUrl);
24692
+ const response = await daemonFetch(url, {
24693
+ method: "GET",
24694
+ headers: this.internalComputerHeaders()
24695
+ });
24696
+ if (!response.ok) {
24697
+ throw new Error(`MIGRATION_TARGET_IMPORT_LOOKUP_FAILED:${response.status}`);
24698
+ }
24699
+ const body = await response.json();
24700
+ if (!body.migration) throw new Error("MIGRATION_TARGET_IMPORT_VIEW_MISSING");
24701
+ return body.migration;
24702
+ }
24703
+ migrationRebindClient(grantKey) {
24704
+ return {
24705
+ startTransfer: (input) => this.postMigrationTargetImportStep(grantKey, "start-transfer", input),
24706
+ flipMachine: (input) => this.postMigrationTargetImportStep(grantKey, "flip-machine", input),
24707
+ markArrived: (input) => this.postMigrationTargetImportStep(grantKey, "arrived", input)
24708
+ };
24709
+ }
24710
+ async postMigrationTargetImportStep(grantKey, step, body) {
24711
+ return await this.postMigrationTargetImportStepOnce(grantKey, step, body, true);
24712
+ }
24713
+ async postMigrationTargetImportStepOnce(grantKey, step, body, allowStartGenerationRefresh) {
24714
+ const url = new URL(`/internal/computer/agent-migrations/${encodeURIComponent(grantKey)}/${step}`, this.options.serverUrl);
24715
+ const response = await daemonFetch(url, {
24716
+ method: "POST",
24717
+ headers: {
24718
+ ...this.internalComputerHeaders(),
24719
+ "Content-Type": "application/json"
24720
+ },
24721
+ body: JSON.stringify(body)
24722
+ });
24723
+ if (!response.ok) {
24724
+ const suffix = await migrationStepErrorSuffix(response);
24725
+ if (step === "start-transfer" && response.status === 409 && suffix === "migration_generation_stale" && allowStartGenerationRefresh) {
24726
+ const current = await this.fetchMigrationTargetImportViewByGrantKey(grantKey);
24727
+ if (current.state === "in_transit") return current;
24728
+ if (current.state === "ready" && current.migrationGeneration !== body.migrationGeneration) {
24729
+ return await this.postMigrationTargetImportStepOnce(grantKey, step, {
24730
+ ...body,
24731
+ migrationGeneration: current.migrationGeneration
24732
+ }, false);
24733
+ }
24734
+ }
24735
+ throw new Error(`MIGRATION_TARGET_IMPORT_${step.toUpperCase().replace("-", "_")}_FAILED:${response.status}:${suffix}`);
24736
+ }
24737
+ const responseBody = await response.json();
24738
+ if (!responseBody.migration) throw new Error("MIGRATION_TARGET_IMPORT_VIEW_MISSING");
24739
+ return responseBody.migration;
24740
+ }
24741
+ internalComputerHeaders() {
24742
+ return {
24743
+ "Authorization": `Bearer ${this.options.apiKey}`,
24744
+ "X-Slock-Client": "daemon-migration-object-store"
24745
+ };
24746
+ }
23038
24747
  withDaemonTraceScope(tracer) {
23039
24748
  return {
23040
24749
  startSpan: (name, options) => createTraceScopeTracer(tracer, this.daemonTraceScope(), {
@@ -23481,6 +25190,9 @@ var DaemonCore = class {
23481
25190
  });
23482
25191
  break;
23483
25192
  }
25193
+ case "machine:migration_transport:lease":
25194
+ this.handleMigrationTransportLease(msg);
25195
+ break;
23484
25196
  case "reminder.upsert":
23485
25197
  this.reminderCache.upsert(msg.reminder);
23486
25198
  break;
@@ -23534,27 +25246,20 @@ var DaemonCore = class {
23534
25246
  agentId: job.ownerAgentId,
23535
25247
  reminderId: job.reminderId,
23536
25248
  version: job.version,
25249
+ // Accepted ambient clock: reminder telemetry reports the client wall-clock fire attempt.
23537
25250
  firedAtClient: (/* @__PURE__ */ new Date()).toISOString()
23538
25251
  });
23539
25252
  }
23540
- handleConnect() {
25253
+ emitReadyIfConnected() {
25254
+ if (this.connection.connected) this.emitReady();
25255
+ }
25256
+ emitReady() {
23541
25257
  const { ids: runtimes, versions: runtimeVersions, diagnostics: runtimeDiagnostics = {} } = this.runtimeDetector();
23542
25258
  const runtimeInfo = runtimes.map((id) => runtimeVersions[id] ? `${id} (${runtimeVersions[id]})` : id);
23543
25259
  logger.info(`[Daemon] Detected runtimes: ${runtimeInfo.join(", ") || "none"}`);
23544
25260
  for (const [runtime, diagnostic] of Object.entries(runtimeDiagnostics)) {
23545
25261
  logger.warn(`[Daemon] Runtime ${runtime} diagnostic: ${diagnostic}`);
23546
25262
  }
23547
- if (!this.opencliWrappersRegenerated) {
23548
- this.opencliWrappersRegenerated = true;
23549
- try {
23550
- const { scanned, rewritten } = regenerateExistingOpencliWrappers(this.agentsDataDir);
23551
- if (scanned > 0) {
23552
- logger.info(`[Daemon] Refreshed ${rewritten}/${scanned} opencli wrapper(s) to current self-healing form`);
23553
- }
23554
- } catch (err) {
23555
- logger.warn(`[Daemon] opencli wrapper refresh skipped: ${err instanceof Error ? err.message : String(err)}`);
23556
- }
23557
- }
23558
25263
  const runningAgentIds = this.agentManager.getRunningAgentIds();
23559
25264
  const idleAgentSessions = this.agentManager.getIdleAgentSessionIds();
23560
25265
  const runtimeProfileReports = this.agentManager.getAgentRuntimeProfileReports();
@@ -23566,7 +25271,8 @@ var DaemonCore = class {
23566
25271
  hostname: this.options.hostname ?? os8.hostname(),
23567
25272
  os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
23568
25273
  daemonVersion: this.daemonVersion,
23569
- ...this.computerVersion ? { computerVersion: this.computerVersion } : {}
25274
+ ...this.computerVersion ? { computerVersion: this.computerVersion } : {},
25275
+ migrationTransport: this.getMigrationTransportReady()
23570
25276
  });
23571
25277
  this.recordDaemonTrace("daemon.ready.sent", {
23572
25278
  runtimes_count: runtimes.length,
@@ -23574,6 +25280,23 @@ var DaemonCore = class {
23574
25280
  idle_agents_count: idleAgentSessions.length,
23575
25281
  runtime_profile_reports_count: runtimeProfileReports.length
23576
25282
  });
25283
+ }
25284
+ handleConnect() {
25285
+ if (!this.opencliWrappersRegenerated) {
25286
+ this.opencliWrappersRegenerated = true;
25287
+ try {
25288
+ const { scanned, rewritten } = regenerateExistingOpencliWrappers(this.agentsDataDir);
25289
+ if (scanned > 0) {
25290
+ logger.info(`[Daemon] Refreshed ${rewritten}/${scanned} opencli wrapper(s) to current self-healing form`);
25291
+ }
25292
+ } catch (err) {
25293
+ logger.warn(`[Daemon] opencli wrapper refresh skipped: ${err instanceof Error ? err.message : String(err)}`);
25294
+ }
25295
+ }
25296
+ this.emitReady();
25297
+ const runningAgentIds = this.agentManager.getRunningAgentIds();
25298
+ const idleAgentSessions = this.agentManager.getIdleAgentSessionIds();
25299
+ const runtimeProfileReports = this.agentManager.getAgentRuntimeProfileReports();
23577
25300
  if (this.options.onComputerUpgradeReconcile) {
23578
25301
  void Promise.resolve().then(
23579
25302
  () => this.options.onComputerUpgradeReconcile((done) => {
@@ -23651,10 +25374,27 @@ export {
23651
25374
  resolveWorkspaceDirectoryPath,
23652
25375
  scanWorkspaceDirectories,
23653
25376
  deleteWorkspaceDirectory,
23654
- AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
23655
- buildAgentMigrationExportManifest,
25377
+ detectLegacyDaemonSupervisor,
25378
+ detectLegacyDaemonSupervisorWithProbe,
25379
+ legacyDaemonSupervisorFallbackCommands,
25380
+ AGENT_MIGRATION_TRANSPORT_HOST_ENV,
25381
+ AGENT_MIGRATION_TRANSPORT_PORT_ENV,
25382
+ AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV,
25383
+ AGENT_MIGRATION_CONTROL_SEAM_ENV,
23656
25384
  AgentMigrationGrantRegistry,
23657
25385
  createAgentMigrationHttpTransport,
25386
+ resolveAgentMigrationHttpTransportListenOptions,
25387
+ resolveAgentMigrationControlSeamEnabled,
25388
+ AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
25389
+ buildAgentMigrationExportManifest,
25390
+ AGENT_MIGRATION_OBJECT_STORE_BUNDLE_SCHEMA_VERSION,
25391
+ AGENT_MIGRATION_OBJECT_STORE_CONTENT_TYPE,
25392
+ buildAgentMigrationObjectStoreBundle,
25393
+ stageAgentMigrationObjectStoreBundle,
25394
+ buildAgentMigrationAdoptPlan,
25395
+ verifyAgentMigrationAdoptPlan,
25396
+ executeAgentMigrationAdoptPlan,
25397
+ hashAgentMigrationManifest,
23658
25398
  DAEMON_CORE_TRACE_ATTR_CONTRACTS,
23659
25399
  DAEMON_CLI_USAGE,
23660
25400
  parseDaemonCliArgs,