@botiverse/raft-daemon 0.72.3 → 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";
@@ -1989,23 +1989,13 @@ var integrationUpdateAppRegistrationOperationSchema = integrationAppDraftFieldsS
1989
1989
  unsafeDemoUrlOverride: z.boolean().optional(),
1990
1990
  draftHint: draftHintSchema
1991
1991
  });
1992
- var migrationExportOperationSchema = z.object({
1993
- type: z.literal("migration:export"),
1994
- targetComputer: idOrHandleSchema,
1995
- mode: z.enum(["cooperative", "forensic"]).default("cooperative"),
1996
- prepDeadlineMs: z.number().int().positive().optional(),
1997
- transferDeadlineMs: z.number().int().positive().optional(),
1998
- arrivalDeadlineMs: z.number().int().positive().optional(),
1999
- draftHint: draftHintSchema
2000
- });
2001
1992
  var actionCardActionSchema = z.discriminatedUnion("type", [
2002
1993
  channelCreateOperationSchema,
2003
1994
  agentCreateOperationSchema,
2004
1995
  channelAddMemberOperationSchema,
2005
1996
  integrationApproveAgentLoginOperationSchema,
2006
1997
  integrationRegisterAppOperationSchema,
2007
- integrationUpdateAppRegistrationOperationSchema,
2008
- migrationExportOperationSchema
1998
+ integrationUpdateAppRegistrationOperationSchema
2009
1999
  ]);
2010
2000
 
2011
2001
  // ../shared/src/agentApiContract.ts
