switchroom 0.13.2 → 0.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/agent-scheduler/index.js +2 -2
  2. package/dist/auth-broker/index.js +2 -2
  3. package/dist/cli/switchroom.js +132 -214
  4. package/dist/host-control/main.js +2 -2
  5. package/dist/vault/approvals/kernel-server.js +2 -2
  6. package/dist/vault/broker/server.js +2 -2
  7. package/package.json +1 -1
  8. package/profiles/_base/start.sh.hbs +8 -8
  9. package/profiles/default/CLAUDE.md.hbs +1 -1
  10. package/telegram-plugin/dist/gateway/gateway.js +42 -10
  11. package/telegram-plugin/gateway/boot-probes.ts +13 -6
  12. package/telegram-plugin/gateway/gateway.ts +44 -6
  13. package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +5 -1
  14. package/telegram-plugin/silent-end.ts +56 -0
  15. package/telegram-plugin/tests/boot-probes.test.ts +26 -2
  16. package/telegram-plugin/tests/silent-end.test.ts +69 -0
  17. package/telegram-plugin/uat/scenarios/bridge-flap-resilience-dm.test.ts +166 -0
  18. package/skills/buildkite-agent-infrastructure/SKILL.md +0 -321
  19. package/skills/buildkite-agent-infrastructure/agents/openai.yaml +0 -6
  20. package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-large.png +0 -0
  21. package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-small.png +0 -0
  22. package/skills/buildkite-agent-infrastructure/references/audit-logging.md +0 -87
  23. package/skills/buildkite-agent-infrastructure/references/graphql-mutations.md +0 -690
  24. package/skills/buildkite-agent-infrastructure/references/instance-shapes.md +0 -38
  25. package/skills/buildkite-agent-infrastructure/references/pipeline-templates.md +0 -73
  26. package/skills/buildkite-agent-infrastructure/references/self-hosted-agents.md +0 -137
  27. package/skills/buildkite-agent-infrastructure/references/sso-saml.md +0 -92
  28. package/skills/buildkite-agent-runtime/SKILL.md +0 -509
  29. package/skills/buildkite-agent-runtime/agents/openai.yaml +0 -6
  30. package/skills/buildkite-agent-runtime/assets/buildkite-icon-large.png +0 -0
  31. package/skills/buildkite-agent-runtime/assets/buildkite-icon-small.png +0 -0
  32. package/skills/buildkite-agent-runtime/references/flag-reference.md +0 -417
  33. package/skills/buildkite-agent-runtime/references/patterns-and-recipes.md +0 -555
  34. package/skills/buildkite-api/SKILL.md +0 -308
  35. package/skills/buildkite-api/agents/openai.yaml +0 -6
  36. package/skills/buildkite-api/assets/buildkite-icon-large.png +0 -0
  37. package/skills/buildkite-api/assets/buildkite-icon-small.png +0 -0
  38. package/skills/buildkite-api/references/graphql-reference.md +0 -195
  39. package/skills/buildkite-api/references/patterns.md +0 -44
  40. package/skills/buildkite-api/references/webhooks.md +0 -161
  41. package/skills/buildkite-cli/SKILL.md +0 -397
  42. package/skills/buildkite-cli/agents/openai.yaml +0 -6
  43. package/skills/buildkite-cli/assets/buildkite-icon-large.png +0 -0
  44. package/skills/buildkite-cli/assets/buildkite-icon-small.png +0 -0
  45. package/skills/buildkite-cli/references/command-reference.md +0 -181
  46. package/skills/buildkite-migration/SKILL.md +0 -195
  47. package/skills/buildkite-pipelines/SKILL.md +0 -481
  48. package/skills/buildkite-pipelines/agents/openai.yaml +0 -6
  49. package/skills/buildkite-pipelines/assets/buildkite-icon-large.png +0 -0
  50. package/skills/buildkite-pipelines/assets/buildkite-icon-small.png +0 -0
  51. package/skills/buildkite-pipelines/examples/basic-pipeline.yml +0 -24
  52. package/skills/buildkite-pipelines/examples/optimized-pipeline.yml +0 -100
  53. package/skills/buildkite-pipelines/references/advanced-patterns.md +0 -286
  54. package/skills/buildkite-pipelines/references/retry-and-error-codes.md +0 -131
  55. package/skills/buildkite-pipelines/references/step-types-reference.md +0 -225
  56. package/skills/buildkite-secure-delivery/SKILL.md +0 -182
  57. package/skills/buildkite-secure-delivery/agents/openai.yaml +0 -6
  58. package/skills/buildkite-secure-delivery/assets/buildkite-icon-large.png +0 -0
  59. package/skills/buildkite-secure-delivery/assets/buildkite-icon-small.png +0 -0
  60. package/skills/buildkite-secure-delivery/references/oidc-cloud-providers.md +0 -83
  61. package/skills/buildkite-secure-delivery/references/package-publishing.md +0 -100
  62. package/skills/buildkite-test-engine/SKILL.md +0 -256
  63. package/skills/buildkite-test-engine/agents/openai.yaml +0 -6
  64. package/skills/buildkite-test-engine/assets/buildkite-icon-large.png +0 -0
  65. package/skills/buildkite-test-engine/assets/buildkite-icon-small.png +0 -0
  66. package/skills/buildkite-test-engine/examples/bktec-splitting.yml +0 -16
  67. package/skills/buildkite-test-engine/examples/collector-pipeline.yml +0 -11
  68. package/skills/buildkite-test-engine/references/collectors.md +0 -198
  69. package/skills/buildkite-test-engine/references/splitting-examples.md +0 -93
