@botiverse/raft-daemon 0.72.2 → 0.72.4-play.20260709142358

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 path22 from "path";
3
3
  import os8 from "os";
4
4
  import { createRequire as createRequire3 } from "module";
5
5
  import { accessSync } from "fs";
@@ -484,7 +484,7 @@ var RUNTIME_PROVIDER_DISPLAY_NAMES = {
484
484
  "openrouter": "OpenRouter",
485
485
  "xai": "xAI",
486
486
  "xiaomi": "Xiaomi MiMo",
487
- "zai": "ZAI",
487
+ "zai": "ZAI Coding Plan (Global)",
488
488
  "zai-coding-cn": "ZAI Coding Plan (China)"
489
489
  };
490
490
 
@@ -2694,6 +2694,51 @@ var agentApiReminderResponseSchema = passthroughObject({
2694
2694
  var agentApiReminderLogResponseSchema = passthroughObject({
2695
2695
  events: z2.array(agentApiReminderEventSummarySchema)
2696
2696
  });
2697
+ var agentApiMigrationStateSchema = z2.enum(["prep", "ready", "in_transit", "arriving", "completed", "aborted", "failed"]);
2698
+ var agentApiMigrationSummarySchema = passthroughObject({
2699
+ id: z2.string(),
2700
+ agentId: z2.string(),
2701
+ sourceMachineId: z2.string(),
2702
+ targetMachineId: z2.string(),
2703
+ state: agentApiMigrationStateSchema,
2704
+ manifestPath: nullableStringSchema,
2705
+ manifestSha256: nullableStringSchema,
2706
+ arrivalReportPath: nullableStringSchema,
2707
+ arrivalReportSha256: nullableStringSchema,
2708
+ abortReason: nullableStringSchema,
2709
+ failureReason: nullableStringSchema,
2710
+ prepDeadlineAt: z2.string().datetime(),
2711
+ transferDeadlineAt: z2.string().datetime(),
2712
+ arrivalDeadlineAt: z2.string().datetime(),
2713
+ readyAt: nullableStringSchema,
2714
+ flippedAt: nullableStringSchema,
2715
+ arrivedAt: nullableStringSchema,
2716
+ completedAt: nullableStringSchema,
2717
+ abortedAt: nullableStringSchema,
2718
+ revision: z2.number().int().positive(),
2719
+ createdAt: z2.string().datetime(),
2720
+ updatedAt: z2.string().datetime()
2721
+ });
2722
+ var agentApiMigrationBeginBodySchema = passthroughObject({
2723
+ targetMachineId: z2.string().trim().min(1),
2724
+ prepDeadlineMs: z2.number().int().positive().optional(),
2725
+ transferDeadlineMs: z2.number().int().positive().optional(),
2726
+ arrivalDeadlineMs: z2.number().int().positive().optional()
2727
+ });
2728
+ var agentApiMigrationReadyBodySchema = passthroughObject({
2729
+ manifestPath: z2.string().trim().min(1),
2730
+ manifestSha256: optionalStringSchema
2731
+ });
2732
+ var agentApiMigrationArrivedBodySchema = passthroughObject({
2733
+ reportPath: optionalStringSchema,
2734
+ reportSha256: optionalStringSchema
2735
+ });
2736
+ var agentApiMigrationResponseSchema = passthroughObject({
2737
+ migration: agentApiMigrationSummarySchema
2738
+ });
2739
+ var agentApiMigrationStatusResponseSchema = passthroughObject({
2740
+ migration: agentApiMigrationSummarySchema.nullable()
2741
+ });
2697
2742
  function route(input) {
2698
2743
  return {
2699
2744
  ...input,
@@ -2941,6 +2986,46 @@ var agentApiContract = {
2941
2986
  request: { body: agentApiTaskUpdateStatusBodySchema },
2942
2987
  response: { body: agentApiTaskUpdateStatusResponseSchema }
2943
2988
  }),
2989
+ migrationBegin: route({
2990
+ key: "migrationBegin",
2991
+ method: "POST",
2992
+ path: "/migrations",
2993
+ client: { resource: "migrations", method: "begin" },
2994
+ capability: "server",
2995
+ description: "Begin a migration for the bound agent credential.",
2996
+ request: { body: agentApiMigrationBeginBodySchema },
2997
+ response: { body: agentApiMigrationResponseSchema }
2998
+ }),
2999
+ migrationStatus: route({
3000
+ key: "migrationStatus",
3001
+ method: "GET",
3002
+ path: "/migrations/current",
3003
+ client: { resource: "migrations", method: "status" },
3004
+ capability: "read",
3005
+ description: "Read the active migration for the bound agent credential, if any.",
3006
+ request: {},
3007
+ response: { body: agentApiMigrationStatusResponseSchema }
3008
+ }),
3009
+ migrationReady: route({
3010
+ key: "migrationReady",
3011
+ method: "POST",
3012
+ path: "/migrations/ready",
3013
+ client: { resource: "migrations", method: "ready" },
3014
+ capability: "read",
3015
+ description: "Mark the bound agent's active migration prep phase ready.",
3016
+ request: { body: agentApiMigrationReadyBodySchema },
3017
+ response: { body: agentApiMigrationResponseSchema }
3018
+ }),
3019
+ migrationArrived: route({
3020
+ key: "migrationArrived",
3021
+ method: "POST",
3022
+ path: "/migrations/arrived",
3023
+ client: { resource: "migrations", method: "arrived" },
3024
+ capability: "read",
3025
+ description: "Mark the bound agent's active migration arrival phase complete.",
3026
+ request: { body: agentApiMigrationArrivedBodySchema },
3027
+ response: { body: agentApiMigrationResponseSchema }
3028
+ }),
2944
3029
  reminderList: route({
2945
3030
  key: "reminderList",
2946
3031
  method: "GET",
@@ -3248,6 +3333,14 @@ var AUTH_REFRESH_ROTATION_RETRY_RTT_BUDGET_MS = 1e3;
3248
3333
  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;
3249
3334
  var AUTH_REFRESH_ROTATION_REPLAY_GRACE_MARGIN_MS = AUTH_REFRESH_ROTATED_REPLAY_GRACE_MS - AUTH_REFRESH_ROTATION_LOSER_WORST_CHAIN_MS;
3250
3335
 
3336
+ // ../shared/src/clock.ts
3337
+ function currentTimeMs() {
3338
+ return Date.now();
3339
+ }
3340
+ function currentDate() {
3341
+ return new Date(currentTimeMs());
3342
+ }
3343
+
3251
3344
  // ../shared/src/agentInbox.ts
3252
3345
  function formatAgentInboxDelta(rows, options = {}) {
3253
3346
  const totalPendingMessages = options.totalPendingMessages;
@@ -3630,7 +3723,7 @@ var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
3630
3723
  var RUNTIMES = [
3631
3724
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
3632
3725
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
3633
- { id: "builtin", displayName: "Builtin Pi", binary: "", supported: true },
3726
+ { id: "builtin", displayName: "Built-in Pi", binary: "", supported: true },
3634
3727
  { id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
3635
3728
  // Kimi: prefer the in-process SDK (`kimi-sdk` → "Kimi Code") for new agents.
3636
3729
  // The legacy `kimi` (kimi-cli child-process) entry stays for backward compat
@@ -4328,8 +4421,8 @@ async function executeResponseRequest(url, init, {
4328
4421
  }
4329
4422
 
4330
4423
  // src/directUploadCapability.ts
4331
- function joinUrl(base, path21) {
4332
- return `${base.replace(/\/+$/, "")}${path21}`;
4424
+ function joinUrl(base, path23) {
4425
+ return `${base.replace(/\/+$/, "")}${path23}`;
4333
4426
  }
4334
4427
  function jsonHeaders(apiKey) {
4335
4428
  return {
@@ -5019,6 +5112,15 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
5019
5112
  return candidates.filter((candidate) => existsSync(candidate.path));
5020
5113
  }
5021
5114
 
5115
+ // src/authEnv.ts
5116
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
5117
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
5118
+ function scrubDaemonChildEnv(env) {
5119
+ delete env[DAEMON_API_KEY_ENV];
5120
+ delete env[SLOCK_AGENT_TOKEN_ENV];
5121
+ return env;
5122
+ }
5123
+
5022
5124
  // src/agentCredentialProxy.ts
5023
5125
  import { randomBytes } from "crypto";
5024
5126
  import http from "http";
@@ -6600,7 +6702,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
6600
6702
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
6601
6703
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
6602
6704
  var RAW_CREDENTIAL_ENV_DENYLIST = [
6603
- "SLOCK_AGENT_CREDENTIAL_KEY"
6705
+ "SLOCK_AGENT_TOKEN",
6706
+ "SLOCK_AGENT_CREDENTIAL_KEY",
6707
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
6604
6708
  ];
6605
6709
  var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
6606
6710
  "agent-token",
@@ -6955,7 +7059,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6955
7059
  SLOCK_SERVER_URL: ctx.config.serverUrl,
6956
7060
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
6957
7061
  };
6958
- delete spawnEnv.SLOCK_AGENT_TOKEN;
7062
+ scrubDaemonChildEnv(spawnEnv);
6959
7063
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
6960
7064
  delete spawnEnv[key];
6961
7065
  }
@@ -7523,7 +7627,7 @@ function requiresWindowsShell(command, platform = process.platform) {
7523
7627
  }
7524
7628
  function resolveCommandOnPath(command, deps = {}) {
7525
7629
  const platform = deps.platform ?? process.platform;
7526
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7630
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7527
7631
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7528
7632
  const existsSyncFn = deps.existsSyncFn ?? existsSync3;
7529
7633
  if (platform === "win32") {
@@ -7549,7 +7653,7 @@ function firstExistingPath(candidates, deps = {}) {
7549
7653
  return null;
7550
7654
  }
7551
7655
  function readCommandVersion(command, args = [], deps = {}) {
7552
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7656
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7553
7657
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7554
7658
  try {
7555
7659
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -7975,7 +8079,7 @@ function codexNotificationDiagnosticEvent(message) {
7975
8079
  return null;
7976
8080
  }
7977
8081
  const sessionId = codexMessageThreadId(message);
7978
- const path21 = boundedString(params.path);
8082
+ const path23 = boundedString(params.path);
7979
8083
  return {
7980
8084
  kind: "runtime_diagnostic",
7981
8085
  severity: "warning",
@@ -7983,7 +8087,7 @@ function codexNotificationDiagnosticEvent(message) {
7983
8087
  itemType: message.method,
7984
8088
  message: diagnosticMessage,
7985
8089
  ...details ? { details } : {},
7986
- ...path21 ? { path: path21 } : {},
8090
+ ...path23 ? { path: path23 } : {},
7987
8091
  ...params.range !== void 0 ? { range: params.range } : {},
7988
8092
  payloadBytes: payloadBytes(params),
7989
8093
  ...sessionId ? { sessionId } : {}
@@ -9633,11 +9737,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
9633
9737
  return parseCursorModelsOutput(String(result.stdout || ""));
9634
9738
  }
9635
9739
  function buildCursorModelProbeEnv(deps = {}) {
9636
- return withWindowsUserEnvironment({
9740
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
9637
9741
  ...deps.env ?? process.env,
9638
9742
  FORCE_COLOR: "0",
9639
9743
  NO_COLOR: "1"
9640
- }, deps);
9744
+ }, deps));
9641
9745
  }
9642
9746
  function runCursorModelsCommand() {
9643
9747
  return spawnSync("cursor-agent", ["models"], {
@@ -9693,7 +9797,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
9693
9797
  }
9694
9798
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
9695
9799
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
9696
- const env = deps.env ?? process.env;
9800
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
9697
9801
  const winPath = path8.win32;
9698
9802
  let geminiEntry = null;
9699
9803
  try {
@@ -9830,12 +9934,15 @@ var GeminiDriver = class {
9830
9934
  // src/drivers/kimi.ts
9831
9935
  import { randomUUID as randomUUID2 } from "crypto";
9832
9936
  import { spawn as spawn7 } from "child_process";
9833
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
9937
+ import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
9834
9938
  import os4 from "os";
9835
9939
  import path9 from "path";
9836
9940
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
9837
9941
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
9838
9942
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
9943
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
9944
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
9945
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
9839
9946
  function parseToolArguments(raw) {
9840
9947
  if (typeof raw !== "string") return raw;
9841
9948
  try {
@@ -9844,6 +9951,73 @@ function parseToolArguments(raw) {
9844
9951
  return raw;
9845
9952
  }
9846
9953
  }
9954
+ function readKimiConfigSource(home = os4.homedir(), env = process.env) {
9955
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
9956
+ if (inlineConfig && inlineConfig.trim()) {
9957
+ return {
9958
+ raw: inlineConfig,
9959
+ explicitPath: null,
9960
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
9961
+ };
9962
+ }
9963
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
9964
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
9965
+ try {
9966
+ return {
9967
+ raw: readFileSync3(configPath, "utf8"),
9968
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
9969
+ sourcePath: configPath
9970
+ };
9971
+ } catch {
9972
+ return {
9973
+ raw: null,
9974
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
9975
+ sourcePath: configPath
9976
+ };
9977
+ }
9978
+ }
9979
+ function buildKimiSpawnEnv(env = process.env) {
9980
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
9981
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
9982
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
9983
+ return scrubDaemonChildEnv(spawnEnv);
9984
+ }
9985
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
9986
+ return {
9987
+ ...process.env,
9988
+ ...ctx.config.envVars || {},
9989
+ ...overrideEnv || {}
9990
+ };
9991
+ }
9992
+ function buildKimiLaunchOptions(ctx, opts = {}) {
9993
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
9994
+ const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
9995
+ const args = [];
9996
+ let configFilePath = null;
9997
+ let configContent = null;
9998
+ if (source.explicitPath) {
9999
+ configFilePath = source.explicitPath;
10000
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
10001
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
10002
+ configContent = source.raw;
10003
+ if (opts.writeGeneratedConfig !== false) {
10004
+ writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
10005
+ chmodSync(configFilePath, 384);
10006
+ }
10007
+ }
10008
+ if (configFilePath) {
10009
+ args.push("--config-file", configFilePath);
10010
+ }
10011
+ if (ctx.config.model && ctx.config.model !== "default") {
10012
+ args.push("--model", ctx.config.model);
10013
+ }
10014
+ return {
10015
+ args,
10016
+ env: buildKimiSpawnEnv(env),
10017
+ configFilePath,
10018
+ configContent
10019
+ };
10020
+ }
9847
10021
  function resolveKimiSpawn(commandArgs, deps = {}) {
9848
10022
  return {
9849
10023
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -9867,7 +10041,25 @@ var KimiDriver = class {
9867
10041
  };
9868
10042
  model = {
9869
10043
  detectedModelsVerifiedAs: "launchable",
9870
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
10044
+ toLaunchSpec: (modelId, ctx, opts) => {
10045
+ if (!ctx) return { args: ["--model", modelId] };
10046
+ const launchCtx = {
10047
+ ...ctx,
10048
+ config: {
10049
+ ...ctx.config,
10050
+ model: modelId
10051
+ }
10052
+ };
10053
+ const launch = buildKimiLaunchOptions(launchCtx, {
10054
+ home: opts?.home,
10055
+ writeGeneratedConfig: false
10056
+ });
10057
+ return {
10058
+ args: launch.args,
10059
+ env: launch.env,
10060
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
10061
+ };
10062
+ }
9871
10063
  };
9872
10064
  supportsStdinNotification = true;
9873
10065
  busyDeliveryMode = "direct";
@@ -9891,21 +10083,23 @@ var KimiDriver = class {
9891
10083
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
9892
10084
  ""
9893
10085
  ].join("\n"), "utf8");
10086
+ const launch = buildKimiLaunchOptions(ctx);
9894
10087
  const args = [
9895
10088
  "--wire",
9896
10089
  "--yolo",
9897
10090
  "--agent-file",
9898
10091
  agentFilePath,
9899
10092
  "--session",
9900
- this.sessionId
10093
+ this.sessionId,
10094
+ ...launch.args
9901
10095
  ];
9902
10096
  const launchRuntimeFields = runtimeConfigToLaunchFields(hydrateRuntimeConfig(ctx.config));
9903
10097
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
9904
10098
  args.push("--model", launchRuntimeFields.model);
9905
10099
  }
9906
10100
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
9907
- const launch = resolveKimiSpawn(args);
9908
- const proc = spawn7(launch.command, launch.args, {
10101
+ const spawnTarget = resolveKimiSpawn(args);
10102
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
9909
10103
  cwd: ctx.workingDirectory,
9910
10104
  stdio: ["pipe", "pipe", "pipe"],
9911
10105
  env: spawnEnv,
@@ -9913,7 +10107,7 @@ var KimiDriver = class {
9913
10107
  // and has an 8191-character command-line limit. Kimi's official
9914
10108
  // installer/uv entrypoint is an executable, so launch it directly and
9915
10109
  // keep prompts on stdin / files instead of routing through cmd.exe.
9916
- shell: launch.shell
10110
+ shell: spawnTarget.shell
9917
10111
  });
9918
10112
  proc.stdin?.write(JSON.stringify({
9919
10113
  jsonrpc: "2.0",
@@ -10026,14 +10220,9 @@ var KimiDriver = class {
10026
10220
  return detectKimiModels();
10027
10221
  }
10028
10222
  };
10029
- function detectKimiModels(home = os4.homedir()) {
10030
- const configPath = path9.join(home, ".kimi", "config.toml");
10031
- let raw;
10032
- try {
10033
- raw = readFileSync3(configPath, "utf8");
10034
- } catch {
10035
- return null;
10036
- }
10223
+ function detectKimiModels(home = os4.homedir(), opts = {}) {
10224
+ const raw = readKimiConfigSource(home, opts.env).raw;
10225
+ if (raw === null) return null;
10037
10226
  const models = [];
10038
10227
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
10039
10228
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -10712,7 +10901,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
10712
10901
  const platform = deps.platform ?? process.platform;
10713
10902
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
10714
10903
  const result = spawnSyncFn("opencode", ["models"], {
10715
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
10904
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
10716
10905
  encoding: "utf8",
10717
10906
  timeout: 5e3,
10718
10907
  shell: platform === "win32"
@@ -11008,7 +11197,9 @@ function createPiSdkEventMappingState(sessionId = null) {
11008
11197
  return {
11009
11198
  sessionId,
11010
11199
  sessionAnnounced: false,
11011
- sawTextDelta: false
11200
+ sawTextDelta: false,
11201
+ thinkingBuffers: /* @__PURE__ */ new Map(),
11202
+ announcedThinkingIndexes: /* @__PURE__ */ new Set()
11012
11203
  };
11013
11204
  }
11014
11205
  function buildPiSessionDir(workingDirectory) {
@@ -11223,10 +11414,40 @@ function pushSessionInitIfNeeded2(state, events) {
11223
11414
  state.sessionAnnounced = true;
11224
11415
  }
11225
11416
  }
11417
+ function piAssistantContentIndex(assistantEvent) {
11418
+ return "contentIndex" in assistantEvent && typeof assistantEvent.contentIndex === "number" ? assistantEvent.contentIndex : 0;
11419
+ }
11420
+ function announcePiThinkingIfNeeded(index, state) {
11421
+ if (state.announcedThinkingIndexes.has(index)) return [];
11422
+ state.announcedThinkingIndexes.add(index);
11423
+ return [{ kind: "thinking", text: "" }];
11424
+ }
11425
+ function resetPiThinking(index, state) {
11426
+ state.thinkingBuffers.delete(index);
11427
+ state.announcedThinkingIndexes.delete(index);
11428
+ }
11226
11429
  function mapPiAssistantMessageEvent(assistantEvent, state) {
11227
11430
  switch (assistantEvent.type) {
11228
- case "thinking_delta":
11229
- return typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0 ? [{ kind: "thinking", text: assistantEvent.delta }] : [];
11431
+ case "thinking_start": {
11432
+ const index = piAssistantContentIndex(assistantEvent);
11433
+ resetPiThinking(index, state);
11434
+ return announcePiThinkingIfNeeded(index, state);
11435
+ }
11436
+ case "thinking_delta": {
11437
+ const index = piAssistantContentIndex(assistantEvent);
11438
+ const events = announcePiThinkingIfNeeded(index, state);
11439
+ if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
11440
+ state.thinkingBuffers.set(index, `${state.thinkingBuffers.get(index) ?? ""}${assistantEvent.delta}`);
11441
+ }
11442
+ return events;
11443
+ }
11444
+ case "thinking_end": {
11445
+ const index = piAssistantContentIndex(assistantEvent);
11446
+ const buffered = state.thinkingBuffers.get(index) ?? "";
11447
+ const text = typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? assistantEvent.content : buffered;
11448
+ resetPiThinking(index, state);
11449
+ return text ? [{ kind: "thinking", text }] : [];
11450
+ }
11230
11451
  case "text_delta":
11231
11452
  if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
11232
11453
  state.sawTextDelta = true;
@@ -11237,8 +11458,6 @@ function mapPiAssistantMessageEvent(assistantEvent, state) {
11237
11458
  return !state.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? [{ kind: "text", text: assistantEvent.content }] : [];
11238
11459
  case "error":
11239
11460
  return [{ kind: "error", message: piErrorMessage(assistantEvent.error.errorMessage || assistantEvent.error) }];
11240
- case "thinking_start":
11241
- case "thinking_end":
11242
11461
  case "text_start":
11243
11462
  case "toolcall_start":
11244
11463
  case "toolcall_delta":
@@ -11282,6 +11501,8 @@ function mapPiSdkEventToParsedEvents(event, state) {
11282
11501
  case "message_start":
11283
11502
  if (event.message.role === "assistant") {
11284
11503
  state.sawTextDelta = false;
11504
+ state.thinkingBuffers.clear();
11505
+ state.announcedThinkingIndexes.clear();
11285
11506
  }
11286
11507
  return events;
11287
11508
  case "message_update":
@@ -14758,8 +14979,9 @@ function classifyTerminalFailure(ap) {
14758
14979
  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)) {
14759
14980
  const actionRequired = diagnostics.spanAttrs.runtime_error_action_required === true;
14760
14981
  return {
14761
- detail: actionRequired ? formatRuntimeLoginRequiredMessage(ap.driver.id) : text,
14762
- actionRequired
14982
+ detail: actionRequired ? formatRuntimeActionRequiredMessage(ap) : text,
14983
+ actionRequired,
14984
+ ...actionRequired ? { entries: buildRuntimeActionRequiredEntries(ap, text, diagnostics) } : {}
14763
14985
  };
14764
14986
  }
14765
14987
  }
@@ -14775,6 +14997,52 @@ function classifyStickyTerminalFailure(ap) {
14775
14997
  if (isRuntimeStartTimeoutText(terminalFailure.detail)) return null;
14776
14998
  return null;
14777
14999
  }
15000
+ function formatRuntimeActionRequiredMessage(ap) {
15001
+ if (ap.driver.id === "claude" && isClaudeCustomProviderConfig(ap.config)) {
15002
+ return "Claude Code custom provider authentication failed. Check this agent's custom Claude provider API key/API URL, then retry starting this agent.";
15003
+ }
15004
+ return formatRuntimeLoginRequiredMessage(ap.driver.id);
15005
+ }
15006
+ function buildRuntimeActionRequiredEntries(ap, text, diagnostics) {
15007
+ const excerpt = String(diagnostics.eventAttrs.runtime_error_message_excerpt ?? "").trim();
15008
+ const subtype = classifyRuntimeActionRequiredSubtype(ap, text);
15009
+ const detail = formatRuntimeActionRequiredMessage(ap);
15010
+ const entries = [
15011
+ { kind: "text", text: `Error: ${detail}` },
15012
+ { kind: "text", text: `Runtime auth diagnostic: ${subtype}` }
15013
+ ];
15014
+ if (excerpt) {
15015
+ entries.push({ kind: "text", text: `Raw error excerpt (redacted): ${excerpt}` });
15016
+ }
15017
+ if (ap.driver.id === "claude") {
15018
+ entries.push({
15019
+ kind: "text",
15020
+ 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."
15021
+ });
15022
+ }
15023
+ return entries;
15024
+ }
15025
+ function classifyRuntimeActionRequiredSubtype(ap, text) {
15026
+ const lower = text.toLowerCase();
15027
+ const customClaudeProvider = ap.driver.id === "claude" && isClaudeCustomProviderConfig(ap.config);
15028
+ if (customClaudeProvider) {
15029
+ 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)) {
15030
+ return "custom_provider_missing_key";
15031
+ }
15032
+ if (/\binvalid\b.*\b(?:api\s*)?key\b|\bunauthorized\b|\b401\b|\b403\b|auth(?:entication)? failed/i.test(text)) {
15033
+ return "custom_provider_invalid_key";
15034
+ }
15035
+ return "custom_provider_auth_error";
15036
+ }
15037
+ if (ap.driver.id === "claude") {
15038
+ if (lower.includes("not logged in") || lower.includes("not signed in") || lower.includes("please log in") || lower.includes("login required")) {
15039
+ return "host_claude_login_required";
15040
+ }
15041
+ return "runtime_auth_error";
15042
+ }
15043
+ if (ap.driver.id === "builtin") return "builtin_provider_auth_error";
15044
+ return "runtime_auth_error";
15045
+ }
14778
15046
  function isAuthClassTerminalLine(text) {
14779
15047
  return buildRuntimeErrorDiagnosticEnvelope(text).spanAttrs.runtime_error_action_required === true;
14780
15048
  }
@@ -16976,7 +17244,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16976
17244
  agentId,
16977
17245
  "error",
16978
17246
  terminalFailureDetail.detail,
16979
- [{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
17247
+ terminalFailureDetail.entries ?? [{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
16980
17248
  ap.launchId,
16981
17249
  "runtime_error"
16982
17250
  );
@@ -17007,9 +17275,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17007
17275
  agentProcess.decisionErrorWindow.recordRuntimeError(startResult.error);
17008
17276
  }
17009
17277
  if (startResult.error && diagnostics?.spanAttrs.runtime_error_action_required === true) {
17010
- const visibleErrorMessage = formatRuntimeLoginRequiredMessage(agentProcess.driver.id);
17278
+ const terminalFailure = classifyTerminalFailure(agentProcess);
17279
+ const visibleErrorMessage = terminalFailure?.detail ?? formatRuntimeActionRequiredMessage(agentProcess);
17011
17280
  this.broadcastActivity(agentId, "error", visibleErrorMessage, [
17012
- { kind: "text", text: `Error: ${visibleErrorMessage}` }
17281
+ ...terminalFailure?.entries ?? [{ kind: "text", text: `Error: ${visibleErrorMessage}` }]
17013
17282
  ], agentProcess.launchId, "runtime_error");
17014
17283
  }
17015
17284
  throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
@@ -20172,11 +20441,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20172
20441
  ap.decisionErrorWindow.recordRuntimeError(event.message);
20173
20442
  }
20174
20443
  let visibleErrorMessage = event.message;
20444
+ let visibleErrorEntries;
20175
20445
  if (ap) {
20176
20446
  const runtimeErrorDiagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
20177
20447
  const runtimeErrorFingerprint = typeof runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint === "string" ? runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint : null;
20178
20448
  if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
20179
- visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
20449
+ visibleErrorMessage = formatRuntimeActionRequiredMessage(ap);
20180
20450
  }
20181
20451
  const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
20182
20452
  const backoffFailPoint = this.runtimeErrorDeliveryBackoffFailPointForTesting?.({
@@ -20202,6 +20472,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20202
20472
  if (fingerprintFence) {
20203
20473
  visibleErrorMessage = fingerprintFence.detail;
20204
20474
  }
20475
+ visibleErrorEntries = terminalFailure?.entries;
20205
20476
  this.noteRuntimeErrorDeliveryBackoff(agentId, ap, event.message, terminalFailure, stickyTerminalFailure, backoffReasonOverride);
20206
20477
  if (reduction.shouldDisableToolBoundaryFlush) {
20207
20478
  this.recordGatedSteeringEvent(agentId, ap, "disabled", {
@@ -20248,9 +20519,14 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20248
20519
  }
20249
20520
  }
20250
20521
  }
20251
- this.broadcastActivity(agentId, "error", visibleErrorMessage, [
20252
- { kind: "text", text: `Error: ${visibleErrorMessage}` }
20253
- ], void 0, "runtime_error");
20522
+ this.broadcastActivity(
20523
+ agentId,
20524
+ "error",
20525
+ visibleErrorMessage,
20526
+ visibleErrorEntries ?? [{ kind: "text", text: `Error: ${visibleErrorMessage}` }],
20527
+ void 0,
20528
+ "runtime_error"
20529
+ );
20254
20530
  break;
20255
20531
  }
20256
20532
  }
@@ -20917,8 +21193,11 @@ var DaemonConnection = class {
20917
21193
  doConnect() {
20918
21194
  if (!this.shouldConnect) return;
20919
21195
  if (this.ws && this.ws.readyState !== WebSocket.CLOSED) return;
20920
- const wsUrl = this.options.serverUrl.replace(/^http/, "ws") + `/daemon/connect?key=${this.options.apiKey}`;
20921
- const wsOptions = buildWebSocketOptions(wsUrl, this.options.proxyEnv ?? process.env);
21196
+ const wsUrl = this.options.serverUrl.replace(/^http/, "ws") + "/daemon/connect";
21197
+ const wsOptions = {
21198
+ ...buildWebSocketOptions(wsUrl, this.options.proxyEnv ?? process.env),
21199
+ headers: { Authorization: `Bearer ${this.options.apiKey}` }
21200
+ };
20922
21201
  logger.info(`[Daemon] Connecting to ${this.options.serverUrl}...`);
20923
21202
  if (wsOptions?.agent) {
20924
21203
  logger.info("[Daemon] Using configured proxy for WebSocket connection");
@@ -21914,138 +22193,653 @@ function assertLegacyDaemonKeyNotAdoptedByComputer(options) {
21914
22193
  if (match) throw new LegacyDaemonKeyAdoptedByComputerError(match);
21915
22194
  }
21916
22195
 
21917
- // src/agentMigrationExport.ts
21918
- import { createHash as createHash7 } from "crypto";
21919
- import { createReadStream } from "fs";
21920
- import { lstat as lstat2, readdir as readdir4, readFile as readFile3, readlink } from "fs/promises";
21921
- import path19 from "path";
21922
- var AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION = "agent-bundle/v1";
21923
- var REGENERABLE_DIRECTORY_NAMES = [
21924
- ".venv",
21925
- "__pycache__",
21926
- "dist",
21927
- "node_modules",
21928
- "target"
21929
- ];
21930
- var SECRET_FILE_NAMES = /* @__PURE__ */ new Set([
21931
- ".env",
21932
- ".env.local",
21933
- ".env.development",
21934
- ".env.production",
21935
- ".env.test",
21936
- "credentials.json",
21937
- "credential.json"
21938
- ]);
21939
- var ENV_KEY_PATTERN = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
21940
- async function buildAgentMigrationExportManifest(input) {
21941
- const slockHome = path19.resolve(input.slockHome);
21942
- const workspace = path19.resolve(input.workspacePath ?? path19.join(slockHome, "agents", input.agentId));
21943
- const proposedIncludes = normalizeProposalPaths(input.cooperativeManifest?.include, workspace, "include");
21944
- const proposedRegenerableExcludes = normalizeProposalPaths(input.cooperativeManifest?.exclude_regenerable, workspace, "exclude_regenerable");
21945
- const cleanedProposal = normalizeProposalPaths(input.cooperativeManifest?.cleaned, workspace, "cleaned");
21946
- const state = {
21947
- workspace,
21948
- files: [],
21949
- excludedRegenerable: [],
21950
- promotedIncludes: [],
21951
- proposalRefusals: [
21952
- ...proposedIncludes.refusals,
21953
- ...proposedRegenerableExcludes.refusals,
21954
- ...cleanedProposal.refusals
21955
- ],
21956
- unreachable: [],
21957
- secretsDisclosed: [],
21958
- seenWorkspacePaths: /* @__PURE__ */ new Set(),
21959
- explicitIncludePaths: new Set(proposedIncludes.paths)
21960
- };
21961
- await walkWorkspace(workspace, "", new Set(proposedRegenerableExcludes.paths), state, { forceIncludeRegenerable: false });
21962
- for (const requestedPath of proposedIncludes.paths) {
21963
- if (!state.seenWorkspacePaths.has(requestedPath)) {
21964
- await includeWorkspacePath(requestedPath, state, { forceIncludeRegenerable: true });
21965
- }
22196
+ // src/agentMigrationHttpTransport.ts
22197
+ import { createHash as createHash7, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
22198
+ import http2 from "http";
22199
+ import { Readable } from "stream";
22200
+ var AGENT_MIGRATION_TRANSPORT_HOST_ENV = "SLOCK_AGENT_MIGRATION_TRANSPORT_HOST";
22201
+ var AGENT_MIGRATION_TRANSPORT_PORT_ENV = "SLOCK_AGENT_MIGRATION_TRANSPORT_PORT";
22202
+ var AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV = "SLOCK_AGENT_MIGRATION_TRANSPORT_PUBLIC_URL";
22203
+ var AGENT_MIGRATION_CONTROL_SEAM_ENV = "SLOCK_AGENT_MIGRATION_CONTROL_SEAM";
22204
+ var CONTROL_REQUEST_MAX_BYTES = 8 * 1024 * 1024;
22205
+ var MigrationBundleStreamError = class extends Error {
22206
+ constructor(code) {
22207
+ super(code);
22208
+ this.code = code;
21966
22209
  }
21967
- for (const requestedPath of proposedRegenerableExcludes.paths) {
21968
- if (isRegenerablePath(requestedPath)) continue;
21969
- state.promotedIncludes.push({ path: requestedPath, reason: "exclude_not_regenerable" });
21970
- await includeWorkspacePath(requestedPath, state, { forceIncludeRegenerable: true });
22210
+ };
22211
+ var AgentMigrationGrantRegistry = class {
22212
+ grants = /* @__PURE__ */ new Map();
22213
+ now;
22214
+ constructor(now = currentDate) {
22215
+ this.now = now;
21971
22216
  }
21972
- const crossTreeRefs = await buildCrossTreeRefs(input.runtimeSessionRefs ?? []);
21973
- const secretDisclosureProposal = normalizeProposalPaths(input.cooperativeManifest?.secrets_disclosed, workspace, "secrets_disclosed");
21974
- state.proposalRefusals.push(...secretDisclosureProposal.refusals);
21975
- return {
21976
- schemaVersion: AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
21977
- agentId: input.agentId,
21978
- mode: input.mode,
21979
- createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
21980
- roots: {
21981
- slockHome,
21982
- workspace
21983
- },
21984
- defaults: {
21985
- unknownFiles: "include",
21986
- excludePolicy: "regenerable_only",
21987
- regenerableDirectoryNames: [...REGENERABLE_DIRECTORY_NAMES]
21988
- },
21989
- files: sortBundleEntries(state.files),
21990
- excludedRegenerable: sortByPath(state.excludedRegenerable),
21991
- promotedIncludes: sortByPath(state.promotedIncludes),
21992
- proposalRefusals: sortByPath(state.proposalRefusals),
21993
- unreachable: sortByPath(state.unreachable),
21994
- secretsDisclosed: sortByPath([
21995
- ...state.secretsDisclosed,
21996
- ...normalizeProvidedSecretDisclosures(secretDisclosureProposal.paths)
21997
- ]),
21998
- cleaned: cleanedProposal.paths,
21999
- crossTreeRefs: crossTreeRefs.sort((a, b) => a.bundlePath.localeCompare(b.bundlePath))
22000
- };
22001
- }
22002
- async function walkWorkspace(root, relativeDir, proposedRegenerableExcludes, state, opts) {
22003
- const absoluteDir = path19.join(root, relativeDir);
22004
- let entries;
22005
- try {
22006
- entries = await readdir4(absoluteDir, { withFileTypes: true });
22007
- } catch {
22008
- state.unreachable.push({
22009
- path: relativeDir || ".",
22010
- sourcePath: absoluteDir,
22011
- reason: "read_error"
22012
- });
22013
- return;
22217
+ createGrant(input) {
22218
+ const token = input.token ?? randomBytes2(32).toString("base64url");
22219
+ const manifestSha256 = sha256Buffer(canonicalJsonBuffer(input.manifest));
22220
+ const grant = {
22221
+ grantId: input.grantId ?? randomBytes2(16).toString("hex"),
22222
+ tokenHash: hashToken(token),
22223
+ expiresAt: input.expiresAt,
22224
+ oneTimeUse: input.oneTimeUse ?? true,
22225
+ state: "issued",
22226
+ manifest: input.manifest,
22227
+ manifestSha256,
22228
+ bundleEtag: `"agent-migration-${manifestSha256}"`,
22229
+ bundleSizeBytes: normalizeBundleSizeBytes(input.bundleSizeBytes)
22230
+ };
22231
+ this.grants.set(grant.grantId, grant);
22232
+ return { grant, token };
22014
22233
  }
22015
- entries.sort((a, b) => a.name.localeCompare(b.name));
22016
- for (const entry of entries) {
22017
- const relativePath = toPosixPath(path19.join(relativeDir, entry.name));
22018
- if (entry.isDirectory()) {
22019
- if (!opts.forceIncludeRegenerable && isRegenerablePath(relativePath)) {
22020
- if (state.explicitIncludePaths.has(relativePath)) {
22021
- await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, { forceIncludeRegenerable: true });
22022
- continue;
22023
- }
22024
- if (hasExplicitIncludeDescendant(relativePath, state.explicitIncludePaths)) {
22025
- await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, opts);
22026
- continue;
22027
- }
22028
- state.excludedRegenerable.push({
22029
- path: relativePath,
22030
- reason: proposedRegenerableExcludes.has(relativePath) ? "cooperative_exclude_regenerable" : "regenerable_default",
22031
- regenerableHint: `${entry.name} is treated as rebuildable/installable state and is not bundled by default`
22032
- });
22033
- continue;
22034
- }
22035
- await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, opts);
22036
- continue;
22037
- }
22038
- await includeWorkspacePath(relativePath, state, { forceIncludeRegenerable: opts.forceIncludeRegenerable });
22234
+ get(grantId) {
22235
+ return this.grants.get(grantId);
22039
22236
  }
22040
- }
22041
- async function includeWorkspacePath(workspaceRelativePath, state, opts) {
22042
- const normalizedRelativePath = normalizeRelativePath(workspaceRelativePath);
22043
- if (state.seenWorkspacePaths.has(normalizedRelativePath)) return;
22044
- const sourcePath = path19.join(state.workspace, normalizedRelativePath);
22045
- let stat4;
22046
- try {
22047
- stat4 = await lstat2(sourcePath);
22048
- } catch {
22237
+ authenticate(grantId, token) {
22238
+ const grant = this.grants.get(grantId);
22239
+ if (!grant || !token || !verifyTokenHash(token, grant.tokenHash)) {
22240
+ return { ok: false, status: 401, code: "migration_grant_auth_failed" };
22241
+ }
22242
+ if (grant.state === "revoked" || grant.state === "consumed") {
22243
+ return { ok: false, status: 410, code: "migration_grant_unavailable" };
22244
+ }
22245
+ if (this.now().getTime() >= grant.expiresAt.getTime()) {
22246
+ grant.state = "expired";
22247
+ return { ok: false, status: 410, code: "migration_grant_unavailable" };
22248
+ }
22249
+ return { ok: true, grant };
22250
+ }
22251
+ revokeAll() {
22252
+ for (const grant of this.grants.values()) {
22253
+ if (grant.state !== "consumed" && grant.state !== "expired") {
22254
+ grant.state = "revoked";
22255
+ }
22256
+ }
22257
+ }
22258
+ beginBundleStream(grant) {
22259
+ const current = this.grants.get(grant.grantId);
22260
+ if (current !== grant) return false;
22261
+ if (current.state !== "issued" && current.state !== "interrupted") return false;
22262
+ current.state = "streaming";
22263
+ return true;
22264
+ }
22265
+ };
22266
+ function createAgentMigrationHttpTransport(options = {}) {
22267
+ const now = options.now ?? currentDate;
22268
+ const registry = new AgentMigrationGrantRegistry(now);
22269
+ const controlBundles = /* @__PURE__ */ new Map();
22270
+ const fallbackBundleStreamFactory = options.bundleStreamFactory ?? defaultBundleStream;
22271
+ const bundleStreamFactory = (grant, request) => {
22272
+ const bundle = controlBundles.get(grant.grantId);
22273
+ if (bundle) {
22274
+ const endExclusive = request.lengthBytes === null ? void 0 : request.offsetBytes + request.lengthBytes;
22275
+ return Readable.from([bundle.subarray(request.offsetBytes, endExclusive)]);
22276
+ }
22277
+ return fallbackBundleStreamFactory(grant, request);
22278
+ };
22279
+ const listenDefaults = options.listen ?? resolveAgentMigrationHttpTransportListenOptions();
22280
+ const controlSeamEnabled = options.controlSeam ?? resolveAgentMigrationControlSeamEnabled();
22281
+ const server = http2.createServer((req, res) => {
22282
+ const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22283
+ if (parsed.pathname.startsWith("/migration-control")) {
22284
+ void handleMigrationControlRequest(req, res, parsed, {
22285
+ enabled: controlSeamEnabled,
22286
+ now,
22287
+ registry,
22288
+ controlBundles
22289
+ });
22290
+ return;
22291
+ }
22292
+ void handleMigrationRequest(req, res, registry, bundleStreamFactory);
22293
+ });
22294
+ return {
22295
+ grants: registry,
22296
+ server,
22297
+ listen: (port = listenDefaults.port ?? 0, host = listenDefaults.host ?? "127.0.0.1") => new Promise((resolve, reject) => {
22298
+ server.once("error", reject);
22299
+ server.listen(port, host, () => {
22300
+ server.off("error", reject);
22301
+ const address = server.address();
22302
+ if (!address || typeof address === "string") {
22303
+ reject(new Error("migration transport listen did not produce a TCP address"));
22304
+ return;
22305
+ }
22306
+ resolve({ url: listenDefaults.publicUrl ?? `http://${formatListenAddress(address.address)}:${address.port}` });
22307
+ });
22308
+ }),
22309
+ close: () => new Promise((resolve, reject) => {
22310
+ registry.revokeAll();
22311
+ server.close((err) => {
22312
+ if (err) reject(err);
22313
+ else resolve();
22314
+ });
22315
+ })
22316
+ };
22317
+ }
22318
+ function resolveAgentMigrationHttpTransportListenOptions(env = process.env) {
22319
+ const host = env[AGENT_MIGRATION_TRANSPORT_HOST_ENV]?.trim();
22320
+ const port = parseOptionalPort(env[AGENT_MIGRATION_TRANSPORT_PORT_ENV]);
22321
+ const publicUrl = env[AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV]?.trim();
22322
+ return {
22323
+ ...host ? { host } : {},
22324
+ ...port !== void 0 ? { port } : {},
22325
+ ...publicUrl ? { publicUrl } : {}
22326
+ };
22327
+ }
22328
+ function resolveAgentMigrationControlSeamEnabled(env = process.env) {
22329
+ return env[AGENT_MIGRATION_CONTROL_SEAM_ENV]?.trim() !== "0";
22330
+ }
22331
+ async function handleMigrationControlRequest(req, res, parsed, context) {
22332
+ if (!context.enabled) {
22333
+ sendJson(res, 404, { code: "migration_route_not_found" });
22334
+ return;
22335
+ }
22336
+ if (parsed.pathname !== "/migration-control/grants") {
22337
+ sendJson(res, 404, { code: "migration_route_not_found" });
22338
+ return;
22339
+ }
22340
+ if (req.method !== "POST") {
22341
+ sendJson(res, 405, { code: "migration_method_not_allowed" });
22342
+ return;
22343
+ }
22344
+ let rawInput;
22345
+ try {
22346
+ rawInput = await readJsonRequestBody(req, CONTROL_REQUEST_MAX_BYTES);
22347
+ } catch (err) {
22348
+ if (err instanceof MigrationBundleStreamError) {
22349
+ sendJson(res, err.code === "migration_control_payload_too_large" ? 413 : 400, { code: err.code });
22350
+ return;
22351
+ }
22352
+ sendJson(res, 400, { code: "migration_control_invalid_json" });
22353
+ return;
22354
+ }
22355
+ const input = parseControlGrantInput(rawInput, context.now);
22356
+ if (!input.ok) {
22357
+ sendJson(res, 400, { code: input.code });
22358
+ return;
22359
+ }
22360
+ const { bundle, createGrantInput } = input;
22361
+ const { grant, token } = context.registry.createGrant({
22362
+ ...createGrantInput,
22363
+ bundleSizeBytes: bundle.byteLength
22364
+ });
22365
+ context.controlBundles.set(grant.grantId, bundle);
22366
+ sendJson(res, 201, {
22367
+ grantId: grant.grantId,
22368
+ token,
22369
+ manifestSha256: grant.manifestSha256,
22370
+ bundleSizeBytes: grant.bundleSizeBytes,
22371
+ expiresAt: grant.expiresAt.toISOString(),
22372
+ manifestUrl: `/migration/${encodeURIComponent(grant.grantId)}/manifest`,
22373
+ bundleUrl: `/migration/${encodeURIComponent(grant.grantId)}/bundle.tar`
22374
+ });
22375
+ }
22376
+ async function handleMigrationRequest(req, res, registry, bundleStreamFactory) {
22377
+ const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22378
+ const match = /^\/migration\/([^/]+)\/([^/]+)(?:\/([^/]+))?$/.exec(parsed.pathname);
22379
+ if (!match) {
22380
+ sendJson(res, 404, { code: "migration_route_not_found" });
22381
+ return;
22382
+ }
22383
+ const [, grantId, resource, resourceId] = match;
22384
+ const auth = registry.authenticate(decodeURIComponent(grantId), extractBearerToken(req));
22385
+ if (!auth.ok) {
22386
+ sendJson(res, auth.status, { code: auth.code });
22387
+ return;
22388
+ }
22389
+ if (resource === "manifest" && req.method === "GET" && !resourceId) {
22390
+ sendJson(res, 200, {
22391
+ manifestSha256: auth.grant.manifestSha256,
22392
+ manifest: auth.grant.manifest
22393
+ }, {
22394
+ "X-Raft-Manifest-Sha": auth.grant.manifestSha256,
22395
+ ETag: `"manifest-${auth.grant.manifestSha256}"`
22396
+ });
22397
+ return;
22398
+ }
22399
+ if (resource === "bundle.tar" && !resourceId) {
22400
+ if (req.method === "HEAD") {
22401
+ sendHead(res, auth.grant);
22402
+ return;
22403
+ }
22404
+ if (req.method === "GET") {
22405
+ await streamBundle(req, res, auth.grant, registry, bundleStreamFactory);
22406
+ return;
22407
+ }
22408
+ }
22409
+ if (resource === "chunk" && req.method === "GET" && resourceId) {
22410
+ sendJson(res, 501, { code: "migration_chunk_not_implemented" });
22411
+ return;
22412
+ }
22413
+ sendJson(res, 405, { code: "migration_method_not_allowed" });
22414
+ }
22415
+ function sendHead(res, grant) {
22416
+ res.statusCode = 200;
22417
+ res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22418
+ res.setHeader("ETag", grant.bundleEtag);
22419
+ res.setHeader("Accept-Ranges", grant.bundleSizeBytes === null ? "none" : "bytes");
22420
+ res.setHeader("X-Raft-Bundle-Size", grant.bundleSizeBytes === null ? "unknown" : grant.bundleSizeBytes.toString());
22421
+ if (grant.bundleSizeBytes !== null) {
22422
+ res.setHeader("Content-Length", grant.bundleSizeBytes.toString());
22423
+ }
22424
+ res.end();
22425
+ }
22426
+ async function streamBundle(req, res, grant, registry, bundleStreamFactory) {
22427
+ if (grant.state === "streaming") {
22428
+ sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22429
+ return;
22430
+ }
22431
+ if (grant.oneTimeUse && grant.state !== "issued" && grant.state !== "interrupted") {
22432
+ sendJson(res, 410, { code: "migration_grant_unavailable" });
22433
+ return;
22434
+ }
22435
+ const range = planRange(req, grant);
22436
+ if (!range.ok) {
22437
+ sendJson(res, 416, { code: range.code }, range.headers);
22438
+ return;
22439
+ }
22440
+ const previousState = grant.state;
22441
+ if (!registry.beginBundleStream(grant)) {
22442
+ sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22443
+ return;
22444
+ }
22445
+ let stream;
22446
+ try {
22447
+ stream = bundleStreamFactory(grant, range.request);
22448
+ } catch (err) {
22449
+ grant.state = previousState;
22450
+ const code = err instanceof MigrationBundleStreamError ? err.code : "migration_bundle_stream_failed";
22451
+ sendJson(res, 500, { code });
22452
+ return;
22453
+ }
22454
+ let completed = false;
22455
+ res.on("close", () => {
22456
+ if (!completed && grant.state === "streaming") {
22457
+ grant.state = "interrupted";
22458
+ }
22459
+ });
22460
+ try {
22461
+ res.statusCode = 200;
22462
+ res.setHeader("Content-Type", "application/x-tar");
22463
+ res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22464
+ res.setHeader("ETag", grant.bundleEtag);
22465
+ res.setHeader("Accept-Ranges", grant.bundleSizeBytes === null ? "none" : "bytes");
22466
+ if (range.request.range) {
22467
+ res.statusCode = 206;
22468
+ res.setHeader("Content-Range", `bytes ${range.request.range.start}-${range.request.range.end}/${grant.bundleSizeBytes}`);
22469
+ }
22470
+ if (range.request.lengthBytes !== null) {
22471
+ res.setHeader("Content-Length", range.request.lengthBytes.toString());
22472
+ }
22473
+ for await (const chunk of stream) {
22474
+ if (!res.write(chunk)) {
22475
+ await onceDrain(res);
22476
+ }
22477
+ }
22478
+ completed = true;
22479
+ grant.state = range.consumesGrant ? "consumed" : "interrupted";
22480
+ res.end();
22481
+ } catch {
22482
+ if (!completed) {
22483
+ grant.state = "interrupted";
22484
+ }
22485
+ if (!res.headersSent) {
22486
+ sendJson(res, 500, { code: "migration_bundle_stream_failed" });
22487
+ } else {
22488
+ res.destroy();
22489
+ }
22490
+ }
22491
+ }
22492
+ function planRange(req, grant) {
22493
+ const header = singleHeader(req.headers.range);
22494
+ if (!header) {
22495
+ return {
22496
+ ok: true,
22497
+ request: { range: null, offsetBytes: 0, lengthBytes: grant.bundleSizeBytes },
22498
+ consumesGrant: true
22499
+ };
22500
+ }
22501
+ if (grant.bundleSizeBytes === null) {
22502
+ return {
22503
+ ok: false,
22504
+ code: "migration_range_not_available",
22505
+ headers: { "Accept-Ranges": "none" }
22506
+ };
22507
+ }
22508
+ const parsed = parseSingleByteRange(header, grant.bundleSizeBytes);
22509
+ if (!parsed.ok) {
22510
+ return {
22511
+ ok: false,
22512
+ code: parsed.code,
22513
+ headers: {
22514
+ "Accept-Ranges": "bytes",
22515
+ "Content-Range": `bytes */${grant.bundleSizeBytes}`
22516
+ }
22517
+ };
22518
+ }
22519
+ return {
22520
+ ok: true,
22521
+ request: {
22522
+ range: parsed.range,
22523
+ offsetBytes: parsed.range.start,
22524
+ lengthBytes: parsed.range.end - parsed.range.start + 1
22525
+ },
22526
+ consumesGrant: parsed.range.end === grant.bundleSizeBytes - 1
22527
+ };
22528
+ }
22529
+ function parseSingleByteRange(header, sizeBytes) {
22530
+ const match = /^bytes=(\d+)-(\d*)$/.exec(header.trim());
22531
+ if (!match) {
22532
+ return { ok: false, code: "migration_range_not_satisfiable" };
22533
+ }
22534
+ const start = Number(match[1]);
22535
+ const requestedEnd = match[2] ? Number(match[2]) : sizeBytes - 1;
22536
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(requestedEnd) || start < 0 || requestedEnd < start || start >= sizeBytes) {
22537
+ return { ok: false, code: "migration_range_not_satisfiable" };
22538
+ }
22539
+ return {
22540
+ ok: true,
22541
+ range: {
22542
+ start,
22543
+ end: Math.min(requestedEnd, sizeBytes - 1)
22544
+ }
22545
+ };
22546
+ }
22547
+ function defaultBundleStream(grant) {
22548
+ void grant;
22549
+ throw new MigrationBundleStreamError("migration_bundle_stream_not_wired");
22550
+ }
22551
+ function parseControlGrantInput(rawInput, now) {
22552
+ if (!rawInput || typeof rawInput !== "object" || Array.isArray(rawInput)) {
22553
+ return { ok: false, code: "migration_control_invalid_payload" };
22554
+ }
22555
+ const input = rawInput;
22556
+ const manifest = input.manifest;
22557
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
22558
+ return { ok: false, code: "migration_control_manifest_required" };
22559
+ }
22560
+ const bundle = decodeControlBundle(input);
22561
+ if (!bundle.ok) return bundle;
22562
+ const expiresAt = parseControlGrantExpiry(input, now);
22563
+ if (!expiresAt.ok) return expiresAt;
22564
+ const grantId = optionalNonEmptyString(input.grantId);
22565
+ const token = optionalNonEmptyString(input.token);
22566
+ const oneTimeUse = typeof input.oneTimeUse === "boolean" ? input.oneTimeUse : void 0;
22567
+ return {
22568
+ ok: true,
22569
+ bundle: bundle.bundle,
22570
+ createGrantInput: {
22571
+ ...grantId ? { grantId } : {},
22572
+ ...token ? { token } : {},
22573
+ expiresAt: expiresAt.expiresAt,
22574
+ manifest,
22575
+ ...oneTimeUse === void 0 ? {} : { oneTimeUse }
22576
+ }
22577
+ };
22578
+ }
22579
+ function decodeControlBundle(input) {
22580
+ if (typeof input.bundleBase64 === "string") {
22581
+ const normalized = input.bundleBase64.trim();
22582
+ if (!normalized) return { ok: false, code: "migration_control_bundle_required" };
22583
+ const bundle = Buffer.from(normalized, "base64");
22584
+ if (bundle.byteLength === 0) return { ok: false, code: "migration_control_bundle_required" };
22585
+ return { ok: true, bundle };
22586
+ }
22587
+ if (typeof input.bundleText === "string") {
22588
+ const bundle = Buffer.from(input.bundleText, "utf8");
22589
+ if (bundle.byteLength === 0) return { ok: false, code: "migration_control_bundle_required" };
22590
+ return { ok: true, bundle };
22591
+ }
22592
+ return { ok: false, code: "migration_control_bundle_required" };
22593
+ }
22594
+ function parseControlGrantExpiry(input, now) {
22595
+ if (typeof input.expiresAt === "string" && input.expiresAt.trim()) {
22596
+ const expiresAt = new Date(input.expiresAt);
22597
+ if (Number.isNaN(expiresAt.getTime())) {
22598
+ return { ok: false, code: "migration_control_invalid_expiry" };
22599
+ }
22600
+ return { ok: true, expiresAt };
22601
+ }
22602
+ if (input.expiresInSeconds !== void 0) {
22603
+ const expiresInSeconds = input.expiresInSeconds;
22604
+ if (typeof expiresInSeconds !== "number" || !Number.isSafeInteger(expiresInSeconds) || expiresInSeconds <= 0) {
22605
+ return { ok: false, code: "migration_control_invalid_expiry" };
22606
+ }
22607
+ return { ok: true, expiresAt: new Date(now().getTime() + expiresInSeconds * 1e3) };
22608
+ }
22609
+ return { ok: true, expiresAt: new Date(now().getTime() + 15 * 60 * 1e3) };
22610
+ }
22611
+ function optionalNonEmptyString(value) {
22612
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
22613
+ }
22614
+ function normalizeBundleSizeBytes(size) {
22615
+ if (size === null || size === void 0) return null;
22616
+ if (!Number.isSafeInteger(size) || size < 0) return null;
22617
+ return size;
22618
+ }
22619
+ function parseOptionalPort(raw) {
22620
+ const value = raw?.trim();
22621
+ if (!value) return void 0;
22622
+ const port = Number(value);
22623
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
22624
+ throw new Error(`${AGENT_MIGRATION_TRANSPORT_PORT_ENV} must be an integer from 0 to 65535`);
22625
+ }
22626
+ return port;
22627
+ }
22628
+ function formatListenAddress(address) {
22629
+ if (address.includes(":") && !address.startsWith("[")) return `[${address}]`;
22630
+ return address;
22631
+ }
22632
+ function extractBearerToken(req) {
22633
+ const directHeader = singleHeader(req.headers["x-raft-migration-token"]);
22634
+ if (directHeader) return directHeader;
22635
+ const authorization = singleHeader(req.headers.authorization);
22636
+ if (!authorization) return null;
22637
+ const match = /^Bearer\s+(.+)$/i.exec(authorization);
22638
+ return match?.[1] ?? null;
22639
+ }
22640
+ function singleHeader(value) {
22641
+ if (!value) return null;
22642
+ return Array.isArray(value) ? value[0] ?? null : value;
22643
+ }
22644
+ function sendJson(res, status, body, headers = {}) {
22645
+ const payload = JSON.stringify(body);
22646
+ res.writeHead(status, {
22647
+ "Content-Type": "application/json",
22648
+ "Content-Length": Buffer.byteLength(payload).toString(),
22649
+ ...headers
22650
+ });
22651
+ res.end(payload);
22652
+ }
22653
+ function readJsonRequestBody(req, maxBytes) {
22654
+ return new Promise((resolve, reject) => {
22655
+ const chunks = [];
22656
+ let totalBytes = 0;
22657
+ let rejected = false;
22658
+ req.on("data", (chunk) => {
22659
+ if (rejected) return;
22660
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
22661
+ totalBytes += buffer.byteLength;
22662
+ if (totalBytes > maxBytes) {
22663
+ rejected = true;
22664
+ reject(new MigrationBundleStreamError("migration_control_payload_too_large"));
22665
+ return;
22666
+ }
22667
+ chunks.push(buffer);
22668
+ });
22669
+ req.on("error", reject);
22670
+ req.on("end", () => {
22671
+ if (rejected) return;
22672
+ try {
22673
+ const text = Buffer.concat(chunks).toString("utf8");
22674
+ resolve(JSON.parse(text));
22675
+ } catch {
22676
+ reject(new MigrationBundleStreamError("migration_control_invalid_json"));
22677
+ }
22678
+ });
22679
+ });
22680
+ }
22681
+ function hashToken(token) {
22682
+ return sha256Buffer(Buffer.from(token, "utf8"));
22683
+ }
22684
+ function verifyTokenHash(token, expectedHashHex) {
22685
+ const actual = Buffer.from(hashToken(token), "hex");
22686
+ const expected = Buffer.from(expectedHashHex, "hex");
22687
+ if (actual.byteLength !== expected.byteLength) return false;
22688
+ return timingSafeEqual(actual, expected);
22689
+ }
22690
+ function sha256Buffer(buffer) {
22691
+ return createHash7("sha256").update(buffer).digest("hex");
22692
+ }
22693
+ function canonicalJsonBuffer(value) {
22694
+ return Buffer.from(canonicalJson(value), "utf8");
22695
+ }
22696
+ function canonicalJson(value) {
22697
+ return JSON.stringify(sortJsonValue(value));
22698
+ }
22699
+ function sortJsonValue(value) {
22700
+ if (Array.isArray(value)) return value.map(sortJsonValue);
22701
+ if (!value || typeof value !== "object") return value;
22702
+ return Object.keys(value).sort().reduce((result, key) => {
22703
+ result[key] = sortJsonValue(value[key]);
22704
+ return result;
22705
+ }, {});
22706
+ }
22707
+ function onceDrain(res) {
22708
+ return new Promise((resolve) => res.once("drain", resolve));
22709
+ }
22710
+
22711
+ // src/agentMigrationExport.ts
22712
+ import { createHash as createHash8 } from "crypto";
22713
+ import { createReadStream } from "fs";
22714
+ import { lstat as lstat2, readdir as readdir4, readFile as readFile3, readlink } from "fs/promises";
22715
+ import path19 from "path";
22716
+ var AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION = "agent-bundle/v1";
22717
+ var REGENERABLE_DIRECTORY_NAMES = [
22718
+ ".venv",
22719
+ "__pycache__",
22720
+ "dist",
22721
+ "node_modules",
22722
+ "target"
22723
+ ];
22724
+ var SECRET_FILE_NAMES = /* @__PURE__ */ new Set([
22725
+ ".env",
22726
+ ".env.local",
22727
+ ".env.development",
22728
+ ".env.production",
22729
+ ".env.test",
22730
+ "credentials.json",
22731
+ "credential.json"
22732
+ ]);
22733
+ var ENV_KEY_PATTERN = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
22734
+ async function buildAgentMigrationExportManifest(input) {
22735
+ const slockHome = path19.resolve(input.slockHome);
22736
+ const workspace = path19.resolve(input.workspacePath ?? path19.join(slockHome, "agents", input.agentId));
22737
+ const proposedIncludes = normalizeProposalPaths(input.cooperativeManifest?.include, workspace, "include");
22738
+ const proposedRegenerableExcludes = normalizeProposalPaths(input.cooperativeManifest?.exclude_regenerable, workspace, "exclude_regenerable");
22739
+ const cleanedProposal = normalizeProposalPaths(input.cooperativeManifest?.cleaned, workspace, "cleaned");
22740
+ const state = {
22741
+ workspace,
22742
+ files: [],
22743
+ excludedRegenerable: [],
22744
+ promotedIncludes: [],
22745
+ proposalRefusals: [
22746
+ ...proposedIncludes.refusals,
22747
+ ...proposedRegenerableExcludes.refusals,
22748
+ ...cleanedProposal.refusals
22749
+ ],
22750
+ unreachable: [],
22751
+ secretsDisclosed: [],
22752
+ seenWorkspacePaths: /* @__PURE__ */ new Set(),
22753
+ explicitIncludePaths: new Set(proposedIncludes.paths)
22754
+ };
22755
+ await walkWorkspace(workspace, "", new Set(proposedRegenerableExcludes.paths), state, { forceIncludeRegenerable: false });
22756
+ for (const requestedPath of proposedIncludes.paths) {
22757
+ if (!state.seenWorkspacePaths.has(requestedPath)) {
22758
+ await includeWorkspacePath(requestedPath, state, { forceIncludeRegenerable: true });
22759
+ }
22760
+ }
22761
+ for (const requestedPath of proposedRegenerableExcludes.paths) {
22762
+ if (isRegenerablePath(requestedPath)) continue;
22763
+ state.promotedIncludes.push({ path: requestedPath, reason: "exclude_not_regenerable" });
22764
+ await includeWorkspacePath(requestedPath, state, { forceIncludeRegenerable: true });
22765
+ }
22766
+ const crossTreeRefs = await buildCrossTreeRefs(input.runtimeSessionRefs ?? []);
22767
+ const secretDisclosureProposal = normalizeProposalPaths(input.cooperativeManifest?.secrets_disclosed, workspace, "secrets_disclosed");
22768
+ state.proposalRefusals.push(...secretDisclosureProposal.refusals);
22769
+ return {
22770
+ schemaVersion: AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
22771
+ agentId: input.agentId,
22772
+ mode: input.mode,
22773
+ createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
22774
+ roots: {
22775
+ slockHome,
22776
+ workspace
22777
+ },
22778
+ defaults: {
22779
+ unknownFiles: "include",
22780
+ excludePolicy: "regenerable_only",
22781
+ regenerableDirectoryNames: [...REGENERABLE_DIRECTORY_NAMES]
22782
+ },
22783
+ files: sortBundleEntries(state.files),
22784
+ excludedRegenerable: sortByPath(state.excludedRegenerable),
22785
+ promotedIncludes: sortByPath(state.promotedIncludes),
22786
+ proposalRefusals: sortByPath(state.proposalRefusals),
22787
+ unreachable: sortByPath(state.unreachable),
22788
+ secretsDisclosed: sortByPath([
22789
+ ...state.secretsDisclosed,
22790
+ ...normalizeProvidedSecretDisclosures(secretDisclosureProposal.paths)
22791
+ ]),
22792
+ cleaned: cleanedProposal.paths,
22793
+ crossTreeRefs: crossTreeRefs.sort((a, b) => a.bundlePath.localeCompare(b.bundlePath))
22794
+ };
22795
+ }
22796
+ async function walkWorkspace(root, relativeDir, proposedRegenerableExcludes, state, opts) {
22797
+ const absoluteDir = path19.join(root, relativeDir);
22798
+ let entries;
22799
+ try {
22800
+ entries = await readdir4(absoluteDir, { withFileTypes: true });
22801
+ } catch {
22802
+ state.unreachable.push({
22803
+ path: relativeDir || ".",
22804
+ sourcePath: absoluteDir,
22805
+ reason: "read_error"
22806
+ });
22807
+ return;
22808
+ }
22809
+ entries.sort((a, b) => a.name.localeCompare(b.name));
22810
+ for (const entry of entries) {
22811
+ const relativePath = toPosixPath(path19.join(relativeDir, entry.name));
22812
+ if (entry.isDirectory()) {
22813
+ if (!opts.forceIncludeRegenerable && isRegenerablePath(relativePath)) {
22814
+ if (state.explicitIncludePaths.has(relativePath)) {
22815
+ await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, { forceIncludeRegenerable: true });
22816
+ continue;
22817
+ }
22818
+ if (hasExplicitIncludeDescendant(relativePath, state.explicitIncludePaths)) {
22819
+ await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, opts);
22820
+ continue;
22821
+ }
22822
+ state.excludedRegenerable.push({
22823
+ path: relativePath,
22824
+ reason: proposedRegenerableExcludes.has(relativePath) ? "cooperative_exclude_regenerable" : "regenerable_default",
22825
+ regenerableHint: `${entry.name} is treated as rebuildable/installable state and is not bundled by default`
22826
+ });
22827
+ continue;
22828
+ }
22829
+ await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, opts);
22830
+ continue;
22831
+ }
22832
+ await includeWorkspacePath(relativePath, state, { forceIncludeRegenerable: opts.forceIncludeRegenerable });
22833
+ }
22834
+ }
22835
+ async function includeWorkspacePath(workspaceRelativePath, state, opts) {
22836
+ const normalizedRelativePath = normalizeRelativePath(workspaceRelativePath);
22837
+ if (state.seenWorkspacePaths.has(normalizedRelativePath)) return;
22838
+ const sourcePath = path19.join(state.workspace, normalizedRelativePath);
22839
+ let stat4;
22840
+ try {
22841
+ stat4 = await lstat2(sourcePath);
22842
+ } catch {
22049
22843
  state.unreachable.push({
22050
22844
  path: normalizedRelativePath,
22051
22845
  sourcePath,
@@ -22129,7 +22923,7 @@ async function buildCrossTreeRefs(refs) {
22129
22923
  return result;
22130
22924
  }
22131
22925
  async function sha256File(filePath) {
22132
- const hash = createHash7("sha256");
22926
+ const hash = createHash8("sha256");
22133
22927
  await new Promise((resolve, reject) => {
22134
22928
  const stream = createReadStream(filePath);
22135
22929
  stream.on("data", (chunk) => hash.update(chunk));
@@ -22235,240 +23029,239 @@ function sortByPath(entries) {
22235
23029
  return [...entries].sort((a, b) => a.path.localeCompare(b.path));
22236
23030
  }
22237
23031
 
22238
- // src/agentMigrationHttpTransport.ts
22239
- import { createHash as createHash8, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
22240
- import http2 from "http";
22241
- var MigrationBundleStreamError = class extends Error {
22242
- constructor(code) {
22243
- super(code);
22244
- this.code = code;
22245
- }
22246
- };
22247
- var AgentMigrationGrantRegistry = class {
22248
- grants = /* @__PURE__ */ new Map();
22249
- now;
22250
- constructor(now = () => /* @__PURE__ */ new Date()) {
22251
- this.now = now;
22252
- }
22253
- createGrant(input) {
22254
- const token = input.token ?? randomBytes2(32).toString("base64url");
22255
- const manifestSha256 = sha256Buffer(canonicalJsonBuffer(input.manifest));
22256
- const grant = {
22257
- grantId: input.grantId ?? randomBytes2(16).toString("hex"),
22258
- tokenHash: hashToken(token),
22259
- expiresAt: input.expiresAt,
22260
- oneTimeUse: input.oneTimeUse ?? true,
22261
- state: "issued",
22262
- manifest: input.manifest,
22263
- manifestSha256,
22264
- bundleEtag: `"agent-migration-${manifestSha256}"`
22265
- };
22266
- this.grants.set(grant.grantId, grant);
22267
- return { grant, token };
22268
- }
22269
- get(grantId) {
22270
- return this.grants.get(grantId);
22271
- }
22272
- authenticate(grantId, token) {
22273
- const grant = this.grants.get(grantId);
22274
- if (!grant || !token || !verifyTokenHash(token, grant.tokenHash)) {
22275
- return { ok: false, status: 401, code: "migration_grant_auth_failed" };
22276
- }
22277
- if (grant.state === "revoked" || grant.state === "consumed") {
22278
- return { ok: false, status: 410, code: "migration_grant_unavailable" };
22279
- }
22280
- if (this.now().getTime() >= grant.expiresAt.getTime()) {
22281
- grant.state = "expired";
22282
- return { ok: false, status: 410, code: "migration_grant_unavailable" };
22283
- }
22284
- return { ok: true, grant };
22285
- }
22286
- revokeAll() {
22287
- for (const grant of this.grants.values()) {
22288
- if (grant.state !== "consumed" && grant.state !== "expired") {
22289
- grant.state = "revoked";
22290
- }
22291
- }
23032
+ // src/agentMigrationImport.ts
23033
+ import { createHash as createHash9 } from "crypto";
23034
+ import { createReadStream as createReadStream2 } from "fs";
23035
+ import { access as access2, cp, lstat as lstat3, mkdir as mkdir3, readlink as readlink2, rename, writeFile as writeFile3 } from "fs/promises";
23036
+ import path20 from "path";
23037
+ async function buildAgentMigrationAdoptPlan(input) {
23038
+ const slockHome = path20.resolve(input.slockHome);
23039
+ let manifest;
23040
+ if (input.sourceKind === "orphan_directory" && !input.manifest) {
23041
+ manifest = await buildAgentMigrationExportManifest({
23042
+ agentId: input.agentId,
23043
+ slockHome,
23044
+ workspacePath: path20.resolve(input.orphanWorkspacePath),
23045
+ mode: "forensic",
23046
+ now: input.now
23047
+ });
23048
+ } else if (input.manifest) {
23049
+ manifest = input.manifest;
23050
+ } else {
23051
+ throw new Error("MIGRATION_MANIFEST_MISSING");
22292
23052
  }
22293
- };
22294
- function createAgentMigrationHttpTransport(options = {}) {
22295
- const registry = new AgentMigrationGrantRegistry(options.now);
22296
- const bundleStreamFactory = options.bundleStreamFactory ?? defaultBundleStream;
22297
- const server = http2.createServer((req, res) => {
22298
- void handleMigrationRequest(req, res, registry, bundleStreamFactory);
23053
+ assertManifestIdentity({
23054
+ manifest,
23055
+ expectedAgentId: input.sourceKind === "orphan_directory" ? input.agentId : manifest.agentId
22299
23056
  });
23057
+ const sourceWorkspacePath = path20.resolve(
23058
+ input.sourceKind === "staged_bundle" ? input.stagingWorkspacePath : input.orphanWorkspacePath
23059
+ );
23060
+ const finalWorkspacePath = path20.resolve(input.finalWorkspacePath ?? path20.join(slockHome, "agents", manifest.agentId));
23061
+ const reportPath = path20.resolve(input.reportPath ?? path20.join(
23062
+ slockHome,
23063
+ "migrations",
23064
+ sanitizePathSegment(input.generation.grantKey),
23065
+ "arrival-report.json"
23066
+ ));
23067
+ const manifestSha256 = hashAgentMigrationManifest(manifest);
23068
+ if (input.manifestSha256 && input.manifestSha256 !== manifestSha256) {
23069
+ throw new Error("MIGRATION_MANIFEST_SHA_MISMATCH");
23070
+ }
22300
23071
  return {
22301
- grants: registry,
22302
- server,
22303
- listen: (port = 0, host = "127.0.0.1") => new Promise((resolve, reject) => {
22304
- server.once("error", reject);
22305
- server.listen(port, host, () => {
22306
- server.off("error", reject);
22307
- const address = server.address();
22308
- if (!address || typeof address === "string") {
22309
- reject(new Error("migration transport listen did not produce a TCP address"));
22310
- return;
22311
- }
22312
- resolve({ url: `http://${address.address}:${address.port}` });
22313
- });
22314
- }),
22315
- close: () => new Promise((resolve, reject) => {
22316
- registry.revokeAll();
22317
- server.close((err) => {
22318
- if (err) reject(err);
22319
- else resolve();
22320
- });
22321
- })
23072
+ sourceKind: input.sourceKind,
23073
+ agentId: manifest.agentId,
23074
+ slockHome,
23075
+ sourceWorkspacePath,
23076
+ finalWorkspacePath,
23077
+ reportPath,
23078
+ manifest,
23079
+ manifestSha256,
23080
+ generation: input.generation,
23081
+ attestationNonce: input.attestationNonce,
23082
+ observedAttestationNonce: input.observedAttestationNonce
22322
23083
  };
22323
23084
  }
22324
- async function handleMigrationRequest(req, res, registry, bundleStreamFactory) {
22325
- const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22326
- const match = /^\/migration\/([^/]+)\/([^/]+)(?:\/([^/]+))?$/.exec(parsed.pathname);
22327
- if (!match) {
22328
- sendJson(res, 404, { code: "migration_route_not_found" });
22329
- return;
23085
+ async function verifyAgentMigrationAdoptPlan(plan) {
23086
+ assertGeneration(plan.generation);
23087
+ assertManifestIdentity({ manifest: plan.manifest, expectedAgentId: plan.agentId });
23088
+ const actualManifestSha256 = hashAgentMigrationManifest(plan.manifest);
23089
+ if (actualManifestSha256 !== plan.manifestSha256) {
23090
+ throw new Error("MIGRATION_MANIFEST_SHA_MISMATCH");
23091
+ }
23092
+ let totalBytes = 0;
23093
+ let fileCount = 0;
23094
+ for (const entry of plan.manifest.files) {
23095
+ await verifyManifestFileEntry(plan.sourceWorkspacePath, entry);
23096
+ if (entry.kind === "file") {
23097
+ fileCount += 1;
23098
+ totalBytes += entry.sizeBytes ?? 0;
23099
+ }
23100
+ }
23101
+ return { fileCount, totalBytes };
23102
+ }
23103
+ async function executeAgentMigrationAdoptPlan(plan, rebind, now = currentDate()) {
23104
+ assertGeneration(plan.generation);
23105
+ await assertFinalWorkspaceAvailable(plan.sourceWorkspacePath, plan.finalWorkspacePath);
23106
+ const verified = await verifyAgentMigrationAdoptPlan(plan);
23107
+ let migrationGeneration = plan.generation.migrationGeneration;
23108
+ migrationGeneration = await assertRebindResult(plan, migrationGeneration, await rebind.startTransfer({
23109
+ grantKey: plan.generation.grantKey,
23110
+ migrationGeneration
23111
+ }));
23112
+ await placeWorkspace(plan.sourceWorkspacePath, plan.finalWorkspacePath);
23113
+ migrationGeneration = await assertRebindResult(plan, migrationGeneration, await rebind.flipMachine({
23114
+ grantKey: plan.generation.grantKey,
23115
+ migrationGeneration
23116
+ }));
23117
+ const report = {
23118
+ schemaVersion: "agent-arrival/v1",
23119
+ agentId: plan.agentId,
23120
+ sourceKind: plan.sourceKind,
23121
+ grantKey: plan.generation.grantKey,
23122
+ migrationGeneration,
23123
+ sourceMachineId: plan.generation.sourceMachineId,
23124
+ targetMachineId: plan.generation.targetMachineId,
23125
+ manifestSha256: plan.manifestSha256,
23126
+ finalWorkspacePath: plan.finalWorkspacePath,
23127
+ verified,
23128
+ attestation: {
23129
+ noncePresent: Boolean(plan.attestationNonce),
23130
+ nonceVerified: Boolean(plan.attestationNonce && plan.observedAttestationNonce === plan.attestationNonce)
23131
+ },
23132
+ arrivedAt: now.toISOString()
23133
+ };
23134
+ const reportSha256 = await writeArrivalReport(plan.reportPath, report);
23135
+ await assertRebindResult(plan, migrationGeneration, await rebind.markArrived({
23136
+ grantKey: plan.generation.grantKey,
23137
+ migrationGeneration,
23138
+ reportPath: plan.reportPath,
23139
+ reportSha256
23140
+ }));
23141
+ return { report, reportPath: plan.reportPath, reportSha256 };
23142
+ }
23143
+ function hashAgentMigrationManifest(manifest) {
23144
+ return sha256Buffer2(Buffer.from(canonicalJson2(manifest), "utf8"));
23145
+ }
23146
+ function assertManifestIdentity(input) {
23147
+ if (input.manifest.schemaVersion !== AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION) {
23148
+ throw new Error("MIGRATION_MANIFEST_SCHEMA_UNSUPPORTED");
22330
23149
  }
22331
- const [, grantId, resource, resourceId] = match;
22332
- const auth = registry.authenticate(decodeURIComponent(grantId), extractBearerToken(req));
22333
- if (!auth.ok) {
22334
- sendJson(res, auth.status, { code: auth.code });
22335
- return;
23150
+ if (input.manifest.agentId !== input.expectedAgentId) {
23151
+ throw new Error("MIGRATION_MANIFEST_AGENT_MISMATCH");
22336
23152
  }
22337
- if (resource === "manifest" && req.method === "GET" && !resourceId) {
22338
- sendJson(res, 200, {
22339
- manifestSha256: auth.grant.manifestSha256,
22340
- manifest: auth.grant.manifest
22341
- }, {
22342
- "X-Raft-Manifest-Sha": auth.grant.manifestSha256,
22343
- ETag: `"manifest-${auth.grant.manifestSha256}"`
22344
- });
22345
- return;
23153
+ }
23154
+ function assertGeneration(generation) {
23155
+ if (generation.localMachineId !== generation.targetMachineId) {
23156
+ throw new Error("MIGRATION_TARGET_MACHINE_MISMATCH");
22346
23157
  }
22347
- if (resource === "bundle.tar" && !resourceId) {
22348
- if (req.method === "HEAD") {
22349
- sendHead(res, auth.grant);
22350
- return;
22351
- }
22352
- if (req.method === "GET") {
22353
- await streamBundle(res, auth.grant, bundleStreamFactory);
22354
- return;
22355
- }
23158
+ if (generation.sourceMachineId === generation.targetMachineId) {
23159
+ throw new Error("MIGRATION_SOURCE_TARGET_MACHINE_MATCH");
22356
23160
  }
22357
- if (resource === "chunk" && req.method === "GET" && resourceId) {
22358
- sendJson(res, 501, { code: "migration_chunk_not_implemented" });
22359
- return;
23161
+ if (!generation.grantKey || !generation.migrationGeneration) {
23162
+ throw new Error("MIGRATION_GENERATION_MISSING");
22360
23163
  }
22361
- sendJson(res, 405, { code: "migration_method_not_allowed" });
22362
23164
  }
22363
- function sendHead(res, grant) {
22364
- res.statusCode = 200;
22365
- res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22366
- res.setHeader("ETag", grant.bundleEtag);
22367
- res.setHeader("Accept-Ranges", "none");
22368
- res.setHeader("X-Raft-Bundle-Size", "unknown");
22369
- res.end();
23165
+ async function assertRebindResult(plan, expectedGeneration, result) {
23166
+ if (!result) return expectedGeneration;
23167
+ if (result.grantKey && result.grantKey !== plan.generation.grantKey) {
23168
+ throw new Error("MIGRATION_REBIND_GRANT_MISMATCH");
23169
+ }
23170
+ if (result.migrationGeneration !== void 0 && result.migrationGeneration.length === 0) {
23171
+ throw new Error("MIGRATION_REBIND_GENERATION_MISMATCH");
23172
+ }
23173
+ if (result.sourceMachineId && result.sourceMachineId !== plan.generation.sourceMachineId) {
23174
+ throw new Error("MIGRATION_REBIND_SOURCE_MACHINE_MISMATCH");
23175
+ }
23176
+ if (result.targetMachineId && result.targetMachineId !== plan.generation.targetMachineId) {
23177
+ throw new Error("MIGRATION_REBIND_TARGET_MACHINE_MISMATCH");
23178
+ }
23179
+ return result.migrationGeneration ?? expectedGeneration;
22370
23180
  }
22371
- async function streamBundle(res, grant, bundleStreamFactory) {
22372
- if (grant.state === "streaming") {
22373
- sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22374
- return;
23181
+ async function verifyManifestFileEntry(sourceWorkspacePath, entry) {
23182
+ if (entry.source !== "workspace" || !entry.workspaceRelativePath) {
23183
+ throw new Error("MIGRATION_MANIFEST_ENTRY_NOT_WORKSPACE");
22375
23184
  }
22376
- if (grant.oneTimeUse && grant.state !== "issued" && grant.state !== "interrupted") {
22377
- sendJson(res, 410, { code: "migration_grant_unavailable" });
23185
+ const relative = normalizeWorkspaceRelativePath(entry.workspaceRelativePath);
23186
+ const entryPath = path20.join(sourceWorkspacePath, relative);
23187
+ const info = await lstat3(entryPath).catch(() => null);
23188
+ if (!info) throw new Error("MIGRATION_MANIFEST_FILE_MISSING");
23189
+ if (entry.kind === "symlink") {
23190
+ if (!info.isSymbolicLink()) throw new Error("MIGRATION_MANIFEST_KIND_MISMATCH");
23191
+ const linkTarget = await readlink2(entryPath);
23192
+ if (entry.linkTarget !== linkTarget) throw new Error("MIGRATION_MANIFEST_LINK_MISMATCH");
22378
23193
  return;
22379
23194
  }
22380
- let stream;
22381
- try {
22382
- stream = bundleStreamFactory(grant);
22383
- } catch (err) {
22384
- const code = err instanceof MigrationBundleStreamError ? err.code : "migration_bundle_stream_failed";
22385
- sendJson(res, 500, { code });
22386
- return;
23195
+ if (!info.isFile()) throw new Error("MIGRATION_MANIFEST_KIND_MISMATCH");
23196
+ if (entry.sizeBytes !== void 0 && entry.sizeBytes !== info.size) {
23197
+ throw new Error("MIGRATION_MANIFEST_FILE_SIZE_MISMATCH");
22387
23198
  }
22388
- grant.state = "streaming";
22389
- let completed = false;
22390
- res.on("close", () => {
22391
- if (!completed && grant.state === "streaming") {
22392
- grant.state = "interrupted";
22393
- }
22394
- });
22395
- try {
22396
- res.statusCode = 200;
22397
- res.setHeader("Content-Type", "application/x-tar");
22398
- res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22399
- res.setHeader("ETag", grant.bundleEtag);
22400
- for await (const chunk of stream) {
22401
- if (!res.write(chunk)) {
22402
- await onceDrain(res);
22403
- }
22404
- }
22405
- completed = true;
22406
- grant.state = "consumed";
22407
- res.end();
22408
- } catch {
22409
- if (!completed && grant.state === "streaming") {
22410
- grant.state = "interrupted";
22411
- }
22412
- if (!res.headersSent) {
22413
- sendJson(res, 500, { code: "migration_bundle_stream_failed" });
22414
- } else {
22415
- res.destroy();
22416
- }
23199
+ if (entry.sha256) {
23200
+ const actual = await sha256File2(entryPath);
23201
+ if (actual !== entry.sha256) throw new Error("MIGRATION_MANIFEST_FILE_SHA_MISMATCH");
22417
23202
  }
22418
23203
  }
22419
- function defaultBundleStream(grant) {
22420
- void grant;
22421
- throw new MigrationBundleStreamError("migration_bundle_stream_not_wired");
22422
- }
22423
- function extractBearerToken(req) {
22424
- const directHeader = singleHeader(req.headers["x-raft-migration-token"]);
22425
- if (directHeader) return directHeader;
22426
- const authorization = singleHeader(req.headers.authorization);
22427
- if (!authorization) return null;
22428
- const match = /^Bearer\s+(.+)$/i.exec(authorization);
22429
- return match?.[1] ?? null;
22430
- }
22431
- function singleHeader(value) {
22432
- if (!value) return null;
22433
- return Array.isArray(value) ? value[0] ?? null : value;
23204
+ function normalizeWorkspaceRelativePath(relativePath) {
23205
+ const normalized = path20.posix.normalize(relativePath.replaceAll("\\", "/"));
23206
+ if (normalized === "." || normalized.startsWith("../") || normalized === ".." || path20.isAbsolute(normalized)) {
23207
+ throw new Error("MIGRATION_MANIFEST_UNSAFE_PATH");
23208
+ }
23209
+ return normalized;
22434
23210
  }
22435
- function sendJson(res, status, body, headers = {}) {
22436
- const payload = JSON.stringify(body);
22437
- res.writeHead(status, {
22438
- "Content-Type": "application/json",
22439
- "Content-Length": Buffer.byteLength(payload).toString(),
22440
- ...headers
22441
- });
22442
- res.end(payload);
23211
+ async function assertFinalWorkspaceAvailable(sourceWorkspacePath, finalWorkspacePath) {
23212
+ if (path20.resolve(sourceWorkspacePath) === path20.resolve(finalWorkspacePath)) return;
23213
+ try {
23214
+ await access2(finalWorkspacePath);
23215
+ throw new Error("MIGRATION_WORKSPACE_ALREADY_EXISTS");
23216
+ } catch (err) {
23217
+ if (err instanceof Error && err.message === "MIGRATION_WORKSPACE_ALREADY_EXISTS") throw err;
23218
+ }
22443
23219
  }
22444
- function hashToken(token) {
22445
- return sha256Buffer(Buffer.from(token, "utf8"));
23220
+ async function placeWorkspace(sourceWorkspacePath, finalWorkspacePath) {
23221
+ if (path20.resolve(sourceWorkspacePath) === path20.resolve(finalWorkspacePath)) return;
23222
+ const stagingPath = `${finalWorkspacePath}.migration-${process.pid}-${Date.now()}`;
23223
+ await mkdir3(path20.dirname(finalWorkspacePath), { recursive: true });
23224
+ await cp(sourceWorkspacePath, stagingPath, { recursive: true, dereference: false, errorOnExist: true, force: false });
23225
+ await rename(stagingPath, finalWorkspacePath);
22446
23226
  }
22447
- function verifyTokenHash(token, expectedHashHex) {
22448
- const actual = Buffer.from(hashToken(token), "hex");
22449
- const expected = Buffer.from(expectedHashHex, "hex");
22450
- if (actual.byteLength !== expected.byteLength) return false;
22451
- return timingSafeEqual(actual, expected);
23227
+ async function writeArrivalReport(reportPath, report) {
23228
+ const payload = `${canonicalJson2(report)}
23229
+ `;
23230
+ await mkdir3(path20.dirname(reportPath), { recursive: true });
23231
+ await writeFile3(reportPath, payload, { mode: 384 });
23232
+ return sha256Buffer2(Buffer.from(payload, "utf8"));
22452
23233
  }
22453
- function sha256Buffer(buffer) {
22454
- return createHash8("sha256").update(buffer).digest("hex");
23234
+ async function sha256File2(filePath) {
23235
+ const hash = createHash9("sha256");
23236
+ const stream = createReadStream2(filePath);
23237
+ for await (const chunk of stream) {
23238
+ hash.update(chunk);
23239
+ }
23240
+ return hash.digest("hex");
22455
23241
  }
22456
- function canonicalJsonBuffer(value) {
22457
- return Buffer.from(canonicalJson(value), "utf8");
23242
+ function sha256Buffer2(buffer) {
23243
+ return createHash9("sha256").update(buffer).digest("hex");
22458
23244
  }
22459
- function canonicalJson(value) {
22460
- return JSON.stringify(sortJsonValue(value));
23245
+ function canonicalJson2(value) {
23246
+ return JSON.stringify(sortJsonValue2(value));
22461
23247
  }
22462
- function sortJsonValue(value) {
22463
- if (Array.isArray(value)) return value.map(sortJsonValue);
23248
+ function sortJsonValue2(value) {
23249
+ if (Array.isArray(value)) return value.map(sortJsonValue2);
22464
23250
  if (!value || typeof value !== "object") return value;
22465
23251
  return Object.keys(value).sort().reduce((result, key) => {
22466
- result[key] = sortJsonValue(value[key]);
23252
+ result[key] = sortJsonValue2(value[key]);
22467
23253
  return result;
22468
23254
  }, {});
22469
23255
  }
22470
- function onceDrain(res) {
22471
- return new Promise((resolve) => res.once("drain", resolve));
23256
+ function sanitizePathSegment(value) {
23257
+ return value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 128) || "migration";
23258
+ }
23259
+
23260
+ // src/secretFile.ts
23261
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
23262
+ import path21 from "path";
23263
+ function readSecretFileSync(filePath) {
23264
+ return readFileSync9(filePath, "utf8").trim();
22472
23265
  }
22473
23266
 
22474
23267
  // src/core.ts
@@ -22597,7 +23390,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
22597
23390
  spanAttrs: ["agentId", "event_kind", "runtime"]
22598
23391
  }
22599
23392
  };
22600
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
23393
+ var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key-file <path>";
22601
23394
  var RunnerCredentialMintError2 = class extends Error {
22602
23395
  code;
22603
23396
  retryable;
@@ -22636,9 +23429,18 @@ async function waitForRunnerCredentialRetry2() {
22636
23429
  function parseDaemonCliArgs(args) {
22637
23430
  let serverUrl = "";
22638
23431
  let apiKey = "";
23432
+ let apiKeyFile = process.env.SLOCK_DAEMON_API_KEY_FILE ?? "";
22639
23433
  for (let i = 0; i < args.length; i++) {
22640
23434
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
22641
23435
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
23436
+ if (args[i] === "--api-key-file" && args[i + 1]) apiKeyFile = args[++i];
23437
+ }
23438
+ if (!apiKey && apiKeyFile) {
23439
+ try {
23440
+ apiKey = readSecretFileSync(apiKeyFile);
23441
+ } catch {
23442
+ apiKey = "";
23443
+ }
22642
23444
  }
22643
23445
  if (!serverUrl || !apiKey) return null;
22644
23446
  return { serverUrl, apiKey };
@@ -22652,13 +23454,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
22652
23454
  }
22653
23455
  }
22654
23456
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
22655
- const thisDir = path20.dirname(fileURLToPath(moduleUrl));
22656
- const bundledDistPath = path20.resolve(thisDir, "cli", "index.js");
23457
+ const thisDir = path22.dirname(fileURLToPath(moduleUrl));
23458
+ const bundledDistPath = path22.resolve(thisDir, "cli", "index.js");
22657
23459
  try {
22658
23460
  accessSync(bundledDistPath);
22659
23461
  return bundledDistPath;
22660
23462
  } catch {
22661
- const workspaceDistPath = path20.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
23463
+ const workspaceDistPath = path22.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
22662
23464
  accessSync(workspaceDistPath);
22663
23465
  return workspaceDistPath;
22664
23466
  }
@@ -22672,7 +23474,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
22672
23474
  }
22673
23475
  async function runBundledSlockCli(argv) {
22674
23476
  process.argv = [process.execPath, "slock", ...argv];
22675
- await import("./dist-V7E6IFYX.js");
23477
+ await import("./dist-NS7NN3BV.js");
22676
23478
  }
22677
23479
  function detectRuntimes(tracer = noopTracer) {
22678
23480
  const ids = [];
@@ -22764,6 +23566,11 @@ function readPositiveIntegerEnv3(name, fallback) {
22764
23566
  if (!Number.isFinite(parsed) || parsed < 1) return fallback;
22765
23567
  return Math.floor(parsed);
22766
23568
  }
23569
+ function hasAgentMigrationHttpTransportListenConfig(env = process.env) {
23570
+ return Boolean(
23571
+ env[AGENT_MIGRATION_TRANSPORT_HOST_ENV]?.trim() || env[AGENT_MIGRATION_TRANSPORT_PORT_ENV]?.trim() || env[AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV]?.trim()
23572
+ );
23573
+ }
22767
23574
  function formatChannelTarget(msg) {
22768
23575
  return msg.message.channel_type === "dm" ? `dm:@${msg.message.channel_name}` : `#${msg.message.channel_name}`;
22769
23576
  }
@@ -22828,6 +23635,9 @@ var DaemonCore = class {
22828
23635
  agentManager;
22829
23636
  connection;
22830
23637
  reminderCache;
23638
+ migrationTransport;
23639
+ migrationTransportListenPromise = null;
23640
+ migrationTransportListening = false;
22831
23641
  tracer;
22832
23642
  injectedTracer;
22833
23643
  machineLock = null;
@@ -22851,6 +23661,7 @@ var DaemonCore = class {
22851
23661
  clock: options.reminderClock,
22852
23662
  onFire: (job) => this.onReminderFire(job)
22853
23663
  });
23664
+ this.migrationTransport = options.migrationTransport === void 0 ? hasAgentMigrationHttpTransportListenConfig() ? createAgentMigrationHttpTransport() : null : options.migrationTransport;
22854
23665
  let connection;
22855
23666
  this.agentsDataDir = options.dataDir ?? resolveSlockHomePath("agents", this.slockHome);
22856
23667
  const traceUploadDisabled = process.env.SLOCK_DAEMON_TRACE_UPLOAD_DISABLED === "1";
@@ -22881,7 +23692,7 @@ var DaemonCore = class {
22881
23692
  }
22882
23693
  resolveMachineStateRoot() {
22883
23694
  if (this.options.machineStateDir) return this.options.machineStateDir;
22884
- if (this.options.dataDir) return path20.join(path20.dirname(this.options.dataDir), "machines");
23695
+ if (this.options.dataDir) return path22.join(path22.dirname(this.options.dataDir), "machines");
22885
23696
  return resolveDefaultMachineStateRoot();
22886
23697
  }
22887
23698
  shouldEnableLocalTrace() {
@@ -22909,7 +23720,7 @@ var DaemonCore = class {
22909
23720
  sinks: [this.localTraceSink]
22910
23721
  }));
22911
23722
  this.agentManager.setTracer(this.tracer);
22912
- this.agentManager.setCliTransportTraceDir(path20.join(machineDir, "traces"));
23723
+ this.agentManager.setCliTransportTraceDir(path22.join(machineDir, "traces"));
22913
23724
  }
22914
23725
  installTraceBundleUploader(machineDir) {
22915
23726
  if (!this.shouldEnableLocalTrace()) return;
@@ -22959,9 +23770,11 @@ var DaemonCore = class {
22959
23770
  span.addEvent("daemon.machine_lock.acquired", { machine_dir_present: true });
22960
23771
  span.end("ok");
22961
23772
  }
23773
+ this.startMigrationTransport();
22962
23774
  try {
22963
23775
  this.connection.connect();
22964
23776
  } catch (err) {
23777
+ void this.stopMigrationTransport();
22965
23778
  this.traceBundleUploader?.stop();
22966
23779
  this.traceBundleUploader = null;
22967
23780
  this.machineLock.release();
@@ -23000,6 +23813,7 @@ var DaemonCore = class {
23000
23813
  shutdown_reason: shutdownReason
23001
23814
  });
23002
23815
  }
23816
+ await this.stopMigrationTransport();
23003
23817
  this.connection.disconnect();
23004
23818
  span.addEvent("daemon.connection.disconnect_requested");
23005
23819
  this.machineLock?.release();
@@ -23025,6 +23839,44 @@ var DaemonCore = class {
23025
23839
  });
23026
23840
  span.end(status);
23027
23841
  }
23842
+ startMigrationTransport() {
23843
+ if (!this.migrationTransport || this.migrationTransportListenPromise) return;
23844
+ this.migrationTransportListenPromise = this.migrationTransport.listen().then(({ url }) => {
23845
+ this.migrationTransportListening = true;
23846
+ logger.info(`[Slock Daemon] Agent migration HTTP transport listening: ${url}`);
23847
+ this.recordDaemonTrace("daemon.migration_transport.listen", {
23848
+ outcome: "listening",
23849
+ url_present: Boolean(url)
23850
+ });
23851
+ }).catch((err) => {
23852
+ this.migrationTransportListening = false;
23853
+ logger.error("[Slock Daemon] Agent migration HTTP transport failed to listen", err);
23854
+ this.recordDaemonTrace("daemon.migration_transport.listen", {
23855
+ outcome: "failed",
23856
+ error_class: err instanceof Error ? err.name : typeof err
23857
+ }, "error");
23858
+ });
23859
+ }
23860
+ async stopMigrationTransport() {
23861
+ if (!this.migrationTransport) return;
23862
+ const listenPromise = this.migrationTransportListenPromise;
23863
+ this.migrationTransportListenPromise = null;
23864
+ if (listenPromise) await listenPromise;
23865
+ if (!this.migrationTransportListening) return;
23866
+ try {
23867
+ await this.migrationTransport.close();
23868
+ logger.info("[Slock Daemon] Agent migration HTTP transport stopped");
23869
+ this.recordDaemonTrace("daemon.migration_transport.stop", { outcome: "stopped" });
23870
+ } catch (err) {
23871
+ logger.error("[Slock Daemon] Agent migration HTTP transport failed to stop", err);
23872
+ this.recordDaemonTrace("daemon.migration_transport.stop", {
23873
+ outcome: "failed",
23874
+ error_class: err instanceof Error ? err.name : typeof err
23875
+ }, "error");
23876
+ } finally {
23877
+ this.migrationTransportListening = false;
23878
+ }
23879
+ }
23028
23880
  withDaemonTraceScope(tracer) {
23029
23881
  return {
23030
23882
  startSpan: (name, options) => createTraceScopeTracer(tracer, this.daemonTraceScope(), {
@@ -23524,6 +24376,7 @@ var DaemonCore = class {
23524
24376
  agentId: job.ownerAgentId,
23525
24377
  reminderId: job.reminderId,
23526
24378
  version: job.version,
24379
+ // Accepted ambient clock: reminder telemetry reports the client wall-clock fire attempt.
23527
24380
  firedAtClient: (/* @__PURE__ */ new Date()).toISOString()
23528
24381
  });
23529
24382
  }
@@ -23641,10 +24494,20 @@ export {
23641
24494
  resolveWorkspaceDirectoryPath,
23642
24495
  scanWorkspaceDirectories,
23643
24496
  deleteWorkspaceDirectory,
23644
- AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
23645
- buildAgentMigrationExportManifest,
24497
+ AGENT_MIGRATION_TRANSPORT_HOST_ENV,
24498
+ AGENT_MIGRATION_TRANSPORT_PORT_ENV,
24499
+ AGENT_MIGRATION_TRANSPORT_PUBLIC_URL_ENV,
24500
+ AGENT_MIGRATION_CONTROL_SEAM_ENV,
23646
24501
  AgentMigrationGrantRegistry,
23647
24502
  createAgentMigrationHttpTransport,
24503
+ resolveAgentMigrationHttpTransportListenOptions,
24504
+ resolveAgentMigrationControlSeamEnabled,
24505
+ AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
24506
+ buildAgentMigrationExportManifest,
24507
+ buildAgentMigrationAdoptPlan,
24508
+ verifyAgentMigrationAdoptPlan,
24509
+ executeAgentMigrationAdoptPlan,
24510
+ hashAgentMigrationManifest,
23648
24511
  DAEMON_CORE_TRACE_ATTR_CONTRACTS,
23649
24512
  DAEMON_CLI_USAGE,
23650
24513
  parseDaemonCliArgs,