@@ -2704,6 +2694,51 @@ var agentApiReminderResponseSchema = passthroughObject({
2704
2694
  var agentApiReminderLogResponseSchema = passthroughObject({
2705
2695
  events: z2.array(agentApiReminderEventSummarySchema)
2706
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
+ });
2707
2742
  function route(input) {
2708
2743
  return {
2709
2744
  ...input,
@@ -2951,6 +2986,46 @@ var agentApiContract = {
2951
2986
  request: { body: agentApiTaskUpdateStatusBodySchema },
2952
2987
  response: { body: agentApiTaskUpdateStatusResponseSchema }
2953
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
+ }),
2954
3029
  reminderList: route({
2955
3030
  key: "reminderList",
2956
3031
  method: "GET",
@@ -3258,6 +3333,14 @@ var AUTH_REFRESH_ROTATION_RETRY_RTT_BUDGET_MS = 1e3;
3258
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;
3259
3334
  var AUTH_REFRESH_ROTATION_REPLAY_GRACE_MARGIN_MS = AUTH_REFRESH_ROTATED_REPLAY_GRACE_MS - AUTH_REFRESH_ROTATION_LOSER_WORST_CHAIN_MS;
3260
3335
 
3336
+ // ../shared/src/clock.ts
3337
+ function currentTimeMs() {
3338
+ return Date.now();
3339
+ }
3340
+ function currentDate() {
3341
+ return new Date(currentTimeMs());
3342
+ }
3343
+
3261
3344
  // ../shared/src/agentInbox.ts
3262
3345
  function formatAgentInboxDelta(rows, options = {}) {
3263
3346
  const totalPendingMessages = options.totalPendingMessages;
@@ -3640,7 +3723,7 @@ var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
3640
3723
  var RUNTIMES = [
3641
3724
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
3642
3725
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
3643
- { id: "builtin", displayName: "Builtin Pi", binary: "", supported: true },
3726
+ { id: "builtin", displayName: "Built-in Pi", binary: "", supported: true },
3644
3727
  { id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
3645
3728
  // Kimi: prefer the in-process SDK (`kimi-sdk` → "Kimi Code") for new agents.
3646
3729
  // The legacy `kimi` (kimi-cli child-process) entry stays for backward compat
@@ -4338,8 +4421,8 @@ async function executeResponseRequest(url, init, {
4338
4421
  }
4339
4422
 
4340
4423
  // src/directUploadCapability.ts
4341
- function joinUrl(base, path21) {
4342
- return `${base.replace(/\/+$/, "")}${path21}`;
4424
+ function joinUrl(base, path23) {
4425
+ return `${base.replace(/\/+$/, "")}${path23}`;
4343
4426
  }
4344
4427
  function jsonHeaders(apiKey) {
4345
4428
  return {
@@ -5029,6 +5112,15 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
5029
5112
  return candidates.filter((candidate) => existsSync(candidate.path));
5030
5113
  }
5031
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
+
5032
5124
  // src/agentCredentialProxy.ts
5033
5125
  import { randomBytes } from "crypto";
5034
5126
  import http from "http";
@@ -6610,7 +6702,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
6610
6702
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
6611
6703
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
6612
6704
  var RAW_CREDENTIAL_ENV_DENYLIST = [
6613
- "SLOCK_AGENT_CREDENTIAL_KEY"
6705
+ "SLOCK_AGENT_TOKEN",
6706
+ "SLOCK_AGENT_CREDENTIAL_KEY",
6707
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
6614
6708
  ];
6615
6709
  var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
6616
6710
  "agent-token",
@@ -6965,7 +7059,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6965
7059
  SLOCK_SERVER_URL: ctx.config.serverUrl,
6966
7060
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
6967
7061
  };
6968
- delete spawnEnv.SLOCK_AGENT_TOKEN;
7062
+ scrubDaemonChildEnv(spawnEnv);
6969
7063
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
6970
7064
  delete spawnEnv[key];
6971
7065
  }
@@ -7533,7 +7627,7 @@ function requiresWindowsShell(command, platform = process.platform) {
7533
7627
  }
7534
7628
  function resolveCommandOnPath(command, deps = {}) {
7535
7629
  const platform = deps.platform ?? process.platform;
7536
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7630
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7537
7631
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7538
7632
  const existsSyncFn = deps.existsSyncFn ?? existsSync3;
7539
7633
  if (platform === "win32") {
@@ -7559,7 +7653,7 @@ function firstExistingPath(candidates, deps = {}) {
7559
7653
  return null;
7560
7654
  }
7561
7655
  function readCommandVersion(command, args = [], deps = {}) {
7562
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7656
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7563
7657
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7564
7658
  try {
7565
7659
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -7985,7 +8079,7 @@ function codexNotificationDiagnosticEvent(message) {
7985
8079
  return null;
7986
8080
  }
7987
8081
  const sessionId = codexMessageThreadId(message);
7988
- const path21 = boundedString(params.path);
8082
+ const path23 = boundedString(params.path);
7989
8083
  return {
7990
8084
  kind: "runtime_diagnostic",
7991
8085
  severity: "warning",
@@ -7993,7 +8087,7 @@ function codexNotificationDiagnosticEvent(message) {
7993
8087
  itemType: message.method,
7994
8088
  message: diagnosticMessage,
7995
8089
  ...details ? { details } : {},
7996
- ...path21 ? { path: path21 } : {},
8090
+ ...path23 ? { path: path23 } : {},
7997
8091
  ...params.range !== void 0 ? { range: params.range } : {},
7998
8092
  payloadBytes: payloadBytes(params),
7999
8093
  ...sessionId ? { sessionId } : {}
@@ -9643,11 +9737,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
9643
9737
  return parseCursorModelsOutput(String(result.stdout || ""));
9644
9738
  }
9645
9739
  function buildCursorModelProbeEnv(deps = {}) {
9646
- return withWindowsUserEnvironment({
9740
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
9647
9741
  ...deps.env ?? process.env,
9648
9742
  FORCE_COLOR: "0",
9649
9743
  NO_COLOR: "1"
9650
- }, deps);
9744
+ }, deps));
9651
9745
  }
9652
9746
  function runCursorModelsCommand() {
9653
9747
  return spawnSync("cursor-agent", ["models"], {
@@ -9703,7 +9797,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
9703
9797
  }
9704
9798
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
9705
9799
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
9706
- const env = deps.env ?? process.env;
9800
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
9707
9801
  const winPath = path8.win32;
9708
9802
  let geminiEntry = null;
9709
9803
  try {
@@ -9840,12 +9934,15 @@ var GeminiDriver = class {
9840
9934
  // src/drivers/kimi.ts
9841
9935
  import { randomUUID as randomUUID2 } from "crypto";
9842
9936
  import { spawn as spawn7 } from "child_process";
9843
- 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";
9844
9938
  import os4 from "os";
9845
9939
  import path9 from "path";
9846
9940
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
9847
9941
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
9848
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";
9849
9946
  function parseToolArguments(raw) {
9850
9947
  if (typeof raw !== "string") return raw;
9851
9948
  try {
@@ -9854,6 +9951,73 @@ function parseToolArguments(raw) {
9854
9951
  return raw;
9855
9952
  }
9856
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
+ }
9857
10021
  function resolveKimiSpawn(commandArgs, deps = {}) {
9858
10022
  return {
9859
10023
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -9877,7 +10041,25 @@ var KimiDriver = class {
9877
10041
  };
9878
10042
  model = {
9879
10043
  detectedModelsVerifiedAs: "launchable",
9880
- 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
+ }
9881
10063
  };
9882
10064
  supportsStdinNotification = true;
9883
10065
  busyDeliveryMode = "direct";
@@ -9901,21 +10083,23 @@ var KimiDriver = class {
9901
10083
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
9902
10084
  ""
9903
10085
  ].join("\n"), "utf8");
10086
+ const launch = buildKimiLaunchOptions(ctx);
9904
10087
  const args = [
9905
10088
  "--wire",
9906
10089
  "--yolo",
9907
10090
  "--agent-file",
9908
10091
  agentFilePath,
9909
10092
  "--session",
9910
- this.sessionId
10093
+ this.sessionId,
10094
+ ...launch.args
9911
10095
  ];
9912
10096
  const launchRuntimeFields = runtimeConfigToLaunchFields(hydrateRuntimeConfig(ctx.config));
9913
10097
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
9914
10098
  args.push("--model", launchRuntimeFields.model);
9915
10099
  }
9916
10100
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
9917
- const launch = resolveKimiSpawn(args);
9918
- const proc = spawn7(launch.command, launch.args, {
10101
+ const spawnTarget = resolveKimiSpawn(args);
10102
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
9919
10103
  cwd: ctx.workingDirectory,
9920
10104
  stdio: ["pipe", "pipe", "pipe"],
9921
10105
  env: spawnEnv,
@@ -9923,7 +10107,7 @@ var KimiDriver = class {
9923
10107
  // and has an 8191-character command-line limit. Kimi's official
9924
10108
  // installer/uv entrypoint is an executable, so launch it directly and
9925
10109
  // keep prompts on stdin / files instead of routing through cmd.exe.
9926
- shell: launch.shell
10110
+ shell: spawnTarget.shell
9927
10111
  });
9928
10112
  proc.stdin?.write(JSON.stringify({
9929
10113
  jsonrpc: "2.0",
@@ -10036,14 +10220,9 @@ var KimiDriver = class {
10036
10220
  return detectKimiModels();
10037
10221
  }
10038
10222
  };
10039
- function detectKimiModels(home = os4.homedir()) {
10040
- const configPath = path9.join(home, ".kimi", "config.toml");
10041
- let raw;
10042
- try {
10043
- raw = readFileSync3(configPath, "utf8");
10044
- } catch {
10045
- return null;
10046
- }
10223
+ function detectKimiModels(home = os4.homedir(), opts = {}) {
10224
+ const raw = readKimiConfigSource(home, opts.env).raw;
10225
+ if (raw === null) return null;
10047
10226
  const models = [];
10048
10227
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
10049
10228
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -10722,7 +10901,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
10722
10901
  const platform = deps.platform ?? process.platform;
10723
10902
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
10724
10903
  const result = spawnSyncFn("opencode", ["models"], {
10725
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
10904
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
10726
10905
  encoding: "utf8",
10727
10906
  timeout: 5e3,
10728
10907
  shell: platform === "win32"
@@ -11018,7 +11197,9 @@ function createPiSdkEventMappingState(sessionId = null) {
11018
11197
  return {
11019
11198
  sessionId,
11020
11199
  sessionAnnounced: false,
11021
- sawTextDelta: false
11200
+ sawTextDelta: false,
11201
+ thinkingBuffers: /* @__PURE__ */ new Map(),
11202
+ announcedThinkingIndexes: /* @__PURE__ */ new Set()
11022
11203
  };
11023
11204
  }
11024
11205
  function buildPiSessionDir(workingDirectory) {
@@ -11233,10 +11414,40 @@ function pushSessionInitIfNeeded2(state, events) {
11233
11414
  state.sessionAnnounced = true;
11234
11415
  }
11235
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
+ }
11236
11429
  function mapPiAssistantMessageEvent(assistantEvent, state) {
11237
11430
  switch (assistantEvent.type) {
11238
- case "thinking_delta":
11239
- 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
+ }
11240
11451
  case "text_delta":
11241
11452
  if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
11242
11453
  state.sawTextDelta = true;
@@ -11247,8 +11458,6 @@ function mapPiAssistantMessageEvent(assistantEvent, state) {
11247
11458
  return !state.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? [{ kind: "text", text: assistantEvent.content }] : [];
11248
11459
  case "error":
11249
11460
  return [{ kind: "error", message: piErrorMessage(assistantEvent.error.errorMessage || assistantEvent.error) }];
11250
- case "thinking_start":
11251
- case "thinking_end":
11252
11461
  case "text_start":
11253
11462
  case "toolcall_start":
11254
11463
  case "toolcall_delta":
@@ -11292,6 +11501,8 @@ function mapPiSdkEventToParsedEvents(event, state) {
11292
11501
  case "message_start":
11293
11502
  if (event.message.role === "assistant") {
11294
11503
  state.sawTextDelta = false;
11504
+ state.thinkingBuffers.clear();
11505
+ state.announcedThinkingIndexes.clear();
11295
11506
  }
11296
11507
  return events;
11297
11508
  case "message_update":
@@ -14768,8 +14979,9 @@ function classifyTerminalFailure(ap) {
14768
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)) {
14769
14980
  const actionRequired = diagnostics.spanAttrs.runtime_error_action_required === true;
14770
14981
  return {
14771
- detail: actionRequired ? formatRuntimeLoginRequiredMessage(ap.driver.id) : text,
14772
- actionRequired
14982
+ detail: actionRequired ? formatRuntimeActionRequiredMessage(ap) : text,
14983
+ actionRequired,
14984
+ ...actionRequired ? { entries: buildRuntimeActionRequiredEntries(ap, text, diagnostics) } : {}
14773
14985
  };
14774
14986
  }
14775
14987
  }
@@ -14785,6 +14997,52 @@ function classifyStickyTerminalFailure(ap) {
14785
14997
  if (isRuntimeStartTimeoutText(terminalFailure.detail)) return null;
14786
14998
  return null;
14787
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
+ }
14788
15046
  function isAuthClassTerminalLine(text) {
14789
15047
  return buildRuntimeErrorDiagnosticEnvelope(text).spanAttrs.runtime_error_action_required === true;
14790
15048
  }
@@ -16986,7 +17244,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16986
17244
  agentId,
16987
17245
  "error",
16988
17246
  terminalFailureDetail.detail,
16989
- [{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
17247
+ terminalFailureDetail.entries ?? [{ kind: "text", text: `Error: ${terminalFailureDetail.detail}` }],
16990
17248
  ap.launchId,
16991
17249
  "runtime_error"
16992
17250
  );
@@ -17017,9 +17275,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17017
17275
  agentProcess.decisionErrorWindow.recordRuntimeError(startResult.error);
17018
17276
  }
17019
17277
  if (startResult.error && diagnostics?.spanAttrs.runtime_error_action_required === true) {
17020
- const visibleErrorMessage = formatRuntimeLoginRequiredMessage(agentProcess.driver.id);
17278
+ const terminalFailure = classifyTerminalFailure(agentProcess);
17279
+ const visibleErrorMessage = terminalFailure?.detail ?? formatRuntimeActionRequiredMessage(agentProcess);
17021
17280
  this.broadcastActivity(agentId, "error", visibleErrorMessage, [
17022
- { kind: "text", text: `Error: ${visibleErrorMessage}` }
17281
+ ...terminalFailure?.entries ?? [{ kind: "text", text: `Error: ${visibleErrorMessage}` }]
17023
17282
  ], agentProcess.launchId, "runtime_error");
17024
17283
  }
17025
17284
  throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
@@ -20182,11 +20441,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20182
20441
  ap.decisionErrorWindow.recordRuntimeError(event.message);
20183
20442
  }
20184
20443
  let visibleErrorMessage = event.message;
20444
+ let visibleErrorEntries;
20185
20445
  if (ap) {
20186
20446
  const runtimeErrorDiagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
20187
20447
  const runtimeErrorFingerprint = typeof runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint === "string" ? runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint : null;
20188
20448
  if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
20189
- visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
20449
+ visibleErrorMessage = formatRuntimeActionRequiredMessage(ap);
20190
20450
  }
20191
20451
  const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
20192
20452
  const backoffFailPoint = this.runtimeErrorDeliveryBackoffFailPointForTesting?.({
@@ -20212,6 +20472,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20212
20472
  if (fingerprintFence) {
20213
20473
  visibleErrorMessage = fingerprintFence.detail;
20214
20474
  }
20475
+ visibleErrorEntries = terminalFailure?.entries;
20215
20476
  this.noteRuntimeErrorDeliveryBackoff(agentId, ap, event.message, terminalFailure, stickyTerminalFailure, backoffReasonOverride);
20216
20477
  if (reduction.shouldDisableToolBoundaryFlush) {
20217
20478
  this.recordGatedSteeringEvent(agentId, ap, "disabled", {
@@ -20258,9 +20519,14 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
20258
20519
  }
20259
20520
  }
20260
20521
  }
20261
- this.broadcastActivity(agentId, "error", visibleErrorMessage, [
20262
- { kind: "text", text: `Error: ${visibleErrorMessage}` }
20263
- ], 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
+ );
20264
20530
  break;
20265
20531
  }
20266
20532
  }
@@ -20927,8 +21193,11 @@ var DaemonConnection = class {
20927
21193
  doConnect() {
20928
21194
  if (!this.shouldConnect) return;
20929
21195
  if (this.ws && this.ws.readyState !== WebSocket.CLOSED) return;
20930
- const wsUrl = this.options.serverUrl.replace(/^http/, "ws") + `/daemon/connect?key=${this.options.apiKey}`;
20931
- const wsOptions = buildWebSocketOptions(wsUrl, this.options.proxyEnv ?? process.env);
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
+ };
20932
21201
  logger.info(`[Daemon] Connecting to ${this.options.serverUrl}...`);
20933
21202
  if (wsOptions?.agent) {
20934
21203
  logger.info("[Daemon] Using configured proxy for WebSocket connection");
@@ -21924,136 +22193,651 @@ function assertLegacyDaemonKeyNotAdoptedByComputer(options) {
21924
22193
  if (match) throw new LegacyDaemonKeyAdoptedByComputerError(match);
21925
22194
  }
21926
22195
 
21927
- // src/agentMigrationExport.ts
21928
- import { createHash as createHash7 } from "crypto";
21929
- import { createReadStream } from "fs";
21930
- import { lstat as lstat2, readdir as readdir4, readFile as readFile3, readlink } from "fs/promises";
21931
- import path19 from "path";
21932
- var AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION = "agent-bundle/v1";
21933
- var REGENERABLE_DIRECTORY_NAMES = [
21934
- ".venv",
21935
- "__pycache__",
21936
- "dist",
21937
- "node_modules",
21938
- "target"
21939
- ];
21940
- var SECRET_FILE_NAMES = /* @__PURE__ */ new Set([
21941
- ".env",
21942
- ".env.local",
21943
- ".env.development",
21944
- ".env.production",
21945
- ".env.test",
21946
- "credentials.json",
21947
- "credential.json"
21948
- ]);
21949
- var ENV_KEY_PATTERN = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
21950
- async function buildAgentMigrationExportManifest(input) {
21951
- const slockHome = path19.resolve(input.slockHome);
21952
- const workspace = path19.resolve(input.workspacePath ?? path19.join(slockHome, "agents", input.agentId));
21953
- const proposedIncludes = normalizeProposalPaths(input.cooperativeManifest?.include, workspace, "include");
21954
- const proposedRegenerableExcludes = normalizeProposalPaths(input.cooperativeManifest?.exclude_regenerable, workspace, "exclude_regenerable");
21955
- const cleanedProposal = normalizeProposalPaths(input.cooperativeManifest?.cleaned, workspace, "cleaned");
21956
- const state = {
21957
- workspace,
21958
- files: [],
21959
- excludedRegenerable: [],
21960
- promotedIncludes: [],
21961
- proposalRefusals: [
21962
- ...proposedIncludes.refusals,
21963
- ...proposedRegenerableExcludes.refusals,
21964
- ...cleanedProposal.refusals
21965
- ],
21966
- unreachable: [],
21967
- secretsDisclosed: [],
21968
- seenWorkspacePaths: /* @__PURE__ */ new Set(),
21969
- explicitIncludePaths: new Set(proposedIncludes.paths)
21970
- };
21971
- await walkWorkspace(workspace, "", new Set(proposedRegenerableExcludes.paths), state, { forceIncludeRegenerable: false });
21972
- for (const requestedPath of proposedIncludes.paths) {
21973
- if (!state.seenWorkspacePaths.has(requestedPath)) {
21974
- await includeWorkspacePath(requestedPath, state, { forceIncludeRegenerable: true });
21975
- }
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;
21976
22209
  }
21977
- for (const requestedPath of proposedRegenerableExcludes.paths) {
21978
- if (isRegenerablePath(requestedPath)) continue;
21979
- state.promotedIncludes.push({ path: requestedPath, reason: "exclude_not_regenerable" });
21980
- 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;
21981
22216
  }
21982
- const crossTreeRefs = await buildCrossTreeRefs(input.runtimeSessionRefs ?? []);
21983
- const secretDisclosureProposal = normalizeProposalPaths(input.cooperativeManifest?.secrets_disclosed, workspace, "secrets_disclosed");
21984
- state.proposalRefusals.push(...secretDisclosureProposal.refusals);
21985
- return {
21986
- schemaVersion: AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
21987
- agentId: input.agentId,
21988
- mode: input.mode,
21989
- createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
21990
- roots: {
21991
- slockHome,
21992
- workspace
21993
- },
21994
- defaults: {
21995
- unknownFiles: "include",
21996
- excludePolicy: "regenerable_only",
21997
- regenerableDirectoryNames: [...REGENERABLE_DIRECTORY_NAMES]
21998
- },
21999
- files: sortBundleEntries(state.files),
22000
- excludedRegenerable: sortByPath(state.excludedRegenerable),
22001
- promotedIncludes: sortByPath(state.promotedIncludes),
22002
- proposalRefusals: sortByPath(state.proposalRefusals),
22003
- unreachable: sortByPath(state.unreachable),
22004
- secretsDisclosed: sortByPath([
22005
- ...state.secretsDisclosed,
22006
- ...normalizeProvidedSecretDisclosures(secretDisclosureProposal.paths)
22007
- ]),
22008
- cleaned: cleanedProposal.paths,
22009
- crossTreeRefs: crossTreeRefs.sort((a, b) => a.bundlePath.localeCompare(b.bundlePath))
22010
- };
22011
- }
22012
- async function walkWorkspace(root, relativeDir, proposedRegenerableExcludes, state, opts) {
22013
- const absoluteDir = path19.join(root, relativeDir);
22014
- let entries;
22015
- try {
22016
- entries = await readdir4(absoluteDir, { withFileTypes: true });
22017
- } catch {
22018
- state.unreachable.push({
22019
- path: relativeDir || ".",
22020
- sourcePath: absoluteDir,
22021
- reason: "read_error"
22022
- });
22023
- 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 };
22024
22233
  }
22025
- entries.sort((a, b) => a.name.localeCompare(b.name));
22026
- for (const entry of entries) {
22027
- const relativePath = toPosixPath(path19.join(relativeDir, entry.name));
22028
- if (entry.isDirectory()) {
22029
- if (!opts.forceIncludeRegenerable && isRegenerablePath(relativePath)) {
22030
- if (state.explicitIncludePaths.has(relativePath)) {
22031
- await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, { forceIncludeRegenerable: true });
22032
- continue;
22033
- }
22034
- if (hasExplicitIncludeDescendant(relativePath, state.explicitIncludePaths)) {
22035
- await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, opts);
22036
- continue;
22037
- }
22038
- state.excludedRegenerable.push({
22039
- path: relativePath,
22040
- reason: proposedRegenerableExcludes.has(relativePath) ? "cooperative_exclude_regenerable" : "regenerable_default",
22041
- regenerableHint: `${entry.name} is treated as rebuildable/installable state and is not bundled by default`
22042
- });
22043
- continue;
22044
- }
22045
- await walkWorkspace(root, relativePath, proposedRegenerableExcludes, state, opts);
22046
- continue;
22047
- }
22048
- await includeWorkspacePath(relativePath, state, { forceIncludeRegenerable: opts.forceIncludeRegenerable });
22234
+ get(grantId) {
22235
+ return this.grants.get(grantId);
22049
22236
  }
22050
- }
22051
- async function includeWorkspacePath(workspaceRelativePath, state, opts) {
22052
- const normalizedRelativePath = normalizeRelativePath(workspaceRelativePath);
22053
- if (state.seenWorkspacePaths.has(normalizedRelativePath)) return;
22054
- const sourcePath = path19.join(state.workspace, normalizedRelativePath);
22055
- let stat4;
22056
- try {
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 {
22057
22841
  stat4 = await lstat2(sourcePath);
22058
22842
  } catch {
22059
22843
  state.unreachable.push({
@@ -22139,7 +22923,7 @@ async function buildCrossTreeRefs(refs) {
22139
22923
  return result;
22140
22924
  }
22141
22925
  async function sha256File(filePath) {
22142
- const hash = createHash7("sha256");
22926
+ const hash = createHash8("sha256");
22143
22927
  await new Promise((resolve, reject) => {
22144
22928
  const stream = createReadStream(filePath);
22145
22929
  stream.on("data", (chunk) => hash.update(chunk));
@@ -22245,240 +23029,239 @@ function sortByPath(entries) {
22245
23029
  return [...entries].sort((a, b) => a.path.localeCompare(b.path));
22246
23030
  }
22247
23031
 
22248
- // src/agentMigrationHttpTransport.ts
22249
- import { createHash as createHash8, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
22250
- import http2 from "http";
22251
- var MigrationBundleStreamError = class extends Error {
22252
- constructor(code) {
22253
- super(code);
22254
- this.code = code;
22255
- }
22256
- };
22257
- var AgentMigrationGrantRegistry = class {
22258
- grants = /* @__PURE__ */ new Map();
22259
- now;
22260
- constructor(now = () => /* @__PURE__ */ new Date()) {
22261
- this.now = now;
22262
- }
22263
- createGrant(input) {
22264
- const token = input.token ?? randomBytes2(32).toString("base64url");
22265
- const manifestSha256 = sha256Buffer(canonicalJsonBuffer(input.manifest));
22266
- const grant = {
22267
- grantId: input.grantId ?? randomBytes2(16).toString("hex"),
22268
- tokenHash: hashToken(token),
22269
- expiresAt: input.expiresAt,
22270
- oneTimeUse: input.oneTimeUse ?? true,
22271
- state: "issued",
22272
- manifest: input.manifest,
22273
- manifestSha256,
22274
- bundleEtag: `"agent-migration-${manifestSha256}"`
22275
- };
22276
- this.grants.set(grant.grantId, grant);
22277
- return { grant, token };
22278
- }
22279
- get(grantId) {
22280
- return this.grants.get(grantId);
22281
- }
22282
- authenticate(grantId, token) {
22283
- const grant = this.grants.get(grantId);
22284
- if (!grant || !token || !verifyTokenHash(token, grant.tokenHash)) {
22285
- return { ok: false, status: 401, code: "migration_grant_auth_failed" };
22286
- }
22287
- if (grant.state === "revoked" || grant.state === "consumed") {
22288
- return { ok: false, status: 410, code: "migration_grant_unavailable" };
22289
- }
22290
- if (this.now().getTime() >= grant.expiresAt.getTime()) {
22291
- grant.state = "expired";
22292
- return { ok: false, status: 410, code: "migration_grant_unavailable" };
22293
- }
22294
- return { ok: true, grant };
22295
- }
22296
- revokeAll() {
22297
- for (const grant of this.grants.values()) {
22298
- if (grant.state !== "consumed" && grant.state !== "expired") {
22299
- grant.state = "revoked";
22300
- }
22301
- }
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");
22302
23052
  }
22303
- };
22304
- function createAgentMigrationHttpTransport(options = {}) {
22305
- const registry = new AgentMigrationGrantRegistry(options.now);
22306
- const bundleStreamFactory = options.bundleStreamFactory ?? defaultBundleStream;
22307
- const server = http2.createServer((req, res) => {
22308
- void handleMigrationRequest(req, res, registry, bundleStreamFactory);
23053
+ assertManifestIdentity({
23054
+ manifest,
23055
+ expectedAgentId: input.sourceKind === "orphan_directory" ? input.agentId : manifest.agentId
22309
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
+ }
22310
23071
  return {
22311
- grants: registry,
22312
- server,
22313
- listen: (port = 0, host = "127.0.0.1") => new Promise((resolve, reject) => {
22314
- server.once("error", reject);
22315
- server.listen(port, host, () => {
22316
- server.off("error", reject);
22317
- const address = server.address();
22318
- if (!address || typeof address === "string") {
22319
- reject(new Error("migration transport listen did not produce a TCP address"));
22320
- return;
22321
- }
22322
- resolve({ url: `http://${address.address}:${address.port}` });
22323
- });
22324
- }),
22325
- close: () => new Promise((resolve, reject) => {
22326
- registry.revokeAll();
22327
- server.close((err) => {
22328
- if (err) reject(err);
22329
- else resolve();
22330
- });
22331
- })
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
22332
23083
  };
22333
23084
  }
22334
- async function handleMigrationRequest(req, res, registry, bundleStreamFactory) {
22335
- const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
22336
- const match = /^\/migration\/([^/]+)\/([^/]+)(?:\/([^/]+))?$/.exec(parsed.pathname);
22337
- if (!match) {
22338
- sendJson(res, 404, { code: "migration_route_not_found" });
22339
- return;
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");
22340
23149
  }
22341
- const [, grantId, resource, resourceId] = match;
22342
- const auth = registry.authenticate(decodeURIComponent(grantId), extractBearerToken(req));
22343
- if (!auth.ok) {
22344
- sendJson(res, auth.status, { code: auth.code });
22345
- return;
23150
+ if (input.manifest.agentId !== input.expectedAgentId) {
23151
+ throw new Error("MIGRATION_MANIFEST_AGENT_MISMATCH");
22346
23152
  }
22347
- if (resource === "manifest" && req.method === "GET" && !resourceId) {
22348
- sendJson(res, 200, {
22349
- manifestSha256: auth.grant.manifestSha256,
22350
- manifest: auth.grant.manifest
22351
- }, {
22352
- "X-Raft-Manifest-Sha": auth.grant.manifestSha256,
22353
- ETag: `"manifest-${auth.grant.manifestSha256}"`
22354
- });
22355
- return;
23153
+ }
23154
+ function assertGeneration(generation) {
23155
+ if (generation.localMachineId !== generation.targetMachineId) {
23156
+ throw new Error("MIGRATION_TARGET_MACHINE_MISMATCH");
22356
23157
  }
22357
- if (resource === "bundle.tar" && !resourceId) {
22358
- if (req.method === "HEAD") {
22359
- sendHead(res, auth.grant);
22360
- return;
22361
- }
22362
- if (req.method === "GET") {
22363
- await streamBundle(res, auth.grant, bundleStreamFactory);
22364
- return;
22365
- }
23158
+ if (generation.sourceMachineId === generation.targetMachineId) {
23159
+ throw new Error("MIGRATION_SOURCE_TARGET_MACHINE_MATCH");
22366
23160
  }
22367
- if (resource === "chunk" && req.method === "GET" && resourceId) {
22368
- sendJson(res, 501, { code: "migration_chunk_not_implemented" });
22369
- return;
23161
+ if (!generation.grantKey || !generation.migrationGeneration) {
23162
+ throw new Error("MIGRATION_GENERATION_MISSING");
22370
23163
  }
22371
- sendJson(res, 405, { code: "migration_method_not_allowed" });
22372
23164
  }
22373
- function sendHead(res, grant) {
22374
- res.statusCode = 200;
22375
- res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22376
- res.setHeader("ETag", grant.bundleEtag);
22377
- res.setHeader("Accept-Ranges", "none");
22378
- res.setHeader("X-Raft-Bundle-Size", "unknown");
22379
- res.end();
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;
22380
23180
  }
22381
- async function streamBundle(res, grant, bundleStreamFactory) {
22382
- if (grant.state === "streaming") {
22383
- sendJson(res, 409, { code: "migration_bundle_stream_in_progress" });
22384
- return;
23181
+ async function verifyManifestFileEntry(sourceWorkspacePath, entry) {
23182
+ if (entry.source !== "workspace" || !entry.workspaceRelativePath) {
23183
+ throw new Error("MIGRATION_MANIFEST_ENTRY_NOT_WORKSPACE");
22385
23184
  }
22386
- if (grant.oneTimeUse && grant.state !== "issued" && grant.state !== "interrupted") {
22387
- 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");
22388
23193
  return;
22389
23194
  }
22390
- let stream;
22391
- try {
22392
- stream = bundleStreamFactory(grant);
22393
- } catch (err) {
22394
- const code = err instanceof MigrationBundleStreamError ? err.code : "migration_bundle_stream_failed";
22395
- sendJson(res, 500, { code });
22396
- return;
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");
22397
23198
  }
22398
- grant.state = "streaming";
22399
- let completed = false;
22400
- res.on("close", () => {
22401
- if (!completed && grant.state === "streaming") {
22402
- grant.state = "interrupted";
22403
- }
22404
- });
22405
- try {
22406
- res.statusCode = 200;
22407
- res.setHeader("Content-Type", "application/x-tar");
22408
- res.setHeader("X-Raft-Manifest-Sha", grant.manifestSha256);
22409
- res.setHeader("ETag", grant.bundleEtag);
22410
- for await (const chunk of stream) {
22411
- if (!res.write(chunk)) {
22412
- await onceDrain(res);
22413
- }
22414
- }
22415
- completed = true;
22416
- grant.state = "consumed";
22417
- res.end();
22418
- } catch {
22419
- if (!completed && grant.state === "streaming") {
22420
- grant.state = "interrupted";
22421
- }
22422
- if (!res.headersSent) {
22423
- sendJson(res, 500, { code: "migration_bundle_stream_failed" });
22424
- } else {
22425
- res.destroy();
22426
- }
23199
+ if (entry.sha256) {
23200
+ const actual = await sha256File2(entryPath);
23201
+ if (actual !== entry.sha256) throw new Error("MIGRATION_MANIFEST_FILE_SHA_MISMATCH");
22427
23202
  }
22428
23203
  }
22429
- function defaultBundleStream(grant) {
22430
- void grant;
22431
- throw new MigrationBundleStreamError("migration_bundle_stream_not_wired");
22432
- }
22433
- function extractBearerToken(req) {
22434
- const directHeader = singleHeader(req.headers["x-raft-migration-token"]);
22435
- if (directHeader) return directHeader;
22436
- const authorization = singleHeader(req.headers.authorization);
22437
- if (!authorization) return null;
22438
- const match = /^Bearer\s+(.+)$/i.exec(authorization);
22439
- return match?.[1] ?? null;
22440
- }
22441
- function singleHeader(value) {
22442
- if (!value) return null;
22443
- 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;
22444
23210
  }
22445
- function sendJson(res, status, body, headers = {}) {
22446
- const payload = JSON.stringify(body);
22447
- res.writeHead(status, {
22448
- "Content-Type": "application/json",
22449
- "Content-Length": Buffer.byteLength(payload).toString(),
22450
- ...headers
22451
- });
22452
- res.end(payload);
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
+ }
22453
23219
  }
22454
- function hashToken(token) {
22455
- 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);
22456
23226
  }
22457
- function verifyTokenHash(token, expectedHashHex) {
22458
- const actual = Buffer.from(hashToken(token), "hex");
22459
- const expected = Buffer.from(expectedHashHex, "hex");
22460
- if (actual.byteLength !== expected.byteLength) return false;
22461
- return timingSafeEqual(actual, expected);
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"));
22462
23233
  }
22463
- function sha256Buffer(buffer) {
22464
- 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");
22465
23241
  }
22466
- function canonicalJsonBuffer(value) {
22467
- return Buffer.from(canonicalJson(value), "utf8");
23242
+ function sha256Buffer2(buffer) {
23243
+ return createHash9("sha256").update(buffer).digest("hex");
22468
23244
  }
22469
- function canonicalJson(value) {
22470
- return JSON.stringify(sortJsonValue(value));
23245
+ function canonicalJson2(value) {
23246
+ return JSON.stringify(sortJsonValue2(value));
22471
23247
  }
22472
- function sortJsonValue(value) {
22473
- if (Array.isArray(value)) return value.map(sortJsonValue);
23248
+ function sortJsonValue2(value) {
23249
+ if (Array.isArray(value)) return value.map(sortJsonValue2);
22474
23250
  if (!value || typeof value !== "object") return value;
22475
23251
  return Object.keys(value).sort().reduce((result, key) => {
22476
- result[key] = sortJsonValue(value[key]);
23252
+ result[key] = sortJsonValue2(value[key]);
22477
23253
  return result;
22478
23254
  }, {});
22479
23255
  }
22480
- function onceDrain(res) {
22481
- 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();
22482
23265
  }
22483
23266
 
22484
23267
  // src/core.ts
@@ -22607,7 +23390,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
22607
23390
  spanAttrs: ["agentId", "event_kind", "runtime"]
22608
23391
  }
22609
23392
  };
22610
- 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>";
22611
23394
  var RunnerCredentialMintError2 = class extends Error {
22612
23395
  code;
22613
23396
  retryable;
@@ -22646,9 +23429,18 @@ async function waitForRunnerCredentialRetry2() {
22646
23429
  function parseDaemonCliArgs(args) {
22647
23430
  let serverUrl = "";
22648
23431
  let apiKey = "";
23432
+ let apiKeyFile = process.env.SLOCK_DAEMON_API_KEY_FILE ?? "";
22649
23433
  for (let i = 0; i < args.length; i++) {
22650
23434
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
22651
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
+ }
22652
23444
  }
22653
23445
  if (!serverUrl || !apiKey) return null;
22654
23446
  return { serverUrl, apiKey };
@@ -22662,13 +23454,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
22662
23454
  }
22663
23455
  }
22664
23456
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
22665
- const thisDir = path20.dirname(fileURLToPath(moduleUrl));
22666
- const bundledDistPath = path20.resolve(thisDir, "cli", "index.js");
23457
+ const thisDir = path22.dirname(fileURLToPath(moduleUrl));
23458
+ const bundledDistPath = path22.resolve(thisDir, "cli", "index.js");
22667
23459
  try {
22668
23460
  accessSync(bundledDistPath);
22669
23461
  return bundledDistPath;
22670
23462
  } catch {
22671
- const workspaceDistPath = path20.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
23463
+ const workspaceDistPath = path22.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
22672
23464
  accessSync(workspaceDistPath);
22673
23465
  return workspaceDistPath;
22674
23466
  }
@@ -22682,7 +23474,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
22682
23474
  }
22683
23475
  async function runBundledSlockCli(argv) {
22684
23476
  process.argv = [process.execPath, "slock", ...argv];
22685
- await import("./dist-YOKZZD4F.js");
23477
+ await import("./dist-NS7NN3BV.js");
22686
23478
  }
22687
23479
  function detectRuntimes(tracer = noopTracer) {
22688
23480
  const ids = [];
@@ -22774,6 +23566,11 @@ function readPositiveIntegerEnv3(name, fallback) {
22774
23566
  if (!Number.isFinite(parsed) || parsed < 1) return fallback;
22775
23567
  return Math.floor(parsed);
22776
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
+ }
22777
23574
  function formatChannelTarget(msg) {
22778
23575
  return msg.message.channel_type === "dm" ? `dm:@${msg.message.channel_name}` : `#${msg.message.channel_name}`;
22779
23576
  }
@@ -22838,6 +23635,9 @@ var DaemonCore = class {
22838
23635
  agentManager;
22839
23636
  connection;
22840
23637
  reminderCache;
23638
+ migrationTransport;
23639
+ migrationTransportListenPromise = null;
23640
+ migrationTransportListening = false;
22841
23641
  tracer;
22842
23642
  injectedTracer;
22843
23643
  machineLock = null;
@@ -22861,6 +23661,7 @@ var DaemonCore = class {
22861
23661
  clock: options.reminderClock,
22862
23662
  onFire: (job) => this.onReminderFire(job)
22863
23663
  });
23664
+ this.migrationTransport = options.migrationTransport === void 0 ? hasAgentMigrationHttpTransportListenConfig() ? createAgentMigrationHttpTransport() : null : options.migrationTransport;
22864
23665
  let connection;
22865
23666
  this.agentsDataDir = options.dataDir ?? resolveSlockHomePath("agents", this.slockHome);
22866
23667
  const traceUploadDisabled = process.env.SLOCK_DAEMON_TRACE_UPLOAD_DISABLED === "1";
@@ -22891,7 +23692,7 @@ var DaemonCore = class {
22891
23692
  }
22892
23693
  resolveMachineStateRoot() {
22893
23694
  if (this.options.machineStateDir) return this.options.machineStateDir;
22894
- 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");
22895
23696
  return resolveDefaultMachineStateRoot();
22896
23697
  }
22897
23698
  shouldEnableLocalTrace() {
@@ -22919,7 +23720,7 @@ var DaemonCore = class {
22919
23720
  sinks: [this.localTraceSink]
22920
23721
  }));
22921
23722
  this.agentManager.setTracer(this.tracer);
22922
- this.agentManager.setCliTransportTraceDir(path20.join(machineDir, "traces"));
23723
+ this.agentManager.setCliTransportTraceDir(path22.join(machineDir, "traces"));
22923
23724
  }
22924
23725
  installTraceBundleUploader(machineDir) {
22925
23726
  if (!this.shouldEnableLocalTrace()) return;
@@ -22969,9 +23770,11 @@ var DaemonCore = class {
22969
23770
  span.addEvent("daemon.machine_lock.acquired", { machine_dir_present: true });
22970
23771
  span.end("ok");
22971
23772
  }
23773
+ this.startMigrationTransport();
22972
23774
  try {
22973
23775
  this.connection.connect();
22974
23776
  } catch (err) {
23777
+ void this.stopMigrationTransport();
22975
23778
  this.traceBundleUploader?.stop();
22976
23779
  this.traceBundleUploader = null;
22977
23780
  this.machineLock.release();
@@ -23010,6 +23813,7 @@ var DaemonCore = class {
23010
23813
  shutdown_reason: shutdownReason
23011
23814
  });
23012
23815
  }
23816
+ await this.stopMigrationTransport();
23013
23817
  this.connection.disconnect();
23014
23818
  span.addEvent("daemon.connection.disconnect_requested");
23015
23819
  this.machineLock?.release();
@@ -23035,6 +23839,44 @@ var DaemonCore = class {
23035
23839
  });
23036
23840
  span.end(status);
23037
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
+ }
23038
23880
  withDaemonTraceScope(tracer) {
23039
23881
  return {
23040
23882
  startSpan: (name, options) => createTraceScopeTracer(tracer, this.daemonTraceScope(), {
@@ -23534,6 +24376,7 @@ var DaemonCore = class {
23534
24376
  agentId: job.ownerAgentId,
23535
24377
  reminderId: job.reminderId,
23536
24378
  version: job.version,
24379
+ // Accepted ambient clock: reminder telemetry reports the client wall-clock fire attempt.
23537
24380
  firedAtClient: (/* @__PURE__ */ new Date()).toISOString()
23538
24381
  });
23539
24382
  }
@@ -23651,10 +24494,20 @@ export {
23651
24494
  resolveWorkspaceDirectoryPath,
23652
24495
  scanWorkspaceDirectories,
23653
24496
  deleteWorkspaceDirectory,
23654
- AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
23655
- 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,
23656
24501
  AgentMigrationGrantRegistry,
23657
24502
  createAgentMigrationHttpTransport,
24503
+ resolveAgentMigrationHttpTransportListenOptions,
24504
+ resolveAgentMigrationControlSeamEnabled,
24505
+ AGENT_MIGRATION_BUNDLE_SCHEMA_VERSION,
24506
+ buildAgentMigrationExportManifest,
24507
+ buildAgentMigrationAdoptPlan,
24508
+ verifyAgentMigrationAdoptPlan,
24509
+ executeAgentMigrationAdoptPlan,
24510
+ hashAgentMigrationManifest,
23658
24511
  DAEMON_CORE_TRACE_ATTR_CONTRACTS,
23659
24512
  DAEMON_CLI_USAGE,
23660
24513
  parseDaemonCliArgs,