@@ -13694,7 +13694,7 @@ var init_schema = __esm(() => {
13694
13694
  extends: exports_external.string().optional(),
13695
13695
  bot_token: exports_external.string().optional(),
13696
13696
  release: ReleaseBlock.optional().describe("Release-channel pin / pointer. Either `channel` (dev|rc|latest) or " + "`pin` (sha-<hex>|v<semver>) \u2014 mutually exclusive. Per-agent value " + "REPLACES the root entirely (no field merge)."),
13697
- timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC' " + "(three-letter aliases like EST/PST and bare offsets like UTC+10 are not accepted)").optional().describe("IANA timezone name (e.g. 'Australia/Melbourne', 'America/New_York', " + "'UTC'). Used to generate the per-turn local-time hint the agent's " + "UserPromptSubmit timezone hook emits, and baked into the systemd " + "unit as TZ= so subprocess `date`/`Date.now()` are correct. If unset " + "at every cascade layer, switchroom auto-detects from /etc/timezone " + "and warns on `reconcile` when the detected zone is UTC."),
13697
+ timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC' " + "(three-letter aliases like EST/PST and bare offsets like UTC+10 are not accepted)").optional().describe("IANA timezone name (e.g. 'Australia/Melbourne', 'America/New_York', " + "'UTC'). Used to generate the per-turn local-time hint the agent's " + "UserPromptSubmit timezone hook emits, and baked into the agent " + "container's `environment.TZ` in compose so subprocess `date`/" + "`Date.now()` are correct. If unset at every cascade layer, switchroom " + "auto-detects from /etc/timezone and warns on `reconcile` when the " + "detected zone is UTC."),
13698
13698
  soul: exports_external.object({
13699
13699
  name: exports_external.string().optional(),
13700
13700
  style: exports_external.string().optional(),
@@ -13755,7 +13755,7 @@ var init_schema = __esm(() => {
13755
13755
  bot_token: exports_external.string().optional().describe("Per-agent Telegram bot token or vault reference (overrides global telegram.bot_token)"),
13756
13756
  release: ReleaseBlock.optional().describe("Per-agent release-channel pin / pointer. REPLACES the root " + "`release` block entirely (no field merge) \u2014 a pinned agent does " + "not inherit the fleet channel, and vice versa."),
13757
13757
  bot_username: exports_external.string().optional().describe("Per-agent Telegram bot username (without leading @) when it doesn't " + "contain the agent slug. Replaces the default 'username includes slug' " + "preflight check with an exact (case-insensitive) match. Use when an " + "agent and its bot have intentionally divergent names (e.g. agent " + "'lawgpt' paired with bot '@meken_law_bot')."),
13758
- timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC' " + "(three-letter aliases like EST/PST and bare offsets like UTC+10 are not accepted)").optional().describe("Per-agent IANA timezone override. Wins over any profile/defaults " + "value and over the top-level switchroom.timezone global. Controls " + "the UserPromptSubmit timezone hook's emitted local time and the " + "systemd unit's TZ= env."),
13758
+ timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC' " + "(three-letter aliases like EST/PST and bare offsets like UTC+10 are not accepted)").optional().describe("Per-agent IANA timezone override. Wins over any profile/defaults " + "value and over the top-level switchroom.timezone global. Controls " + "the UserPromptSubmit timezone hook's emitted local time and the " + "agent container's `environment.TZ` in compose."),
13759
13759
  auth: exports_external.object({
13760
13760
  override: exports_external.string().min(1).optional().describe("Per-agent override of the fleet-wide `auth.active`. Edge-case use only \u2014 " + "this agent talks to the named account regardless of fleet active. See RFC H \u00a74.5.")
13761
13761
  }).optional().describe("Account routing for switchroom-auth-broker. RFC H schema uses " + "fleet-wide `auth.active` plus per-agent `override:` for edge cases. " + "Pre-RFC-H `auth.accounts: [..]` and `auth_label:` are migrated in-place " + "on first apply (see src/auth/migrate-schema.ts)."),
@@ -23872,7 +23872,7 @@ import {
23872
23872
  existsSync as existsSync17,
23873
23873
  mkdirSync as mkdirSync12,
23874
23874
  readFileSync as readFileSync17,
23875
- readdirSync as readdirSync7,
23875
+ readdirSync as readdirSync8,
23876
23876
  renameSync as renameSync5,
23877
23877
  rmSync as rmSync4,
23878
23878
  statSync as statSync11,
@@ -23943,7 +23943,7 @@ function listSlots(agentDir) {
23943
23943
  if (!existsSync17(dir))
23944
23944
  return [];
23945
23945
  try {
23946
- return readdirSync7(dir).filter((name) => {
23946
+ return readdirSync8(dir).filter((name) => {
23947
23947
  try {
23948
23948
  return statSync11(join13(dir, name)).isDirectory();
23949
23949
  } catch {
@@ -24115,7 +24115,7 @@ var init_accounts = __esm(() => {
24115
24115
  import { execFileSync as execFileSync8 } from "node:child_process";
24116
24116
  import {
24117
24117
  readFileSync as readFileSync18,
24118
- readdirSync as readdirSync8,
24118
+ readdirSync as readdirSync9,
24119
24119
  existsSync as existsSync18,
24120
24120
  writeFileSync as writeFileSync10,
24121
24121
  mkdirSync as mkdirSync13,
@@ -24272,7 +24272,7 @@ function cleanupAuthTempDirs(agentDir) {
24272
24272
  if (!existsSync18(dir))
24273
24273
  return;
24274
24274
  try {
24275
- for (const entry of readdirSync8(dir)) {
24275
+ for (const entry of readdirSync9(dir)) {
24276
24276
  if (entry.startsWith(".setup-token-tmp-")) {
24277
24277
  rmSync5(join14(dir, entry), { recursive: true, force: true });
24278
24278
  }
@@ -25226,7 +25226,7 @@ class AuthBrokerClient {
25226
25226
  closed = false;
25227
25227
  constructor(opts = {}) {
25228
25228
  this.socketPath = resolveAuthBrokerSocketPath(opts);
25229
- this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
25229
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
25230
25230
  }
25231
25231
  getSocketPath() {
25232
25232
  return this.socketPath;
@@ -25477,7 +25477,7 @@ async function withAuthBrokerClient(fn, opts) {
25477
25477
  function authBrokerSocketExists(opts) {
25478
25478
  return existsSync25(resolveAuthBrokerSocketPath(opts));
25479
25479
  }
25480
- var DEFAULT_TIMEOUT_MS3 = 5000, AuthBrokerError, AuthBrokerUnreachableError;
25480
+ var DEFAULT_TIMEOUT_MS2 = 5000, AuthBrokerError, AuthBrokerUnreachableError;
25481
25481
  var init_client2 = __esm(() => {
25482
25482
  init_protocol2();
25483
25483
  AuthBrokerError = class AuthBrokerError extends Error {
@@ -25537,7 +25537,7 @@ import {
25537
25537
  existsSync as existsSync26,
25538
25538
  mkdirSync as mkdirSync15,
25539
25539
  readFileSync as readFileSync22,
25540
- readdirSync as readdirSync12,
25540
+ readdirSync as readdirSync13,
25541
25541
  renameSync as renameSync6,
25542
25542
  rmSync as rmSync10,
25543
25543
  statSync as statSync15,
@@ -25568,7 +25568,7 @@ function listAccounts(home2 = homedir8()) {
25568
25568
  if (!existsSync26(root))
25569
25569
  return [];
25570
25570
  try {
25571
- return readdirSync12(root).filter((name) => {
25571
+ return readdirSync13(root).filter((name) => {
25572
25572
  try {
25573
25573
  return statSync15(join19(root, name)).isDirectory();
25574
25574
  } catch {
@@ -25730,7 +25730,7 @@ __export(exports_oauth, {
25730
25730
  });
25731
25731
  import * as http from "node:http";
25732
25732
  import * as crypto2 from "node:crypto";
25733
- import { spawn as spawn3 } from "node:child_process";
25733
+ import { spawn as spawn2 } from "node:child_process";
25734
25734
  function detectHeadless(env2) {
25735
25735
  const hasDisplay = Boolean(env2.DISPLAY && env2.DISPLAY.trim() !== "" || env2.WAYLAND_DISPLAY && env2.WAYLAND_DISPLAY.trim() !== "");
25736
25736
  const inSsh = Boolean(env2.SSH_CONNECTION && env2.SSH_CONNECTION.trim() !== "" || env2.SSH_TTY && env2.SSH_TTY.trim() !== "");
@@ -25890,7 +25890,7 @@ function buildLoopbackAuthUrl(cfg, redirectUri, state) {
25890
25890
  u.searchParams.set("state", state);
25891
25891
  return u.toString();
25892
25892
  }
25893
- async function openBrowser(url, platform = process.platform, spawnImpl = spawn3) {
25893
+ async function openBrowser(url, platform = process.platform, spawnImpl = spawn2) {
25894
25894
  let cmd;
25895
25895
  let args;
25896
25896
  if (platform === "darwin") {
@@ -26407,7 +26407,7 @@ async function waitForApproval(opts) {
26407
26407
  const request = opts._request ?? approvalRequest;
26408
26408
  const lookup = opts._lookup ?? approvalLookup;
26409
26409
  const sleep3 = opts._sleep ?? defaultSleep;
26410
- const timeoutMs = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS4;
26410
+ const timeoutMs = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS3;
26411
26411
  const initialPoll = opts.initial_poll_ms ?? DEFAULT_INITIAL_POLL_MS;
26412
26412
  const maxPoll = opts.max_poll_ms ?? DEFAULT_MAX_POLL_MS;
26413
26413
  const backoff = opts.backoff ?? DEFAULT_BACKOFF;
@@ -26495,7 +26495,7 @@ async function waitForApproval(opts) {
26495
26495
  }
26496
26496
  }
26497
26497
  }
26498
- var DEFAULT_TIMEOUT_MS4 = 600000, DEFAULT_INITIAL_POLL_MS = 2000, DEFAULT_MAX_POLL_MS = 30000, DEFAULT_BACKOFF = 1.5, DENY_MODES;
26498
+ var DEFAULT_TIMEOUT_MS3 = 600000, DEFAULT_INITIAL_POLL_MS = 2000, DEFAULT_MAX_POLL_MS = 30000, DEFAULT_BACKOFF = 1.5, DENY_MODES;
26499
26499
  var init_wait = __esm(() => {
26500
26500
  init_client3();
26501
26501
  DENY_MODES = new Set(["deny", "deny_perm"]);
@@ -27053,7 +27053,7 @@ async function runViaClaude(opts) {
27053
27053
  if (tmuxHasSession(SESSION))
27054
27054
  tmuxKillSession(SESSION);
27055
27055
  }
27056
- const spawn4 = opts.spawnClaude ?? (() => {
27056
+ const spawn3 = opts.spawnClaude ?? (() => {
27057
27057
  execFileSync11("tmux", [
27058
27058
  "new-session",
27059
27059
  "-d",
@@ -27069,7 +27069,7 @@ async function runViaClaude(opts) {
27069
27069
  ], { stdio: "ignore", timeout: 5000 });
27070
27070
  });
27071
27071
  log(" Spawning claude in a tmux session to mint a broader-scope OAuth token\u2026");
27072
- spawn4();
27072
+ spawn3();
27073
27073
  try {
27074
27074
  const preFired = new Set;
27075
27075
  const phase1Deadline = Date.now() + urlTimeout;
@@ -27605,7 +27605,7 @@ var init_doctor_status = __esm(() => {
27605
27605
  import {
27606
27606
  existsSync as existsSync46,
27607
27607
  readFileSync as readFileSync42,
27608
- readdirSync as readdirSync16
27608
+ readdirSync as readdirSync17
27609
27609
  } from "node:fs";
27610
27610
  import { dirname as dirname11, join as join38 } from "node:path";
27611
27611
  import { execSync as execSync2 } from "node:child_process";
@@ -27686,7 +27686,7 @@ function probePlaywrightMcpVersion() {
27686
27686
  if (!existsSync46(npxCache))
27687
27687
  return null;
27688
27688
  try {
27689
- const entries = readdirSync16(npxCache);
27689
+ const entries = readdirSync17(npxCache);
27690
27690
  for (const entry of entries) {
27691
27691
  const pkgPath = join38(npxCache, entry, "node_modules/@playwright/mcp/package.json");
27692
27692
  if (existsSync46(pkgPath)) {
@@ -28834,7 +28834,7 @@ import { join as join42 } from "node:path";
28834
28834
  function runCredentialsMigrationChecks(config, deps = {}) {
28835
28835
  const credDir = deps.credentialsDir ?? join42(homedir23(), ".switchroom", "credentials");
28836
28836
  const existsSync49 = deps.existsSync ?? ((p) => realExistsSync2(p));
28837
- const readdirSync18 = deps.readdirSync ?? ((p) => realReaddirSync(p));
28837
+ const readdirSync19 = deps.readdirSync ?? ((p) => realReaddirSync(p));
28838
28838
  const isDirectory = deps.isDirectory ?? ((p) => {
28839
28839
  try {
28840
28840
  return realStatSync(p).isDirectory();
@@ -28847,7 +28847,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
28847
28847
  const agentNames = new Set(Object.keys(config.agents ?? {}));
28848
28848
  let entries;
28849
28849
  try {
28850
- entries = readdirSync18(credDir);
28850
+ entries = readdirSync19(credDir);
28851
28851
  } catch {
28852
28852
  return [
28853
28853
  {
@@ -29403,7 +29403,7 @@ import {
29403
29403
  lstatSync as lstatSync5,
29404
29404
  mkdirSync as mkdirSync27,
29405
29405
  readFileSync as readFileSync45,
29406
- readdirSync as readdirSync18,
29406
+ readdirSync as readdirSync19,
29407
29407
  statSync as statSync22
29408
29408
  } from "node:fs";
29409
29409
  import { dirname as dirname12, join as join45, resolve as resolve29 } from "node:path";
@@ -29413,7 +29413,7 @@ function findInNvm(bin) {
29413
29413
  if (!existsSync50(nvmRoot))
29414
29414
  return null;
29415
29415
  try {
29416
- const versions = readdirSync18(nvmRoot).sort().reverse();
29416
+ const versions = readdirSync19(nvmRoot).sort().reverse();
29417
29417
  for (const v of versions) {
29418
29418
  const candidate = join45(nvmRoot, v, "bin", bin);
29419
29419
  try {
@@ -29585,7 +29585,7 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
29585
29585
  if (!existsSync50(cacheDir))
29586
29586
  continue;
29587
29587
  try {
29588
- const entries = readdirSync18(cacheDir).filter((e) => e.startsWith("chromium"));
29588
+ const entries = readdirSync19(cacheDir).filter((e) => e.startsWith("chromium"));
29589
29589
  for (const entry of entries) {
29590
29590
  const candidates2 = [
29591
29591
  join45(cacheDir, entry, "chrome-linux64", "chrome"),
@@ -29898,7 +29898,7 @@ function checkPendingRetainsQueue(dir) {
29898
29898
  }
29899
29899
  let names;
29900
29900
  try {
29901
- names = readdirSync18(pendingDir);
29901
+ names = readdirSync19(pendingDir);
29902
29902
  } catch (err) {
29903
29903
  return {
29904
29904
  name: "pending-retains queue",
@@ -30158,7 +30158,7 @@ function checkRepoHygiene(repoRoot) {
30158
30158
  });
30159
30159
  }
30160
30160
  try {
30161
- const entries = readdirSync18(repoRoot);
30161
+ const entries = readdirSync19(repoRoot);
30162
30162
  for (const name of entries) {
30163
30163
  if (name === "clerk-export-with-secrets.tar.gz")
30164
30164
  continue;
@@ -47248,8 +47248,8 @@ var {
47248
47248
  } = import__.default;
47249
47249
 
47250
47250
  // src/build-info.ts
47251
- var VERSION = "0.13.2";
47252
- var COMMIT_SHA = "afa0fbea";
47251
+ var VERSION = "0.13.4";
47252
+ var COMMIT_SHA = "b4fd0264";
47253
47253
 
47254
47254
  // src/cli/agent.ts
47255
47255
  init_source();
@@ -50468,13 +50468,11 @@ import { join as join12 } from "node:path";
50468
50468
  import { execFileSync as execFileSync6 } from "node:child_process";
50469
50469
 
50470
50470
  // src/agents/handoff-summarizer.ts
50471
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, renameSync as renameSync4, mkdirSync as mkdirSync11, existsSync as existsSync14, statSync as statSync9 } from "node:fs";
50471
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, renameSync as renameSync4, mkdirSync as mkdirSync11, existsSync as existsSync14, statSync as statSync9, readdirSync as readdirSync7 } from "node:fs";
50472
50472
  import { join as join11 } from "node:path";
50473
- import { spawn as spawn2 } from "node:child_process";
50474
- var DEFAULT_SUMMARIZER_MODEL = "claude-haiku-4-5-20251001";
50475
50473
  var DEFAULT_MAX_TURNS = 50;
50476
- var DEFAULT_TIMEOUT_MS2 = 30000;
50477
50474
  var TOPIC_MAX_CHARS = 117;
50475
+ var TURN_TEXT_MAX_CHARS = 1200;
50478
50476
  function extractTurnsFromJsonl(path, maxTurns) {
50479
50477
  let raw;
50480
50478
  try {
@@ -50507,7 +50505,8 @@ function extractTurnsFromJsonl(path, maxTurns) {
50507
50505
  }
50508
50506
  if (obj.type === "user" && obj.message && typeof obj.message === "object") {
50509
50507
  const content = obj.message.content;
50510
- const text = extractTextBlocks(content);
50508
+ const raw2 = extractTextBlocks(content);
50509
+ const text = raw2 ? extractChannelBody(raw2) : null;
50511
50510
  if (text)
50512
50511
  turns.push({ role: "user", text });
50513
50512
  continue;
@@ -50520,9 +50519,16 @@ function extractTurnsFromJsonl(path, maxTurns) {
50520
50519
  continue;
50521
50520
  }
50522
50521
  }
50523
- if (turns.length <= maxTurns)
50524
- return turns;
50525
- return turns.slice(turns.length - maxTurns);
50522
+ const deduped = [];
50523
+ for (const t of turns) {
50524
+ const prev = deduped[deduped.length - 1];
50525
+ if (prev && prev.role === t.role && prev.text === t.text)
50526
+ continue;
50527
+ deduped.push(t);
50528
+ }
50529
+ if (deduped.length <= maxTurns)
50530
+ return deduped;
50531
+ return deduped.slice(deduped.length - maxTurns);
50526
50532
  }
50527
50533
  function extractChannelBody(raw) {
50528
50534
  const m = raw.match(/<channel[^>]*>([\s\S]*?)<\/channel>/);
@@ -50553,56 +50559,48 @@ function extractTextBlocks(content) {
50553
50559
  `).trim();
50554
50560
  return joined.length > 0 ? joined : null;
50555
50561
  }
50556
- function buildHandoffPrompt(turns) {
50557
- const system = "You produce concise handoff briefings for an AI assistant that is " + "about to start a fresh session. The next session has no memory of " + `what just happened; your briefing is its only carry-over context.
50558
-
50559
- ` + `Output format \u2014 EXACTLY this structure, no preamble:
50560
- ` + `## Topic: <one short line, max 100 chars, describing what the user and assistant were most recently focused on>
50561
-
50562
- ` + `## Summary
50563
- <one paragraph, what we were working on>
50564
-
50565
- ` + `## Open threads
50566
- - <bulleted list of pending/unresolved items; empty list ok>
50562
+ function formatTranscriptTail(turns) {
50563
+ const header = `# Handoff \u2014 previous session
50567
50564
 
50568
- ` + `## Last exchange
50569
- **User:** <verbatim or near-verbatim last user message, truncated to ~500 chars>
50570
- **Assistant:** <last assistant response, truncated to ~500 chars>
50571
-
50572
- ` + `## Key decisions & facts
50573
- - <bullets; empty list ok>
50574
-
50575
- ` + `## Active files / paths
50576
- - <bullets; empty list ok>
50577
-
50578
- ` + "Keep the whole briefing under ~1500 tokens. Prefer brevity. Omit sections only if truly empty (still emit the heading with '- (none)').";
50579
- const transcript = turns.map((t) => `### ${t.role.toUpperCase()}
50580
- ${t.text}`).join(`
50565
+ ` + "You are resuming this agent's work. There is no generated summary " + "\u2014 below is the **raw tail of the previous session's transcript** " + "(oldest first, most recent last). Read it to reorient, then carry " + "on. Anything important worth keeping long-term should already be " + `in your memory files \u2014 check those too.
50566
+ `;
50567
+ if (turns.length === 0) {
50568
+ return header + `
50569
+ _(No recent turns were recoverable from the previous session.)_
50570
+ `;
50571
+ }
50572
+ const body = turns.map((t) => {
50573
+ let text = t.text;
50574
+ if (text.length > TURN_TEXT_MAX_CHARS) {
50575
+ text = text.slice(0, TURN_TEXT_MAX_CHARS) + `
50576
+ \u2026[truncated]`;
50577
+ }
50578
+ return `### ${t.role === "user" ? "User" : "Assistant"}
50579
+ ${text}`;
50580
+ }).join(`
50581
50581
 
50582
50582
  `);
50583
- const user = `Here is the recent session transcript (oldest first). Produce the handoff briefing per the specified format.
50583
+ return `${header}
50584
+ ## Recent turns
50584
50585
 
50585
- ` + transcript;
50586
- return { system, user };
50586
+ ${body}
50587
+ `;
50587
50588
  }
50588
- function parseHandoffResponse(raw) {
50589
- const lines = raw.split(/\r?\n/);
50590
- let start = 0;
50591
- while (start < lines.length && lines[start].trim() === "")
50592
- start++;
50593
- if (start >= lines.length)
50594
- return null;
50595
- const first = lines[start].trim();
50596
- const m = first.match(/^##\s*Topic:\s*(.+)$/i);
50597
- if (!m)
50598
- return null;
50599
- let topic = m[1].trim();
50600
- if (topic.length > TOPIC_MAX_CHARS) {
50601
- topic = topic.slice(0, TOPIC_MAX_CHARS) + "\u2026";
50589
+ function deriveTopic(turns) {
50590
+ for (let i = turns.length - 1;i >= 0; i--) {
50591
+ if (turns[i].role === "user")
50592
+ return clampTopic(firstLine(turns[i].text));
50602
50593
  }
50603
- const briefing = lines.slice(start).join(`
50604
- `).trim();
50605
- return { topic, briefing };
50594
+ if (turns.length > 0)
50595
+ return clampTopic(firstLine(turns[turns.length - 1].text));
50596
+ return "previous session";
50597
+ }
50598
+ function firstLine(s) {
50599
+ const line = s.split(/\r?\n/).find((l) => l.trim().length > 0) ?? s;
50600
+ return line.trim();
50601
+ }
50602
+ function clampTopic(s) {
50603
+ return s.length > TOPIC_MAX_CHARS ? s.slice(0, TOPIC_MAX_CHARS) + "\u2026" : s;
50606
50604
  }
50607
50605
  function writeSidecarsAtomic(agentDir, briefing, topic) {
50608
50606
  mkdirSync11(agentDir, { recursive: true });
@@ -50615,99 +50613,24 @@ function writeSidecarsAtomic(agentDir, briefing, topic) {
50615
50613
  renameSync4(handoffTmp, handoffPath);
50616
50614
  renameSync4(topicTmp, topicPath);
50617
50615
  }
50618
- async function summarize(opts) {
50619
- const model = opts.model ?? DEFAULT_SUMMARIZER_MODEL;
50616
+ async function buildHandoff(opts) {
50620
50617
  const maxTurns = opts.maxTurns ?? DEFAULT_MAX_TURNS;
50621
- const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
50622
50618
  const turns = extractTurnsFromJsonl(opts.jsonlPath, maxTurns);
50623
50619
  if (turns.length === 0) {
50624
50620
  return "no-turns";
50625
50621
  }
50626
- const runner = opts.runner ?? defaultClaudeCliRunner;
50627
- const prompt = buildHandoffPrompt(turns);
50628
- let raw;
50629
- try {
50630
- raw = await runner.run({
50631
- model,
50632
- system: prompt.system,
50633
- user: prompt.user,
50634
- timeoutMs
50635
- });
50636
- } catch (err) {
50637
- process.stderr.write(`handoff-summarizer: claude -p call failed \u2014 ${errMsg(err)}
50638
- `);
50639
- return "cli-error";
50640
- }
50641
- raw = raw.trim();
50642
- if (!raw) {
50643
- return "empty-response";
50644
- }
50645
- const parsed = parseHandoffResponse(raw);
50646
- if (!parsed) {
50647
- process.stderr.write(`handoff-summarizer: response missing '## Topic:' header; skipping
50648
- `);
50649
- return "parse-error";
50650
- }
50622
+ const briefing = formatTranscriptTail(turns);
50623
+ const topic = deriveTopic(turns);
50651
50624
  try {
50652
- writeSidecarsAtomic(opts.agentDir, parsed.briefing, parsed.topic);
50625
+ writeSidecarsAtomic(opts.agentDir, briefing, topic);
50653
50626
  } catch (err) {
50654
- process.stderr.write(`handoff-summarizer: sidecar write failed \u2014 ${errMsg(err)}
50627
+ process.stderr.write(`handoff: sidecar write failed \u2014 ${errMsg(err)}
50655
50628
  `);
50656
50629
  return "write-error";
50657
50630
  }
50658
- await mirrorToHindsight(parsed.briefing, opts).catch(() => {});
50631
+ await mirrorToHindsight(briefing, opts).catch(() => {});
50659
50632
  return "ok";
50660
50633
  }
50661
- var defaultClaudeCliRunner = {
50662
- async run({ model, system, user, timeoutMs }) {
50663
- return await new Promise((resolve14, reject) => {
50664
- const args = [
50665
- "-p",
50666
- user,
50667
- "--model",
50668
- model,
50669
- "--append-system-prompt",
50670
- system,
50671
- "--no-session-persistence"
50672
- ];
50673
- const env2 = {
50674
- ...process.env,
50675
- FORCE_COLOR: "0",
50676
- NO_COLOR: "1"
50677
- };
50678
- const child = spawn2("claude", args, {
50679
- stdio: ["ignore", "pipe", "pipe"],
50680
- env: env2
50681
- });
50682
- let stdout = "";
50683
- let stderr = "";
50684
- const timer = setTimeout(() => {
50685
- child.kill("SIGTERM");
50686
- reject(new Error(`timeout after ${timeoutMs}ms`));
50687
- }, timeoutMs);
50688
- child.stdout.on("data", (d) => {
50689
- stdout += d.toString("utf-8");
50690
- });
50691
- child.stderr.on("data", (d) => {
50692
- stderr += d.toString("utf-8");
50693
- });
50694
- child.on("error", (err) => {
50695
- clearTimeout(timer);
50696
- reject(err);
50697
- });
50698
- child.on("close", (code) => {
50699
- clearTimeout(timer);
50700
- if (code === 0) {
50701
- resolve14(stdout);
50702
- } else {
50703
- const tail = stderr.trim().split(`
50704
- `).slice(-3).join(" | ");
50705
- reject(new Error(`claude -p exited ${code}: ${tail || "(no stderr)"}`));
50706
- }
50707
- });
50708
- });
50709
- }
50710
- };
50711
50634
  function errMsg(err) {
50712
50635
  if (err && typeof err === "object" && "message" in err) {
50713
50636
  return String(err.message);
@@ -50738,7 +50661,7 @@ async function mirrorToHindsight(briefing, opts) {
50738
50661
  body: JSON.stringify(body)
50739
50662
  });
50740
50663
  } catch (err) {
50741
- process.stderr.write(`handoff-summarizer: hindsight mirror failed \u2014 ${errMsg(err)}
50664
+ process.stderr.write(`handoff: hindsight mirror failed \u2014 ${errMsg(err)}
50742
50665
  `);
50743
50666
  }
50744
50667
  }
@@ -50750,7 +50673,7 @@ function findLatestSessionJsonl(claudeConfigDir) {
50750
50673
  const walk = (dir) => {
50751
50674
  let entries;
50752
50675
  try {
50753
- entries = __require("fs").readdirSync(dir);
50676
+ entries = readdirSync7(dir);
50754
50677
  } catch {
50755
50678
  return;
50756
50679
  }
@@ -51704,7 +51627,7 @@ function enforceUsername(username, agentSlug, expectedUsername, loose, warn) {
51704
51627
  }
51705
51628
 
51706
51629
  // src/setup/profile-picker.ts
51707
- import { existsSync as existsSync20, readdirSync as readdirSync9, statSync as statSync13 } from "node:fs";
51630
+ import { existsSync as existsSync20, readdirSync as readdirSync10, statSync as statSync13 } from "node:fs";
51708
51631
  import { resolve as resolve16 } from "node:path";
51709
51632
  var PROFILE_GLOSSES = {
51710
51633
  default: "minimal baseline \u2014 generic chat helper, no opinion.",
@@ -51720,7 +51643,7 @@ function defaultListProfileSkills(profileName) {
51720
51643
  return [];
51721
51644
  }
51722
51645
  try {
51723
- return readdirSync9(skillsDir).filter((entry) => {
51646
+ return readdirSync10(skillsDir).filter((entry) => {
51724
51647
  try {
51725
51648
  return statSync13(resolve16(skillsDir, entry)).isDirectory();
51726
51649
  } catch {
@@ -53330,7 +53253,6 @@ Scaffolding agent: ${name}
53330
53253
  }
53331
53254
  try {
53332
53255
  const result = reconcileAgent(n, agentConfig, agentsDir, config.telegram, config, configPath, { preserveClaudeMd: opts.preserveClaudeMd });
53333
- const unitChanges = [];
53334
53256
  const allChanges = [...result.changes];
53335
53257
  if (allChanges.length === 0) {
53336
53258
  console.log(source_default.gray(` ${n}: already in sync`));
@@ -53341,7 +53263,7 @@ Scaffolding agent: ${name}
53341
53263
  const semantics = result.changesBySemantics;
53342
53264
  if (semantics) {
53343
53265
  const { hot, staleTillRestart } = semantics;
53344
- const restartRequired = [...semantics.restartRequired, ...unitChanges];
53266
+ const restartRequired = [...semantics.restartRequired];
53345
53267
  if (restartRequired.length > 0) {
53346
53268
  console.log(source_default.yellow(`
53347
53269
  Changed (restart required \u2014 soul/MCP/settings/start.sh):`));
@@ -53384,7 +53306,7 @@ Restart: switchroom agent restart ${n}`));
53384
53306
  }
53385
53307
  }
53386
53308
  const changesBySemantics = result.changesBySemantics;
53387
- const autoRestartNeeded = !opts.noRestart && (changesBySemantics !== undefined && changesBySemantics.restartRequired.length > 0 || unitChanges.length > 0);
53309
+ const autoRestartNeeded = !opts.noRestart && changesBySemantics !== undefined && changesBySemantics.restartRequired.length > 0;
53388
53310
  const shouldRestart = (opts.restart || opts.gracefulRestart || autoRestartNeeded) && allChanges.length > 0;
53389
53311
  if (shouldRestart) {
53390
53312
  try {
@@ -53815,7 +53737,7 @@ switchroom agent add: ${name}
53815
53737
  process.exit(1);
53816
53738
  }
53817
53739
  }));
53818
- agent.command("rename <old> <new>").description("Rename an agent slug: stop, snapshot, rename dir + systemd units + vault key, " + "update switchroom.yaml, reconcile, start. Rolls back on any failure.").option("--hindsight <mode>", 'Hindsight bank handling: "preserve" (default, keep old bank) or "fresh" (drop, recreate on first run). "migrate" is deferred.', "preserve").option("-y, --yes", "Skip confirmation prompt").action(withConfigError(async (oldName, newName, opts) => {
53740
+ agent.command("rename <old> <new>").description("Rename an agent slug: stop, snapshot, rename dir + vault key, " + "update switchroom.yaml, reconcile, start. Rolls back on any failure.").option("--hindsight <mode>", 'Hindsight bank handling: "preserve" (default, keep old bank) or "fresh" (drop, recreate on first run). "migrate" is deferred.', "preserve").option("-y, --yes", "Skip confirmation prompt").action(withConfigError(async (oldName, newName, opts) => {
53819
53741
  const configPath = getConfigPath(program3);
53820
53742
  const config = getConfig(program3);
53821
53743
  if (!config.agents[oldName]) {
@@ -53834,7 +53756,7 @@ switchroom agent add: ${name}
53834
53756
  if (!opts.yes) {
53835
53757
  process.stdout.write(source_default.yellow(`
53836
53758
  Rename agent "${oldName}" \u2192 "${newName}"?
53837
- ` + ` This will: stop ${oldName}, copy dir, rename systemd units,
53759
+ ` + ` This will: stop ${oldName}, copy dir,
53838
53760
  ` + ` rename vault keys (if passphrase available), update switchroom.yaml,
53839
53761
  ` + ` reconcile, and start ${newName}.
53840
53762
  ` + ` Hindsight mode: ${hindsightMode}
@@ -55387,7 +55309,7 @@ init_source();
55387
55309
  init_loader();
55388
55310
  init_vault();
55389
55311
  init_hindsight();
55390
- import { readFileSync as readFileSync26, writeFileSync as writeFileSync16, copyFileSync as copyFileSync6, existsSync as existsSync30, readdirSync as readdirSync13, statSync as statSync17 } from "node:fs";
55312
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync16, copyFileSync as copyFileSync6, existsSync as existsSync30, readdirSync as readdirSync14, statSync as statSync17 } from "node:fs";
55391
55313
  import { execFileSync as execFileSync12 } from "node:child_process";
55392
55314
  import { join as join23 } from "node:path";
55393
55315
  import { homedir as homedir10 } from "node:os";
@@ -55412,7 +55334,7 @@ function findClaudeTranscripts() {
55412
55334
  const walk = (dir) => {
55413
55335
  let entries;
55414
55336
  try {
55415
- entries = readdirSync13(dir);
55337
+ entries = readdirSync14(dir);
55416
55338
  } catch {
55417
55339
  return;
55418
55340
  }
@@ -55771,13 +55693,13 @@ init_loader();
55771
55693
  init_loader();
55772
55694
  init_client();
55773
55695
  import { readFileSync as readFileSync31, existsSync as existsSync34, unlinkSync as unlinkSync9 } from "node:fs";
55774
- import { spawn as spawn4 } from "node:child_process";
55696
+ import { spawn as spawn3 } from "node:child_process";
55775
55697
 
55776
55698
  // src/vault/broker/server.ts
55777
55699
  init_compose();
55778
55700
  init_vault();
55779
55701
  import * as net3 from "node:net";
55780
- import { mkdirSync as mkdirSync20, chmodSync as chmodSync7, chownSync, existsSync as existsSync33, readFileSync as readFileSync29, readdirSync as readdirSync14, statSync as statSync19, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18, renameSync as renameSync9 } from "node:fs";
55702
+ import { mkdirSync as mkdirSync20, chmodSync as chmodSync7, chownSync, existsSync as existsSync33, readFileSync as readFileSync29, readdirSync as readdirSync15, statSync as statSync19, unlinkSync as unlinkSync8, writeFileSync as writeFileSync18, renameSync as renameSync9 } from "node:fs";
55781
55703
  import { dirname as dirname6, resolve as resolve24, basename as basename5 } from "node:path";
55782
55704
  import * as os4 from "node:os";
55783
55705
  import * as path3 from "node:path";
@@ -59841,7 +59763,7 @@ async function main() {
59841
59763
  let perAgentTargets = [];
59842
59764
  try {
59843
59765
  if (existsSync33(perAgentDir)) {
59844
- const entries = readdirSync14(perAgentDir, { withFileTypes: true });
59766
+ const entries = readdirSync15(perAgentDir, { withFileTypes: true });
59845
59767
  const flat = [];
59846
59768
  const subdirs = [];
59847
59769
  for (const e of entries) {
@@ -60121,7 +60043,7 @@ function registerVaultBrokerCommand(vaultCmd, program3) {
60121
60043
  const args = ["vault", "broker", "start", "--foreground"];
60122
60044
  if (parentOpts.config)
60123
60045
  args.unshift("--config", parentOpts.config);
60124
- const child = spawn4(process.execPath, [self2, ...args], {
60046
+ const child = spawn3(process.execPath, [self2, ...args], {
60125
60047
  detached: true,
60126
60048
  stdio: "ignore"
60127
60049
  });
@@ -60408,11 +60330,11 @@ function levelLabel(level) {
60408
60330
  function printDiagnostic(d) {
60409
60331
  const glyph = levelGlyph(d.level);
60410
60332
  const label = levelLabel(d.level);
60411
- const firstLine = d.message.split(`
60333
+ const firstLine2 = d.message.split(`
60412
60334
  `)[0];
60413
60335
  const rest = d.message.split(`
60414
60336
  `).slice(1);
60415
- console.log(` ${glyph} [${label}] ${firstLine}`);
60337
+ console.log(` ${glyph} [${label}] ${firstLine2}`);
60416
60338
  for (const line of rest) {
60417
60339
  console.log(` ${source_default.gray(line)}`);
60418
60340
  }
@@ -60807,7 +60729,7 @@ import {
60807
60729
  fsyncSync as fsyncSync5,
60808
60730
  mkdirSync as mkdirSync21,
60809
60731
  openSync as openSync9,
60810
- readdirSync as readdirSync15,
60732
+ readdirSync as readdirSync16,
60811
60733
  readFileSync as readFileSync33,
60812
60734
  renameSync as renameSync10,
60813
60735
  statSync as statSync20,
@@ -60915,7 +60837,7 @@ function listBackupFiles(dir) {
60915
60837
  return [];
60916
60838
  let entries;
60917
60839
  try {
60918
- entries = readdirSync15(dir);
60840
+ entries = readdirSync16(dir);
60919
60841
  } catch {
60920
60842
  return [];
60921
60843
  }
@@ -60938,7 +60860,7 @@ function backupVault(opts) {
60938
60860
  throw new Error(`vault backup refused: source is not a valid vault envelope: ${validationError}`);
60939
60861
  }
60940
60862
  mkdirSync21(opts.destDir, { recursive: true, mode: 448 });
60941
- const dirEntries = readdirSync15(opts.destDir);
60863
+ const dirEntries = readdirSync16(opts.destDir);
60942
60864
  const offender = findAutoUnlockSibling(dirEntries);
60943
60865
  if (offender) {
60944
60866
  throw new Error(`vault backup refused: destination '${opts.destDir}' contains a file ` + `that looks like an auto-unlock credential ('${offender}'). The ` + `machine-bound auto-unlock blob MUST NOT be co-located with the ` + `encrypted vault \u2014 if they're together in version control, the ` + `passphrase gate is bypassed. Move/remove that file and retry.`);
@@ -62707,7 +62629,7 @@ import {
62707
62629
  } from "node:fs";
62708
62630
  import { resolve as resolve26, extname, join as join37, relative, dirname as dirname9 } from "node:path";
62709
62631
  import { homedir as homedir19 } from "node:os";
62710
- import { spawn as spawn5 } from "node:child_process";
62632
+ import { spawn as spawn4 } from "node:child_process";
62711
62633
  import { timingSafeEqual as timingSafeEqual2, randomBytes as randomBytes10 } from "node:crypto";
62712
62634
 
62713
62635
  // src/web/api.ts
@@ -68623,7 +68545,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
68623
68545
  existing.kill();
68624
68546
  ws._logProcess = null;
68625
68547
  }
68626
- const child = spawn5("docker", ["logs", "-f", "--tail", "20", containerName(agentName)], { stdio: ["ignore", "pipe", "pipe"] });
68548
+ const child = spawn4("docker", ["logs", "-f", "--tail", "20", containerName(agentName)], { stdio: ["ignore", "pipe", "pipe"] });
68627
68549
  child.on("error", (err) => {
68628
68550
  try {
68629
68551
  ws.send(JSON.stringify({
@@ -70230,7 +70152,7 @@ init_helpers();
70230
70152
  init_loader();
70231
70153
  import { resolve as resolve32 } from "node:path";
70232
70154
  function registerHandoffCommand(program3) {
70233
- program3.command("handoff <agent>", { hidden: true }).description("Summarize the agent's last session into a handoff briefing " + "(.handoff.md) and topic line (.handoff-topic). [internal \u2014 used by Stop hook]").option("--timeout <secs>", "API call timeout in seconds", "30").option("--max-turns <n>", "Max turns fed to the summarizer", String(DEFAULT_MAX_TURNS)).option("--model <id>", "Anthropic model for the summarizer", DEFAULT_SUMMARIZER_MODEL).action(withConfigError(async (agentName, opts) => {
70155
+ program3.command("handoff <agent>", { hidden: true }).description("Build the agent's session handoff sidecars \u2014 a transcript-tail " + "briefing (.handoff.md) and topic line (.handoff-topic). " + "[internal \u2014 used by the Stop hook]").option("--max-turns <n>", "Max turns kept in the handoff transcript tail", String(DEFAULT_MAX_TURNS)).action(withConfigError(async (agentName, opts) => {
70234
70156
  const config = getConfig(program3);
70235
70157
  const agentConfig = config.agents[agentName];
70236
70158
  if (!agentConfig) {
@@ -70253,17 +70175,13 @@ function registerHandoffCommand(program3) {
70253
70175
  `);
70254
70176
  return;
70255
70177
  }
70256
- const timeoutMs = Math.max(1, parseInt(opts.timeout, 10)) * 1000;
70257
70178
  const maxTurns = Math.max(1, parseInt(opts.maxTurns, 10));
70258
- const model = continuity?.summarizer_model ?? opts.model;
70259
70179
  const cappedMaxTurns = continuity?.max_turns_in_briefing ?? maxTurns;
70260
- const status = await summarize({
70180
+ const status = await buildHandoff({
70261
70181
  jsonlPath: jsonl,
70262
70182
  agentDir,
70263
70183
  agentName,
70264
- model,
70265
- maxTurns: cappedMaxTurns,
70266
- timeoutMs
70184
+ maxTurns: cappedMaxTurns
70267
70185
  });
70268
70186
  process.stderr.write(`handoff: ${status}
70269
70187
  `);
@@ -70276,7 +70194,7 @@ import {
70276
70194
  existsSync as existsSync53,
70277
70195
  mkdirSync as mkdirSync29,
70278
70196
  openSync as openSync11,
70279
- readdirSync as readdirSync19,
70197
+ readdirSync as readdirSync20,
70280
70198
  readFileSync as readFileSync48,
70281
70199
  renameSync as renameSync11,
70282
70200
  statSync as statSync24,
@@ -70765,7 +70683,7 @@ var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
70765
70683
  function sweepOrphanTmpFiles(stateDir) {
70766
70684
  let entries;
70767
70685
  try {
70768
- entries = readdirSync19(stateDir);
70686
+ entries = readdirSync20(stateDir);
70769
70687
  } catch {
70770
70688
  return;
70771
70689
  }
@@ -72338,7 +72256,7 @@ function registerSoulCommand(program3) {
72338
72256
  // src/cli/debug.ts
72339
72257
  init_helpers();
72340
72258
  init_loader();
72341
- import { existsSync as existsSync59, readFileSync as readFileSync52, readdirSync as readdirSync20, statSync as statSync25 } from "node:fs";
72259
+ import { existsSync as existsSync59, readFileSync as readFileSync52, readdirSync as readdirSync21, statSync as statSync25 } from "node:fs";
72342
72260
  import { resolve as resolve37, join as join53 } from "node:path";
72343
72261
  import { createHash as createHash12 } from "node:crypto";
72344
72262
  init_merge();
@@ -72357,7 +72275,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
72357
72275
  if (!existsSync59(projectsDir))
72358
72276
  return;
72359
72277
  try {
72360
- const entries = readdirSync20(projectsDir, { withFileTypes: true });
72278
+ const entries = readdirSync21(projectsDir, { withFileTypes: true });
72361
72279
  let latest;
72362
72280
  for (const entry of entries) {
72363
72281
  if (!entry.isDirectory())
@@ -72595,7 +72513,7 @@ import {
72595
72513
  mkdirSync as mkdirSync32,
72596
72514
  writeFileSync as writeFileSync29,
72597
72515
  readFileSync as readFileSync53,
72598
- readdirSync as readdirSync21,
72516
+ readdirSync as readdirSync22,
72599
72517
  unlinkSync as unlinkSync12,
72600
72518
  existsSync as existsSync60,
72601
72519
  renameSync as renameSync12
@@ -72638,7 +72556,7 @@ function listRecords() {
72638
72556
  ensureDir2();
72639
72557
  const dir = registryDir();
72640
72558
  const records = [];
72641
- for (const entry of readdirSync21(dir)) {
72559
+ for (const entry of readdirSync22(dir)) {
72642
72560
  if (!entry.endsWith(".json"))
72643
72561
  continue;
72644
72562
  const id = entry.slice(0, -5);
@@ -72985,7 +72903,7 @@ init_scaffold_integration();
72985
72903
  import {
72986
72904
  chmodSync as chmodSync9,
72987
72905
  mkdirSync as mkdirSync34,
72988
- readdirSync as readdirSync22,
72906
+ readdirSync as readdirSync23,
72989
72907
  rmSync as rmSync15,
72990
72908
  writeFileSync as writeFileSync30
72991
72909
  } from "node:fs";
@@ -73158,7 +73076,7 @@ function resolveCredentialsDir(env2) {
73158
73076
  function writeSeedFile(dir, email, seed) {
73159
73077
  mkdirSync34(dir, { recursive: true, mode: 448 });
73160
73078
  chmodSync9(dir, 448);
73161
- for (const name of readdirSync22(dir)) {
73079
+ for (const name of readdirSync23(dir)) {
73162
73080
  rmSync15(join56(dir, name), { force: true, recursive: true });
73163
73081
  }
73164
73082
  const filename = encodeCredentialsFilename(email);
@@ -73278,9 +73196,9 @@ async function runDriveMcpLauncher(opts) {
73278
73196
  const tier = opts.tier ?? configSecrets.tier;
73279
73197
  const args = buildUvxArgs(tier);
73280
73198
  const env2 = buildChildEnv(process.env, credentialsDir, brokerCreds.accountEmail);
73281
- const { spawn: spawn6 } = await import("node:child_process");
73199
+ const { spawn: spawn5 } = await import("node:child_process");
73282
73200
  const os5 = await import("node:os");
73283
- const child = spawn6("uvx", args, {
73201
+ const child = spawn5("uvx", args, {
73284
73202
  stdio: ["pipe", "pipe", "inherit"],
73285
73203
  env: env2
73286
73204
  });
@@ -73315,7 +73233,7 @@ function registerDriveMcpLauncherCommand(program3) {
73315
73233
 
73316
73234
  // src/cli/apply.ts
73317
73235
  init_source();
73318
- import { accessSync as accessSync3, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync67, mkdirSync as mkdirSync36, readdirSync as readdirSync24, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
73236
+ import { accessSync as accessSync3, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync67, mkdirSync as mkdirSync36, readdirSync as readdirSync25, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
73319
73237
  import { mkdir, writeFile } from "node:fs/promises";
73320
73238
  import { spawnSync as childSpawnSync } from "node:child_process";
73321
73239
  import readline from "node:readline";
@@ -73872,7 +73790,7 @@ import {
73872
73790
  chownSync as chownSync2,
73873
73791
  existsSync as existsSync66,
73874
73792
  lstatSync as lstatSync7,
73875
- readdirSync as readdirSync23,
73793
+ readdirSync as readdirSync24,
73876
73794
  realpathSync as realpathSync6,
73877
73795
  statSync as statSync26
73878
73796
  } from "node:fs";
@@ -73928,7 +73846,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
73928
73846
  });
73929
73847
  const readdir2 = deps.readdir ?? ((p) => {
73930
73848
  try {
73931
- return readdirSync23(p);
73849
+ return readdirSync24(p);
73932
73850
  } catch {
73933
73851
  return [];
73934
73852
  }
@@ -73974,7 +73892,7 @@ function resolveVaultBindMountDir(homeDir, ctx) {
73974
73892
  function inspectVaultBindMountDir(vaultDir) {
73975
73893
  if (!existsSync67(vaultDir))
73976
73894
  return { kind: "missing" };
73977
- const entries = readdirSync24(vaultDir);
73895
+ const entries = readdirSync25(vaultDir);
73978
73896
  const unknown = [];
73979
73897
  for (const name of entries) {
73980
73898
  if (KNOWN_VAULT_ARTIFACT_NAMES.has(name))
@@ -74544,7 +74462,7 @@ function runRedactStdin() {
74544
74462
  }
74545
74463
 
74546
74464
  // src/cli/status-ask.ts
74547
- import { readFileSync as readFileSync55, existsSync as existsSync68, readdirSync as readdirSync25 } from "node:fs";
74465
+ import { readFileSync as readFileSync55, existsSync as existsSync68, readdirSync as readdirSync26 } from "node:fs";
74548
74466
  import { join as join61 } from "node:path";
74549
74467
  import { homedir as homedir34 } from "node:os";
74550
74468
 
@@ -74888,7 +74806,7 @@ function resolveSources(explicitPath) {
74888
74806
  const sources = [];
74889
74807
  let entries;
74890
74808
  try {
74891
- entries = readdirSync25(agentsDir);
74809
+ entries = readdirSync26(agentsDir);
74892
74810
  } catch {
74893
74811
  return [];
74894
74812
  }
@@ -75176,7 +75094,7 @@ import {
75176
75094
  fsyncSync as fsyncSync6,
75177
75095
  mkdirSync as mkdirSync38,
75178
75096
  openSync as openSync13,
75179
- readdirSync as readdirSync26,
75097
+ readdirSync as readdirSync27,
75180
75098
  readFileSync as readFileSync57,
75181
75099
  renameSync as renameSync14,
75182
75100
  statSync as statSync27,
@@ -75296,7 +75214,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
75296
75214
  if (!existsSync70(paths.skillsDir))
75297
75215
  return [];
75298
75216
  const out = [];
75299
- for (const name of readdirSync26(paths.skillsDir)) {
75217
+ for (const name of readdirSync27(paths.skillsDir)) {
75300
75218
  if (!/\.ya?ml$/i.test(name))
75301
75219
  continue;
75302
75220
  const full = join63(paths.skillsDir, name);
@@ -75323,7 +75241,7 @@ function listOverlayEntries(agent, opts = {}) {
75323
75241
  if (!existsSync70(paths.scheduleDir))
75324
75242
  return [];
75325
75243
  const out = [];
75326
- for (const name of readdirSync26(paths.scheduleDir)) {
75244
+ for (const name of readdirSync27(paths.scheduleDir)) {
75327
75245
  if (!/\.ya?ml$/i.test(name))
75328
75246
  continue;
75329
75247
  const full = join63(paths.scheduleDir, name);
@@ -75475,7 +75393,7 @@ import {
75475
75393
  fsyncSync as fsyncSync7,
75476
75394
  mkdirSync as mkdirSync39,
75477
75395
  openSync as openSync14,
75478
- readdirSync as readdirSync27,
75396
+ readdirSync as readdirSync28,
75479
75397
  readFileSync as readFileSync58,
75480
75398
  renameSync as renameSync15,
75481
75399
  unlinkSync as unlinkSync15,
@@ -75531,7 +75449,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
75531
75449
  if (!existsSync71(dir))
75532
75450
  return [];
75533
75451
  const out = [];
75534
- for (const name of readdirSync27(dir).sort()) {
75452
+ for (const name of readdirSync28(dir).sort()) {
75535
75453
  if (!name.endsWith(".meta.json"))
75536
75454
  continue;
75537
75455
  const stageId = name.slice(0, -".meta.json".length);
@@ -76269,7 +76187,7 @@ init_helpers();
76269
76187
  init_loader();
76270
76188
  import {
76271
76189
  existsSync as existsSync75,
76272
- readdirSync as readdirSync28,
76190
+ readdirSync as readdirSync29,
76273
76191
  readFileSync as readFileSync61,
76274
76192
  renameSync as renameSync16,
76275
76193
  statSync as statSync28,
@@ -76288,7 +76206,7 @@ function planCronUnitRenames(agentsDir, agents) {
76288
76206
  continue;
76289
76207
  let entries;
76290
76208
  try {
76291
- entries = readdirSync28(telegramDir);
76209
+ entries = readdirSync29(telegramDir);
76292
76210
  } catch {
76293
76211
  continue;
76294
76212
  }
@@ -76443,7 +76361,7 @@ function registerMigrateCommand(program3) {
76443
76361
  // src/cli/hostd.ts
76444
76362
  init_source();
76445
76363
  init_helpers();
76446
- import { existsSync as existsSync76, mkdirSync as mkdirSync40, readdirSync as readdirSync29, readFileSync as readFileSync62, writeFileSync as writeFileSync34, statSync as statSync29, copyFileSync as copyFileSync12 } from "node:fs";
76364
+ import { existsSync as existsSync76, mkdirSync as mkdirSync40, readdirSync as readdirSync30, readFileSync as readFileSync62, writeFileSync as writeFileSync34, statSync as statSync29, copyFileSync as copyFileSync12 } from "node:fs";
76447
76365
  import { homedir as homedir36 } from "node:os";
76448
76366
  import { join as join67 } from "node:path";
76449
76367
  import { spawnSync as spawnSync11 } from "node:child_process";
@@ -76644,7 +76562,7 @@ function doStatus() {
76644
76562
  if (existsSync76(dir)) {
76645
76563
  const entries = [];
76646
76564
  try {
76647
- for (const name of readdirSync29(dir)) {
76565
+ for (const name of readdirSync30(dir)) {
76648
76566
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
76649
76567
  continue;
76650
76568
  const sockPath = join67(dir, name, "sock");