@turmind/halo 1.5.6 → 1.5.7

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 (31) hide show
  1. package/admin-out/404.html +1 -1
  2. package/admin-out/index.html +1 -1
  3. package/admin-out/index.txt +1 -1
  4. package/bundled-docs/design/agent.md +7 -7
  5. package/bundled-docs/design/architecture.md +12 -11
  6. package/bundled-docs/design/storage.md +2 -1
  7. package/bundled-docs/dev/add-model-provider.md +33 -24
  8. package/bundled-docs/dev/deploy.md +1 -1
  9. package/bundled-docs/dev/desktop-packaging.md +4 -3
  10. package/bundled-docs/dev/env.md +1 -1
  11. package/bundled-docs/guide/agents.md +2 -2
  12. package/bundled-docs/guide/cli.md +5 -4
  13. package/bundled-docs/guide/getting-started.md +1 -1
  14. package/bundled-docs/requirements/settings.md +2 -1
  15. package/dist/index.js +409 -388
  16. package/package.json +1 -1
  17. package/templates/models/anthropic.yaml +1 -0
  18. package/templates/models/aws-bedrock-claude-invoke.yaml +1 -0
  19. package/templates/models/aws-bedrock-mantle.yaml +1 -0
  20. package/templates/models/aws-bedrock-openai.yaml +1 -0
  21. package/templates/models/deepseek.yaml +1 -0
  22. package/templates/models/doubao.yaml +1 -0
  23. package/templates/models/hunyuan.yaml +1 -0
  24. package/templates/models/kimi.yaml +1 -0
  25. package/templates/models/mimo-token-plan-china.yaml +1 -0
  26. package/templates/models/minimax.yaml +1 -0
  27. package/templates/models/openai.yaml +1 -0
  28. package/templates/models/qwen.yaml +1 -0
  29. package/templates/models/zhipu.yaml +1 -0
  30. /package/admin-out/_next/static/{visjvN510yFK-Ff5zwx-0 → rTD3zkMvzwtqUwzhz6QJH}/_buildManifest.js +0 -0
  31. /package/admin-out/_next/static/{visjvN510yFK-Ff5zwx-0 → rTD3zkMvzwtqUwzhz6QJH}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -3102,6 +3102,7 @@ __export(init_exports, {
3102
3102
  mergeAgentYaml: () => mergeAgentYaml,
3103
3103
  readInstalledOptionalSkills: () => readInstalledOptionalSkills,
3104
3104
  readSeedVersion: () => readSeedVersion,
3105
+ refreshTemplatesIfOutdated: () => refreshTemplatesIfOutdated,
3105
3106
  writeFileAtomic: () => writeFileAtomic,
3106
3107
  writeInstalledOptionalSkills: () => writeInstalledOptionalSkills
3107
3108
  });
@@ -3124,6 +3125,17 @@ function readSeedVersion(haloHome2) {
3124
3125
  return 0;
3125
3126
  }
3126
3127
  }
3128
+ function refreshTemplatesIfOutdated(haloHome2, logTag) {
3129
+ const seedVersion = readSeedVersion(haloHome2);
3130
+ if (seedVersion <= 0 || seedVersion >= TEMPLATE_VERSION)
3131
+ return;
3132
+ console.log(`[${logTag}] Templates outdated (v${seedVersion} \u2192 v${TEMPLATE_VERSION}), refreshing ~/.halo/global/`);
3133
+ try {
3134
+ ensureHaloHome(haloHome2);
3135
+ } catch (err) {
3136
+ console.error(`[${logTag}] Template refresh failed: ${err instanceof Error ? err.message : String(err)}`);
3137
+ }
3138
+ }
3127
3139
  function writeFileAtomic(filePath, content, mode) {
3128
3140
  fs7.mkdirSync(path9.dirname(filePath), { recursive: true });
3129
3141
  const tmpPath = `${filePath}.tmp`;
@@ -3455,7 +3467,7 @@ var init_init = __esm({
3455
3467
  path9.resolve(__dirname, "..", "bundled-docs"),
3456
3468
  path9.resolve(__dirname, "..", "..", "..", ".halo", "docs")
3457
3469
  ];
3458
- TEMPLATE_VERSION = 81;
3470
+ TEMPLATE_VERSION = 82;
3459
3471
  VERSION_FILE = ".template-version";
3460
3472
  SKIP_NAMES = /* @__PURE__ */ new Set([".DS_Store", "schema.sql", "__pycache__", ".pytest_cache"]);
3461
3473
  BUILTIN_AGENT_IDS = /* @__PURE__ */ new Set([
@@ -7144,27 +7156,27 @@ var init_workspace = __esm({
7144
7156
  constructor(projectRoot) {
7145
7157
  this.projectRoot = projectRoot;
7146
7158
  }
7147
- async readFile(path73) {
7148
- const fullPath = this.validatePath(path73);
7159
+ async readFile(path74) {
7160
+ const fullPath = this.validatePath(path74);
7149
7161
  return readFile(fullPath, "utf-8");
7150
7162
  }
7151
- async writeFile(path73, content) {
7152
- const fullPath = this.validatePath(path73);
7163
+ async writeFile(path74, content) {
7164
+ const fullPath = this.validatePath(path74);
7153
7165
  const dir = resolve(fullPath, "..");
7154
7166
  await mkdir(dir, { recursive: true });
7155
7167
  await writeFile(fullPath, content, "utf-8");
7156
7168
  }
7157
- validatePath(path73) {
7158
- const fullPath = isAbsolute(path73) ? resolve(path73) : resolve(this.projectRoot, path73);
7169
+ validatePath(path74) {
7170
+ const fullPath = isAbsolute(path74) ? resolve(path74) : resolve(this.projectRoot, path74);
7159
7171
  const root = resolve(this.projectRoot);
7160
7172
  if (fullPath !== root && !fullPath.startsWith(root + sep)) {
7161
- throw new WorkspaceError(`Path "${path73}" is outside the workspace directory "${this.projectRoot}"`);
7173
+ throw new WorkspaceError(`Path "${path74}" is outside the workspace directory "${this.projectRoot}"`);
7162
7174
  }
7163
7175
  try {
7164
7176
  const real = realpathSync2(fullPath);
7165
7177
  const realRoot = realpathSync2(root);
7166
7178
  if (real !== realRoot && !real.startsWith(realRoot + sep)) {
7167
- throw new WorkspaceError(`Path "${path73}" is outside the workspace directory "${this.projectRoot}"`);
7179
+ throw new WorkspaceError(`Path "${path74}" is outside the workspace directory "${this.projectRoot}"`);
7168
7180
  }
7169
7181
  } catch (err) {
7170
7182
  if (err instanceof WorkspaceError)
@@ -7351,12 +7363,12 @@ var init_git_manager = __esm({
7351
7363
  const code = parts[0];
7352
7364
  const status = code[0];
7353
7365
  const isRenameOrCopy = status === "R" || status === "C";
7354
- const path73 = isRenameOrCopy ? parts[2] : parts[1];
7355
- if (!path73 || seen.has(path73))
7366
+ const path74 = isRenameOrCopy ? parts[2] : parts[1];
7367
+ if (!path74 || seen.has(path74))
7356
7368
  continue;
7357
- seen.add(path73);
7369
+ seen.add(path74);
7358
7370
  files.push({
7359
- path: path73,
7371
+ path: path74,
7360
7372
  status,
7361
7373
  from: isRenameOrCopy ? parts[1] : void 0
7362
7374
  });
@@ -7504,9 +7516,9 @@ var init_git_manager = __esm({
7504
7516
  * empty for an added file or the root commit) and `modified` is the version
7505
7517
  * at `commit` (empty for a deletion). The working-tree/staged path above is
7506
7518
  * left entirely untouched (commit === undefined preserves existing behavior). */
7507
- async getFileDiff(path73, staged, from, commit) {
7519
+ async getFileDiff(path74, staged, from, commit) {
7508
7520
  if (commit) {
7509
- const parentPath = from || path73;
7521
+ const parentPath = from || path74;
7510
7522
  let original2 = "";
7511
7523
  try {
7512
7524
  original2 = await this.git.show([`${commit}^:${parentPath}`]);
@@ -7515,13 +7527,13 @@ var init_git_manager = __esm({
7515
7527
  }
7516
7528
  let modified2 = "";
7517
7529
  try {
7518
- modified2 = await this.git.show([`${commit}:${path73}`]);
7530
+ modified2 = await this.git.show([`${commit}:${path74}`]);
7519
7531
  } catch {
7520
7532
  modified2 = "";
7521
7533
  }
7522
7534
  return { original: original2, modified: modified2 };
7523
7535
  }
7524
- const headPath = from || path73;
7536
+ const headPath = from || path74;
7525
7537
  let original = "";
7526
7538
  try {
7527
7539
  original = await this.git.show([`HEAD:${headPath}`]);
@@ -7531,13 +7543,13 @@ var init_git_manager = __esm({
7531
7543
  let modified = "";
7532
7544
  if (staged) {
7533
7545
  try {
7534
- modified = await this.git.show([`:${path73}`]);
7546
+ modified = await this.git.show([`:${path74}`]);
7535
7547
  } catch {
7536
7548
  modified = "";
7537
7549
  }
7538
7550
  } else {
7539
7551
  try {
7540
- modified = await this.workspace.readFile(path73);
7552
+ modified = await this.workspace.readFile(path74);
7541
7553
  } catch {
7542
7554
  modified = "";
7543
7555
  }
@@ -10239,12 +10251,29 @@ var init_anthropic_agent = __esm({
10239
10251
  });
10240
10252
 
10241
10253
  // ../server/dist/agents/model-runtime.js
10242
- function createModelRuntime(providerId, cfg) {
10243
- switch (providerId) {
10244
- case "aws-bedrock-claude-invoke":
10254
+ import path28 from "node:path";
10255
+ function resolveProviderRuntime(providerId) {
10256
+ const yamlPath = path28.join(HALO_GLOBAL_DIR, "models", `${providerId}.yaml`);
10257
+ const providers2 = getModelsRegistry().providers;
10258
+ const entry = providers2.find((p) => p.id === providerId);
10259
+ if (!entry) {
10260
+ throw new Error(`[model-runtime] Unknown provider "${providerId}": no provider yaml with that id (expected ${yamlPath}). Check agent.yaml model.provider, or run \`halo setup\` to install the bundled providers.`);
10261
+ }
10262
+ const runtime = entry.runtime;
10263
+ if (typeof runtime !== "string" || !runtime) {
10264
+ throw new Error(`[model-runtime] Provider "${providerId}" has no \`runtime:\` field in ${yamlPath}. Run \`halo setup\` or restart the Halo server to refresh templates.`);
10265
+ }
10266
+ if (!MODEL_RUNTIME_NAMES.includes(runtime)) {
10267
+ throw new Error(`[model-runtime] Provider "${providerId}" names unknown runtime "${runtime}" in ${yamlPath}. Valid runtimes: ${MODEL_RUNTIME_NAMES.join(", ")}.`);
10268
+ }
10269
+ return runtime;
10270
+ }
10271
+ function createModelRuntime(runtime, cfg) {
10272
+ switch (runtime) {
10273
+ case "bedrock-invoke":
10245
10274
  return new BedrockAgent({
10246
10275
  modelId: cfg.modelId,
10247
- endpoint: cfg.endpoint ?? "https://bedrock-runtime.us-east-1.amazonaws.com",
10276
+ endpoint: cfg.endpoint,
10248
10277
  systemPrompt: cfg.systemPrompt,
10249
10278
  tools: cfg.tools,
10250
10279
  maxTokens: cfg.maxTokens,
@@ -10257,7 +10286,7 @@ function createModelRuntime(providerId, cfg) {
10257
10286
  case "kimi":
10258
10287
  return new KimiAgent({
10259
10288
  modelId: cfg.modelId,
10260
- endpoint: cfg.endpoint ?? "https://api.moonshot.cn/v1",
10289
+ endpoint: cfg.endpoint,
10261
10290
  apiKey: cfg.apiKey ?? "",
10262
10291
  systemPrompt: cfg.systemPrompt,
10263
10292
  tools: cfg.tools,
@@ -10268,7 +10297,7 @@ function createModelRuntime(providerId, cfg) {
10268
10297
  case "deepseek":
10269
10298
  return new DeepSeekAgent({
10270
10299
  modelId: cfg.modelId,
10271
- endpoint: cfg.endpoint ?? "https://api.deepseek.com",
10300
+ endpoint: cfg.endpoint,
10272
10301
  apiKey: cfg.apiKey ?? "",
10273
10302
  systemPrompt: cfg.systemPrompt,
10274
10303
  tools: cfg.tools,
@@ -10278,7 +10307,7 @@ function createModelRuntime(providerId, cfg) {
10278
10307
  case "minimax":
10279
10308
  return new MiniMaxAgent({
10280
10309
  modelId: cfg.modelId,
10281
- endpoint: cfg.endpoint ?? "https://api.minimaxi.com/anthropic",
10310
+ endpoint: cfg.endpoint,
10282
10311
  apiKey: cfg.apiKey ?? "",
10283
10312
  systemPrompt: cfg.systemPrompt,
10284
10313
  tools: cfg.tools,
@@ -10291,7 +10320,7 @@ function createModelRuntime(providerId, cfg) {
10291
10320
  case "qwen":
10292
10321
  return new QwenAgent({
10293
10322
  modelId: cfg.modelId,
10294
- endpoint: cfg.endpoint ?? "https://dashscope.aliyuncs.com/apps/anthropic",
10323
+ endpoint: cfg.endpoint,
10295
10324
  apiKey: cfg.apiKey ?? "",
10296
10325
  systemPrompt: cfg.systemPrompt,
10297
10326
  tools: cfg.tools,
@@ -10303,7 +10332,7 @@ function createModelRuntime(providerId, cfg) {
10303
10332
  case "hunyuan":
10304
10333
  return new HunyuanAgent({
10305
10334
  modelId: cfg.modelId,
10306
- endpoint: cfg.endpoint ?? "https://tokenhub.tencentmaas.com/v1",
10335
+ endpoint: cfg.endpoint,
10307
10336
  apiKey: cfg.apiKey ?? "",
10308
10337
  systemPrompt: cfg.systemPrompt,
10309
10338
  tools: cfg.tools,
@@ -10313,7 +10342,7 @@ function createModelRuntime(providerId, cfg) {
10313
10342
  case "doubao":
10314
10343
  return new DoubaoAgent({
10315
10344
  modelId: cfg.modelId,
10316
- endpoint: cfg.endpoint ?? "https://ark.cn-beijing.volces.com/api/v3",
10345
+ endpoint: cfg.endpoint,
10317
10346
  apiKey: cfg.apiKey ?? "",
10318
10347
  systemPrompt: cfg.systemPrompt,
10319
10348
  tools: cfg.tools,
@@ -10323,32 +10352,30 @@ function createModelRuntime(providerId, cfg) {
10323
10352
  case "zhipu":
10324
10353
  return new ZhipuAgent({
10325
10354
  modelId: cfg.modelId,
10326
- endpoint: cfg.endpoint ?? "https://open.bigmodel.cn/api/paas/v4",
10355
+ endpoint: cfg.endpoint,
10327
10356
  apiKey: cfg.apiKey ?? "",
10328
10357
  systemPrompt: cfg.systemPrompt,
10329
10358
  tools: cfg.tools,
10330
10359
  maxTokens: cfg.maxTokens,
10331
10360
  thinking: cfg.thinking
10332
10361
  });
10333
- case "openai":
10362
+ case "openai-chat":
10334
10363
  return new OpenAIAgent({
10335
10364
  modelId: cfg.modelId,
10336
- endpoint: cfg.endpoint ?? "https://api.openai.com/v1",
10365
+ endpoint: cfg.endpoint,
10337
10366
  apiKey: cfg.apiKey ?? "",
10338
10367
  systemPrompt: cfg.systemPrompt,
10339
10368
  tools: cfg.tools,
10340
10369
  maxTokens: cfg.maxTokens,
10341
10370
  thinking: cfg.thinking
10342
10371
  });
10343
- // aws-bedrock-openai is the same OpenAI Responses wire format on the
10344
- // bedrock-runtime host (/openai/v1, cross-Region profile ids only) —
10345
- // Grok 4.6 / Kimi K3 live there and not (or not everywhere) on Mantle.
10346
- // See aws-bedrock-openai.yaml.
10347
- case "aws-bedrock-mantle":
10348
- case "aws-bedrock-openai":
10372
+ // OpenAI Responses API on Bedrock — serves both aws-bedrock-mantle
10373
+ // (bedrock-mantle host) and aws-bedrock-openai (bedrock-runtime host,
10374
+ // /openai/v1); the endpoint alone picks the host and SigV4 region.
10375
+ case "bedrock-mantle":
10349
10376
  return new MantleAgent({
10350
10377
  modelId: cfg.modelId,
10351
- endpoint: cfg.endpoint ?? (providerId === "aws-bedrock-openai" ? "https://bedrock-runtime.us-east-1.amazonaws.com/openai/v1" : "https://bedrock-mantle.us-east-1.api.aws/openai/v1"),
10378
+ endpoint: cfg.endpoint,
10352
10379
  // No bearer token → MantleAgent falls back to SigV4 IAM auth using
10353
10380
  // these creds (or the SDK default chain when also empty).
10354
10381
  apiKey: cfg.apiKey ?? "",
@@ -10359,22 +10386,10 @@ function createModelRuntime(providerId, cfg) {
10359
10386
  thinking: cfg.thinking,
10360
10387
  verbosity: cfg.verbosity
10361
10388
  });
10362
- case "anthropic":
10389
+ case "anthropic-messages":
10363
10390
  return new AnthropicAgent({
10364
10391
  modelId: cfg.modelId,
10365
- endpoint: cfg.endpoint ?? "https://api.anthropic.com",
10366
- apiKey: cfg.apiKey ?? "",
10367
- systemPrompt: cfg.systemPrompt,
10368
- tools: cfg.tools,
10369
- maxTokens: cfg.maxTokens,
10370
- promptCaching: cfg.promptCaching,
10371
- thinking: cfg.thinking,
10372
- thinkingBudgetTokens: cfg.thinkingBudgetTokens
10373
- });
10374
- case "mimo-token-plan-china":
10375
- return new AnthropicAgent({
10376
- modelId: cfg.modelId,
10377
- endpoint: cfg.endpoint ?? "https://token-plan-cn.xiaomimimo.com/anthropic",
10392
+ endpoint: cfg.endpoint,
10378
10393
  apiKey: cfg.apiKey ?? "",
10379
10394
  systemPrompt: cfg.systemPrompt,
10380
10395
  tools: cfg.tools,
@@ -10384,9 +10399,10 @@ function createModelRuntime(providerId, cfg) {
10384
10399
  thinkingBudgetTokens: cfg.thinkingBudgetTokens
10385
10400
  });
10386
10401
  default:
10387
- throw new Error(`[model-runtime] Unknown provider "${providerId}". Check agent.yaml model.provider and <global>/models/*.yaml.`);
10402
+ throw new Error(`[model-runtime] Unknown runtime "${runtime}". Valid runtimes: ${MODEL_RUNTIME_NAMES.join(", ")}.`);
10388
10403
  }
10389
10404
  }
10405
+ var MODEL_RUNTIME_NAMES;
10390
10406
  var init_model_runtime = __esm({
10391
10407
  "../server/dist/agents/model-runtime.js"() {
10392
10408
  "use strict";
@@ -10401,17 +10417,31 @@ var init_model_runtime = __esm({
10401
10417
  init_openai_agent();
10402
10418
  init_mantle_agent();
10403
10419
  init_anthropic_agent();
10420
+ init_config();
10421
+ MODEL_RUNTIME_NAMES = [
10422
+ "anthropic-messages",
10423
+ "openai-chat",
10424
+ "bedrock-invoke",
10425
+ "bedrock-mantle",
10426
+ "kimi",
10427
+ "deepseek",
10428
+ "minimax",
10429
+ "qwen",
10430
+ "hunyuan",
10431
+ "doubao",
10432
+ "zhipu"
10433
+ ];
10404
10434
  }
10405
10435
  });
10406
10436
 
10407
10437
  // ../server/dist/tools/workspace-tools.js
10408
10438
  import fs24 from "node:fs/promises";
10409
- import path28 from "node:path";
10439
+ import path29 from "node:path";
10410
10440
  import crypto from "node:crypto";
10411
10441
  import { homedir as homedir11 } from "node:os";
10412
10442
  function resolvePath2(filePath, workspaceRoot) {
10413
- const expanded = filePath.startsWith("~/") ? path28.join(HOME2, filePath.slice(2)) : filePath;
10414
- return path28.isAbsolute(expanded) ? path28.resolve(expanded) : path28.resolve(workspaceRoot, expanded);
10443
+ const expanded = filePath.startsWith("~/") ? path29.join(HOME2, filePath.slice(2)) : filePath;
10444
+ return path29.isAbsolute(expanded) ? path29.resolve(expanded) : path29.resolve(workspaceRoot, expanded);
10415
10445
  }
10416
10446
  async function* walkDir(dir, signal) {
10417
10447
  if (signal?.aborted)
@@ -10427,7 +10457,7 @@ async function* walkDir(dir, signal) {
10427
10457
  return;
10428
10458
  if (SKIP_DIRS.has(name))
10429
10459
  continue;
10430
- const fullPath = path28.join(dir, name);
10460
+ const fullPath = path29.join(dir, name);
10431
10461
  let stat;
10432
10462
  try {
10433
10463
  stat = await fs24.lstat(fullPath);
@@ -10435,7 +10465,7 @@ async function* walkDir(dir, signal) {
10435
10465
  continue;
10436
10466
  }
10437
10467
  if (stat.isDirectory()) {
10438
- if (path28.basename(dir) === ".halo" && SKIP_HALO_SUBDIRS.has(name))
10468
+ if (path29.basename(dir) === ".halo" && SKIP_HALO_SUBDIRS.has(name))
10439
10469
  continue;
10440
10470
  if (process.platform === "win32" && await isReparsePoint(fullPath))
10441
10471
  continue;
@@ -10663,7 +10693,7 @@ function createWorkspaceTools(workspaceRoot, accessLevelOrOpts = "full") {
10663
10693
  callback: async (_input) => {
10664
10694
  const input = _input;
10665
10695
  const fullPath = resolvePath2(input.path, workspaceRoot);
10666
- const ext = path28.extname(fullPath).toLowerCase();
10696
+ const ext = path29.extname(fullPath).toLowerCase();
10667
10697
  const supportedExts = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
10668
10698
  if (!supportedExts.has(ext)) {
10669
10699
  return `${TOOL_WARN_MARKER}
@@ -10860,7 +10890,7 @@ Error: old_string appears more than once in ${input.path}. Either expand it with
10860
10890
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
10861
10891
  if (entry.isDirectory) {
10862
10892
  results.push(`\u{1F4C1} ${relPath}/`);
10863
- await walk(path28.join(dir, entry.name), relPath);
10893
+ await walk(path29.join(dir, entry.name), relPath);
10864
10894
  } else {
10865
10895
  results.push(`\u{1F4C4} ${relPath}`);
10866
10896
  }
@@ -10969,7 +10999,7 @@ Error: Invalid regex pattern: ${err.message}`;
10969
10999
  for await (const filePath of files) {
10970
11000
  if (results.length >= maxResults)
10971
11001
  break;
10972
- if (input.include && !matchInclude(path28.basename(filePath), input.include)) {
11002
+ if (input.include && !matchInclude(path29.basename(filePath), input.include)) {
10973
11003
  continue;
10974
11004
  }
10975
11005
  if (await isBinaryFile(filePath))
@@ -10980,7 +11010,7 @@ Error: Invalid regex pattern: ${err.message}`;
10980
11010
  } catch {
10981
11011
  continue;
10982
11012
  }
10983
- const relativePath = path28.relative(workspaceRoot, filePath);
11013
+ const relativePath = path29.relative(workspaceRoot, filePath);
10984
11014
  const lines = content.split("\n");
10985
11015
  for (let i = 0; i < lines.length; i++) {
10986
11016
  if (results.length >= maxResults)
@@ -11025,7 +11055,7 @@ Error: Invalid regex pattern: ${err.message}`;
11025
11055
  for await (const filePath of walkDir(searchDir, signal)) {
11026
11056
  if (results.length >= GLOB_MAX_RESULTS)
11027
11057
  break;
11028
- const relativePath = path28.relative(workspaceRoot, filePath);
11058
+ const relativePath = path29.relative(workspaceRoot, filePath);
11029
11059
  if (matchGlob(relativePath, input.pattern)) {
11030
11060
  results.push(relativePath);
11031
11061
  }
@@ -11150,7 +11180,7 @@ var init_workspace_tools = __esm({
11150
11180
 
11151
11181
  // ../server/dist/prompts/system-prompts.js
11152
11182
  import fs25 from "node:fs/promises";
11153
- import path29 from "node:path";
11183
+ import path30 from "node:path";
11154
11184
  import { homedir as homedir12 } from "node:os";
11155
11185
  async function dirExists(dirPath) {
11156
11186
  try {
@@ -11186,7 +11216,7 @@ async function loadDir(dirPath, fallback) {
11186
11216
  const chunks = [];
11187
11217
  const loaded = [];
11188
11218
  for (const name of mdFiles) {
11189
- const fullPath = path29.join(dirPath, name);
11219
+ const fullPath = path30.join(dirPath, name);
11190
11220
  try {
11191
11221
  const content = (await fs25.readFile(fullPath, "utf-8")).trim();
11192
11222
  if (content) {
@@ -11203,11 +11233,11 @@ async function loadDir(dirPath, fallback) {
11203
11233
  }
11204
11234
  async function resolvePromptsDir(scope, workspaceRoot) {
11205
11235
  if (workspaceRoot) {
11206
- const wsDir = path29.join(workspaceRoot, ".halo", "prompts", scope);
11236
+ const wsDir = path30.join(workspaceRoot, ".halo", "prompts", scope);
11207
11237
  if (await dirExists(wsDir))
11208
11238
  return wsDir;
11209
11239
  }
11210
- return path29.join(GLOBAL_PROMPTS_DIR, scope);
11240
+ return path30.join(GLOBAL_PROMPTS_DIR, scope);
11211
11241
  }
11212
11242
  async function loadSystemPrompts(workspaceRoot) {
11213
11243
  const [bootstrapDir, allDir, rootDir] = await Promise.all([
@@ -11236,12 +11266,12 @@ var GLOBAL_PROMPTS_DIR;
11236
11266
  var init_system_prompts = __esm({
11237
11267
  "../server/dist/prompts/system-prompts.js"() {
11238
11268
  "use strict";
11239
- GLOBAL_PROMPTS_DIR = path29.join(homedir12(), ".halo", "global", "prompts");
11269
+ GLOBAL_PROMPTS_DIR = path30.join(homedir12(), ".halo", "global", "prompts");
11240
11270
  }
11241
11271
  });
11242
11272
 
11243
11273
  // ../server/dist/agents/session-agent-builder.js
11244
- import path30 from "node:path";
11274
+ import path31 from "node:path";
11245
11275
  var SessionAgentBuilder;
11246
11276
  var init_session_agent_builder = __esm({
11247
11277
  "../server/dist/agents/session-agent-builder.js"() {
@@ -11316,7 +11346,7 @@ var init_session_agent_builder = __esm({
11316
11346
  * (yamlConfig null — read/parse failed, see loadAgentYaml) from "config
11317
11347
  * genuinely incomplete", so the error is self-diagnosing. */
11318
11348
  validateAgentModelConfig(agentId, yamlConfig) {
11319
- const yamlPath = path30.join(agentSourceDir(agentId, this.host.workspaceRoot), "agent.yaml");
11349
+ const yamlPath = path31.join(agentSourceDir(agentId, this.host.workspaceRoot), "agent.yaml");
11320
11350
  if (yamlConfig === null) {
11321
11351
  throw new Error(`[SessionManager] Agent "${agentId}": agent.yaml missing or unreadable at ${yamlPath}.`);
11322
11352
  }
@@ -11403,7 +11433,7 @@ var init_session_agent_builder = __esm({
11403
11433
 
11404
11434
  The project workspace is at: ${this.host.workspaceRoot}
11405
11435
  `;
11406
- if (workingDir && path30.resolve(workingDir) !== path30.resolve(this.host.workspaceRoot)) {
11436
+ if (workingDir && path31.resolve(workingDir) !== path31.resolve(this.host.workspaceRoot)) {
11407
11437
  systemPrompt += `Working directory: ${workingDir}
11408
11438
  `;
11409
11439
  }
@@ -11532,7 +11562,7 @@ ${roster}`;
11532
11562
  const aws = resolveAwsCredentials(providerId, this.host.workspaceRoot);
11533
11563
  const awsCreds = aws.accessKeyId && aws.secretAccessKey ? aws : void 0;
11534
11564
  const apiKey = resolveApiKey(providerId, this.host.workspaceRoot);
11535
- const agent = createModelRuntime(providerId, {
11565
+ const agent = createModelRuntime(resolveProviderRuntime(providerId), {
11536
11566
  modelId,
11537
11567
  endpoint,
11538
11568
  systemPrompt,
@@ -11587,7 +11617,7 @@ ${roster}`;
11587
11617
  const loaded = systemPrompts.files[scope];
11588
11618
  if (loaded.length > 0) {
11589
11619
  for (const file of loaded) {
11590
- mdFiles.push({ label: `prompt/${scope}/${path30.basename(file)}`, path: file });
11620
+ mdFiles.push({ label: `prompt/${scope}/${path31.basename(file)}`, path: file });
11591
11621
  }
11592
11622
  } else {
11593
11623
  mdFiles.push({ label: `prompt/${scope} (built-in fallback)`, path: systemPrompts.dirs[scope] });
@@ -11693,7 +11723,7 @@ var init_session_skill_commands = __esm({
11693
11723
 
11694
11724
  // ../server/dist/agents/session-state-store.js
11695
11725
  import fsSync6 from "node:fs";
11696
- import path31 from "node:path";
11726
+ import path32 from "node:path";
11697
11727
  var SessionStateStore;
11698
11728
  var init_session_state_store = __esm({
11699
11729
  "../server/dist/agents/session-state-store.js"() {
@@ -11720,7 +11750,7 @@ var init_session_state_store = __esm({
11720
11750
  try {
11721
11751
  const dir = this.sessionDir(session.agentId);
11722
11752
  fsSync6.mkdirSync(dir, { recursive: true });
11723
- const filePath = path31.join(dir, `${fileSegment(session.id)}.json`);
11753
+ const filePath = path32.join(dir, `${fileSegment(session.id)}.json`);
11724
11754
  let existing = {};
11725
11755
  try {
11726
11756
  existing = JSON.parse(fsSync6.readFileSync(filePath, "utf-8"));
@@ -11749,7 +11779,7 @@ var init_session_state_store = __esm({
11749
11779
  /** Load agent.messages from the delegated log file's rawMessages field */
11750
11780
  loadAgentState(sessionId, agentId) {
11751
11781
  try {
11752
- const filePath = path31.join(this.sessionDir(agentId), `${fileSegment(sessionId)}.json`);
11782
+ const filePath = path32.join(this.sessionDir(agentId), `${fileSegment(sessionId)}.json`);
11753
11783
  const data = JSON.parse(fsSync6.readFileSync(filePath, "utf-8"));
11754
11784
  return data.rawMessages ?? [];
11755
11785
  } catch {
@@ -11829,7 +11859,7 @@ async function initObservability() {
11829
11859
  return true;
11830
11860
  }
11831
11861
  const { registerSdk: registerSdk2 } = await Promise.resolve().then(() => (init_otel_sdk(), otel_sdk_exports));
11832
- providers = registerSdk2(process.env.OTEL_SERVICE_NAME, "1.5.6");
11862
+ providers = registerSdk2(process.env.OTEL_SERVICE_NAME, "1.5.7");
11833
11863
  console.log(`[Observability] OTLP export \u2192 ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT} (service.name=${process.env.OTEL_SERVICE_NAME}, capture_content=${config.observability.captureContent})`);
11834
11864
  return true;
11835
11865
  }
@@ -12115,7 +12145,7 @@ var init_genai_spans = __esm({
12115
12145
 
12116
12146
  // ../server/dist/agents/session-manager.js
12117
12147
  import fsSync7 from "node:fs";
12118
- import path32 from "node:path";
12148
+ import path33 from "node:path";
12119
12149
  import { eq as eq10, and as and7, isNull as isNull4, isNotNull as isNotNull2 } from "drizzle-orm";
12120
12150
  function simpleHash(str) {
12121
12151
  let h = 0;
@@ -12313,7 +12343,7 @@ var init_session_manager = __esm({
12313
12343
  }
12314
12344
  constructor(workspaceRoot, opts) {
12315
12345
  this.workspaceRoot = workspaceRoot;
12316
- const haloDir = path32.join(workspaceRoot, ".halo");
12346
+ const haloDir = path33.join(workspaceRoot, ".halo");
12317
12347
  this.db = createDb(haloDir);
12318
12348
  this.uiStore = new SessionUIStore(this);
12319
12349
  this.queryStore = new SessionQueryStore(this);
@@ -12426,9 +12456,9 @@ var init_session_manager = __esm({
12426
12456
  */
12427
12457
  /** @internal */
12428
12458
  async resolveWorkingDir(input) {
12429
- const resolved = path32.isAbsolute(input) ? path32.resolve(input) : path32.resolve(this.workspaceRoot, input);
12430
- const wsRoot = path32.resolve(this.workspaceRoot);
12431
- if (resolved !== wsRoot && !resolved.startsWith(wsRoot + path32.sep)) {
12459
+ const resolved = path33.isAbsolute(input) ? path33.resolve(input) : path33.resolve(this.workspaceRoot, input);
12460
+ const wsRoot = path33.resolve(this.workspaceRoot);
12461
+ if (resolved !== wsRoot && !resolved.startsWith(wsRoot + path33.sep)) {
12432
12462
  throw new Error(`working_dir "${input}" is outside the workspace`);
12433
12463
  }
12434
12464
  try {
@@ -12548,7 +12578,7 @@ var init_session_manager = __esm({
12548
12578
  if (meta.archivedAt)
12549
12579
  throw new Error(`Session ${sessionId} is archived and cannot be resumed`);
12550
12580
  const restoredAccessLevel = meta.accessLevel === "readonly" ? "readonly" : meta.accessLevel === "workspace" ? "workspace" : null;
12551
- const restoredWorkingDir = meta.workingDir ? path32.resolve(this.workspaceRoot, meta.workingDir) : void 0;
12581
+ const restoredWorkingDir = meta.workingDir ? path33.resolve(this.workspaceRoot, meta.workingDir) : void 0;
12552
12582
  const { agent, yamlConfig: resumedYaml, contextConfig, modelId, systemPrompt, thinkingEffort, meta: agentMeta } = await this.buildAgentInstance(meta.agentId, sessionId, meta.parentId, restoredWorkingDir, restoredAccessLevel);
12553
12583
  const savedMessages = this.loadAgentState(sessionId, meta.agentId);
12554
12584
  if (savedMessages.length > 0) {
@@ -12617,7 +12647,7 @@ var init_session_manager = __esm({
12617
12647
  agentId,
12618
12648
  agentName: resolvedAgentName,
12619
12649
  description,
12620
- workingDir: workingDir ? path32.relative(this.workspaceRoot, workingDir) : null,
12650
+ workingDir: workingDir ? path33.relative(this.workspaceRoot, workingDir) : null,
12621
12651
  accessLevel,
12622
12652
  createdAt: now,
12623
12653
  updatedAt: now
@@ -12626,7 +12656,7 @@ var init_session_manager = __esm({
12626
12656
  try {
12627
12657
  const dir = this.sessionDir(agentId);
12628
12658
  fsSync7.mkdirSync(dir, { recursive: true });
12629
- atomicWriteSessionFile(path32.join(dir, `${fileSegment(sessionId)}.json`), JSON.stringify({ id: sessionId, agentId, title, createdAt: new Date(now).toISOString() }));
12659
+ atomicWriteSessionFile(path33.join(dir, `${fileSegment(sessionId)}.json`), JSON.stringify({ id: sessionId, agentId, title, createdAt: new Date(now).toISOString() }));
12630
12660
  } catch (err) {
12631
12661
  console.warn(`[SessionManager] Failed to pre-seed title for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
12632
12662
  }
@@ -12705,7 +12735,7 @@ var init_session_manager = __esm({
12705
12735
  if (row.exchangeCount !== null)
12706
12736
  return row.title || null;
12707
12737
  try {
12708
- const filePath = path32.join(this.sessionDir(row.agentId), `${fileSegment(sessionId)}.json`);
12738
+ const filePath = path33.join(this.sessionDir(row.agentId), `${fileSegment(sessionId)}.json`);
12709
12739
  const data = JSON.parse(fsSync7.readFileSync(filePath, "utf-8"));
12710
12740
  return data.title || null;
12711
12741
  } catch {
@@ -14195,7 +14225,7 @@ ${result.summary}`, agentName: "user", report: true, taskId });
14195
14225
  live.agent.messages = repaired;
14196
14226
  this.saveAgentState(live);
14197
14227
  } else {
14198
- const filePath = path32.join(this.sessionDir(info.agentId), `${fileSegment(rootId)}.json`);
14228
+ const filePath = path33.join(this.sessionDir(info.agentId), `${fileSegment(rootId)}.json`);
14199
14229
  let data = {};
14200
14230
  try {
14201
14231
  data = JSON.parse(fsSync7.readFileSync(filePath, "utf-8"));
@@ -14351,7 +14381,7 @@ ${result.summary}`, agentName: "user", report: true, taskId });
14351
14381
  lastActivityAt = session.lastActivityAt;
14352
14382
  } else {
14353
14383
  try {
14354
- const filePath = path32.join(this.sessionDir(meta.agentId), `${fileSegment(sessionId)}.json`);
14384
+ const filePath = path33.join(this.sessionDir(meta.agentId), `${fileSegment(sessionId)}.json`);
14355
14385
  const data = JSON.parse(fsSync7.readFileSync(filePath, "utf-8"));
14356
14386
  output = typeof data.output === "string" ? data.output : "";
14357
14387
  lastActivityAt = typeof data.lastActivityAt === "string" ? data.lastActivityAt : null;
@@ -14518,7 +14548,7 @@ var init_session_manager_registry = __esm({
14518
14548
 
14519
14549
  // ../server/dist/logger.js
14520
14550
  import fs27 from "node:fs";
14521
- import path33 from "node:path";
14551
+ import path34 from "node:path";
14522
14552
  import { homedir as homedir13 } from "node:os";
14523
14553
  import { SeverityNumber } from "@opentelemetry/api-logs";
14524
14554
  function ensureDir() {
@@ -14641,8 +14671,8 @@ var init_logger = __esm({
14641
14671
  init_config();
14642
14672
  init_otel();
14643
14673
  LOG_FILENAME = "server.log";
14644
- logDir = path33.join(homedir13(), ".halo", "global", "logs");
14645
- logFile = path33.join(logDir, LOG_FILENAME);
14674
+ logDir = path34.join(homedir13(), ".halo", "global", "logs");
14675
+ logFile = path34.join(logDir, LOG_FILENAME);
14646
14676
  currentSize = -1;
14647
14677
  LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
14648
14678
  OTEL_SEVERITY = {
@@ -14662,17 +14692,18 @@ __export(harness_exports, {
14662
14692
  listAgents: () => listAgents,
14663
14693
  listSessions: () => listSessions
14664
14694
  });
14665
- import path34 from "node:path";
14695
+ import path35 from "node:path";
14666
14696
  import fs28 from "node:fs";
14667
14697
  async function initRuntime() {
14668
14698
  console.log = (...args) => process.stderr.write(args.map(String).join(" ") + "\n");
14699
+ refreshTemplatesIfOutdated(HALO_HOME3, "CLI");
14669
14700
  initLogger();
14670
14701
  await initBwrapCheck();
14671
14702
  const { hiddenDirs, hiddenFiles } = resolveSandboxPaths();
14672
14703
  setSandboxHiddenPaths(hiddenDirs, hiddenFiles);
14673
14704
  }
14674
14705
  async function listAgents(workspace) {
14675
- const absWs = path34.resolve(workspace);
14706
+ const absWs = path35.resolve(workspace);
14676
14707
  ensureWorkspaceHalo(absWs);
14677
14708
  const sm = new SessionManager(absWs);
14678
14709
  const disabledSet = getDisabledSet(sm.getDb(), "agent");
@@ -14680,7 +14711,7 @@ async function listAgents(workspace) {
14680
14711
  return agents.map((a) => ({ id: a.id, description: a.description, scope: a.scope }));
14681
14712
  }
14682
14713
  function listSessions(workspace) {
14683
- const absWs = path34.resolve(workspace);
14714
+ const absWs = path35.resolve(workspace);
14684
14715
  ensureWorkspaceHalo(absWs);
14685
14716
  const sm = new SessionManager(absWs);
14686
14717
  const { sessions } = sm.listSessions({ limit: 200 });
@@ -14692,7 +14723,7 @@ async function agentSupportsImage(sm, workspace, agentId) {
14692
14723
  return entry ? modelSupportsImage(entry.model) : false;
14693
14724
  }
14694
14725
  async function createHarness(opts) {
14695
- const initialPath = path34.resolve(opts.workspace);
14726
+ const initialPath = path35.resolve(opts.workspace);
14696
14727
  if (!fs28.existsSync(initialPath)) {
14697
14728
  throw new Error(`Workspace does not exist: ${initialPath}`);
14698
14729
  }
@@ -14864,7 +14895,7 @@ async function createHarness(opts) {
14864
14895
  return Array.from(merged.values()).sort((a, b) => a.slashName.localeCompare(b.slashName));
14865
14896
  }
14866
14897
  async function switchWorkspace(newPath) {
14867
- const resolved = path34.resolve(newPath);
14898
+ const resolved = path35.resolve(newPath);
14868
14899
  if (resolved === state.workspace) return;
14869
14900
  if (!fs28.existsSync(resolved)) {
14870
14901
  throw new Error(`Workspace does not exist: ${resolved}`);
@@ -14988,7 +15019,7 @@ __export(setup_config_exports, {
14988
15019
  updateConfigLeaves: () => updateConfigLeaves
14989
15020
  });
14990
15021
  import fs29 from "node:fs";
14991
- import path35 from "node:path";
15022
+ import path36 from "node:path";
14992
15023
  import { homedir as homedir14 } from "node:os";
14993
15024
  import YAML8 from "yaml";
14994
15025
  function updateConfigLeaves(updates) {
@@ -14999,7 +15030,7 @@ function updateConfigLeaves(updates) {
14999
15030
  parts.push("value");
15000
15031
  doc.setIn(parts, val);
15001
15032
  }
15002
- fs29.mkdirSync(path35.dirname(CONFIG_PATH), { recursive: true });
15033
+ fs29.mkdirSync(path36.dirname(CONFIG_PATH), { recursive: true });
15003
15034
  fs29.writeFileSync(CONFIG_PATH, doc.toString(), { encoding: "utf-8", mode: 384 });
15004
15035
  try {
15005
15036
  fs29.chmodSync(CONFIG_PATH, 384);
@@ -15023,7 +15054,7 @@ var CONFIG_PATH;
15023
15054
  var init_setup_config = __esm({
15024
15055
  "../server/dist/setup-config.js"() {
15025
15056
  "use strict";
15026
- CONFIG_PATH = path35.join(homedir14(), ".halo", "secrets", "config.yaml");
15057
+ CONFIG_PATH = path36.join(homedir14(), ".halo", "secrets", "config.yaml");
15027
15058
  }
15028
15059
  });
15029
15060
 
@@ -15036,7 +15067,7 @@ __export(setup_settings_exports, {
15036
15067
  writeSettings: () => writeSettings
15037
15068
  });
15038
15069
  import fs30 from "node:fs";
15039
- import path36 from "node:path";
15070
+ import path37 from "node:path";
15040
15071
  import { homedir as homedir15 } from "node:os";
15041
15072
  import YAML9 from "yaml";
15042
15073
  function loadDoc() {
@@ -15048,7 +15079,7 @@ function loadDoc() {
15048
15079
  return YAML9.parseDocument(raw);
15049
15080
  }
15050
15081
  function saveDoc(doc) {
15051
- fs30.mkdirSync(path36.dirname(SETTINGS_PATH2), { recursive: true });
15082
+ fs30.mkdirSync(path37.dirname(SETTINGS_PATH2), { recursive: true });
15052
15083
  fs30.writeFileSync(SETTINGS_PATH2, doc.toString({ lineWidth: 120 }), { encoding: "utf-8", mode: 384 });
15053
15084
  try {
15054
15085
  fs30.chmodSync(SETTINGS_PATH2, 384);
@@ -15108,7 +15139,7 @@ var SETTINGS_PATH2;
15108
15139
  var init_setup_settings = __esm({
15109
15140
  "../server/dist/setup-settings.js"() {
15110
15141
  "use strict";
15111
- SETTINGS_PATH2 = path36.join(homedir15(), ".halo", "secrets", "settings.yaml");
15142
+ SETTINGS_PATH2 = path37.join(homedir15(), ".halo", "secrets", "settings.yaml");
15112
15143
  }
15113
15144
  });
15114
15145
 
@@ -15125,7 +15156,7 @@ __export(setup_providers_exports, {
15125
15156
  readDefaultAgentProvider: () => readDefaultAgentProvider
15126
15157
  });
15127
15158
  import fs31 from "node:fs";
15128
- import path37 from "node:path";
15159
+ import path38 from "node:path";
15129
15160
  import { homedir as homedir16 } from "node:os";
15130
15161
  import YAML10 from "yaml";
15131
15162
  function readYamlFile2(filePath) {
@@ -15155,14 +15186,14 @@ function parseFields(raw) {
15155
15186
  })).filter((f) => f.key.length > 0);
15156
15187
  }
15157
15188
  function listModelProviders() {
15158
- const dir = path37.join(TEMPLATES_DIR, "models");
15189
+ const dir = path38.join(TEMPLATES_DIR, "models");
15159
15190
  if (!fs31.existsSync(dir))
15160
15191
  return [];
15161
15192
  const out = [];
15162
15193
  for (const f of fs31.readdirSync(dir)) {
15163
15194
  if (!f.endsWith(".yaml"))
15164
15195
  continue;
15165
- const data = readYamlFile2(path37.join(dir, f));
15196
+ const data = readYamlFile2(path38.join(dir, f));
15166
15197
  if (!data || typeof data !== "object")
15167
15198
  continue;
15168
15199
  const id = typeof data.id === "string" ? data.id : f.replace(/\.yaml$/, "");
@@ -15177,15 +15208,15 @@ function listModelProviders() {
15177
15208
  return out.sort((a, b) => a.id.localeCompare(b.id));
15178
15209
  }
15179
15210
  function listOptionalSkills() {
15180
- const dir = path37.join(TEMPLATES_DIR, "optional-skills");
15211
+ const dir = path38.join(TEMPLATES_DIR, "optional-skills");
15181
15212
  if (!fs31.existsSync(dir))
15182
15213
  return [];
15183
15214
  const out = [];
15184
15215
  for (const id of fs31.readdirSync(dir)) {
15185
- const skillDir = path37.join(dir, id);
15216
+ const skillDir = path38.join(dir, id);
15186
15217
  if (!fs31.statSync(skillDir).isDirectory())
15187
15218
  continue;
15188
- const skillMd = path37.join(skillDir, "SKILL.md");
15219
+ const skillMd = path38.join(skillDir, "SKILL.md");
15189
15220
  if (!fs31.existsSync(skillMd))
15190
15221
  continue;
15191
15222
  const text6 = fs31.readFileSync(skillMd, "utf-8");
@@ -15204,7 +15235,7 @@ function listOptionalSkills() {
15204
15235
  } catch {
15205
15236
  }
15206
15237
  }
15207
- const cfg = readYamlFile2(path37.join(skillDir, "config.yaml"));
15238
+ const cfg = readYamlFile2(path38.join(skillDir, "config.yaml"));
15208
15239
  out.push({
15209
15240
  id,
15210
15241
  name,
@@ -15226,7 +15257,7 @@ function listConfiguredSwitchableProviders() {
15226
15257
  });
15227
15258
  }
15228
15259
  function readDefaultAgentProvider() {
15229
- const data = readYamlFile2(path37.join(GLOBAL_AGENTS_DIR3, "default", "agent.yaml"));
15260
+ const data = readYamlFile2(path38.join(GLOBAL_AGENTS_DIR3, "default", "agent.yaml"));
15230
15261
  const provider = data?.model && typeof data.model === "object" ? data.model.provider : void 0;
15231
15262
  return typeof provider === "string" && provider.length > 0 ? provider : void 0;
15232
15263
  }
@@ -15256,7 +15287,7 @@ function modelBlockFromProvider(providerId, data) {
15256
15287
  return block;
15257
15288
  }
15258
15289
  function buildTemplateModelBlock(providerId) {
15259
- const data = readYamlFile2(path37.join(TEMPLATES_DIR, "models", `${providerId}.yaml`));
15290
+ const data = readYamlFile2(path38.join(TEMPLATES_DIR, "models", `${providerId}.yaml`));
15260
15291
  if (!data || typeof data !== "object")
15261
15292
  return null;
15262
15293
  return modelBlockFromProvider(providerId, data);
@@ -15267,7 +15298,7 @@ function bindBuiltinAgentsToProvider(providerId) {
15267
15298
  return null;
15268
15299
  const agents = [];
15269
15300
  for (const id of REBIND_BUILTIN_AGENT_IDS) {
15270
- const yamlPath = path37.join(GLOBAL_AGENTS_DIR3, id, "agent.yaml");
15301
+ const yamlPath = path38.join(GLOBAL_AGENTS_DIR3, id, "agent.yaml");
15271
15302
  if (!fs31.existsSync(yamlPath))
15272
15303
  continue;
15273
15304
  try {
@@ -15281,22 +15312,22 @@ function bindBuiltinAgentsToProvider(providerId) {
15281
15312
  return { modelId: typeof block.id === "string" ? block.id : void 0, agents };
15282
15313
  }
15283
15314
  function listRequiredSkillsWithSecrets() {
15284
- const dir = path37.join(TEMPLATES_DIR, "skills");
15315
+ const dir = path38.join(TEMPLATES_DIR, "skills");
15285
15316
  if (!fs31.existsSync(dir))
15286
15317
  return [];
15287
15318
  const out = [];
15288
15319
  for (const id of fs31.readdirSync(dir)) {
15289
- const skillDir = path37.join(dir, id);
15320
+ const skillDir = path38.join(dir, id);
15290
15321
  if (!fs31.statSync(skillDir).isDirectory())
15291
15322
  continue;
15292
- const cfgPath = path37.join(skillDir, "config.yaml");
15323
+ const cfgPath = path38.join(skillDir, "config.yaml");
15293
15324
  if (!fs31.existsSync(cfgPath))
15294
15325
  continue;
15295
15326
  const cfg = readYamlFile2(cfgPath);
15296
15327
  const fields = parseFields(cfg?.params).filter((f) => f.secret);
15297
15328
  if (fields.length === 0)
15298
15329
  continue;
15299
- const skillMd = path37.join(skillDir, "SKILL.md");
15330
+ const skillMd = path38.join(skillDir, "SKILL.md");
15300
15331
  let name = id;
15301
15332
  let description = "";
15302
15333
  if (fs31.existsSync(skillMd)) {
@@ -15327,7 +15358,7 @@ var init_setup_providers = __esm({
15327
15358
  init_setup_settings();
15328
15359
  AWS_CREDENTIAL_CHAIN_PROVIDER = "aws-bedrock-claude-invoke";
15329
15360
  REBIND_BUILTIN_AGENT_IDS = ["default", "executor", "deep-executor"];
15330
- GLOBAL_AGENTS_DIR3 = path37.join(homedir16(), ".halo", "global", "agents");
15361
+ GLOBAL_AGENTS_DIR3 = path38.join(homedir16(), ".halo", "global", "agents");
15331
15362
  }
15332
15363
  });
15333
15364
 
@@ -15645,13 +15676,13 @@ __export(upgrade_verify_exports, {
15645
15676
  NATIVE_MODULE_SMOKES: () => NATIVE_MODULE_SMOKES,
15646
15677
  verifyNativeModules: () => verifyNativeModules
15647
15678
  });
15648
- import path38 from "node:path";
15679
+ import path39 from "node:path";
15649
15680
  import fs32 from "node:fs";
15650
15681
  import { spawnSync } from "node:child_process";
15651
15682
  function verifyNativeModules(pkgRoot) {
15652
15683
  const broken = [];
15653
15684
  for (const { name, smoke } of NATIVE_MODULE_SMOKES) {
15654
- const modDir = path38.join(pkgRoot, "node_modules", name);
15685
+ const modDir = path39.join(pkgRoot, "node_modules", name);
15655
15686
  if (!fs32.existsSync(modDir)) {
15656
15687
  broken.push(name);
15657
15688
  continue;
@@ -15690,7 +15721,7 @@ var init_render_md = __esm({
15690
15721
 
15691
15722
  // src/resolve-refs.ts
15692
15723
  import fs33 from "node:fs";
15693
- import path39 from "node:path";
15724
+ import path40 from "node:path";
15694
15725
  function resolveRefs(input, workspace) {
15695
15726
  const images = [];
15696
15727
  const attachments = [];
@@ -15703,8 +15734,8 @@ function resolveRefs(input, workspace) {
15703
15734
  lastIdx = match.index + match[0].length;
15704
15735
  const kind = match[1];
15705
15736
  const rawPath = match[2] ?? match[3];
15706
- const resolved = path39.isAbsolute(rawPath) ? rawPath : path39.resolve(workspace, rawPath);
15707
- const ext = path39.extname(resolved).toLowerCase();
15737
+ const resolved = path40.isAbsolute(rawPath) ? rawPath : path40.resolve(workspace, rawPath);
15738
+ const ext = path40.extname(resolved).toLowerCase();
15708
15739
  let stat = null;
15709
15740
  try {
15710
15741
  stat = fs33.statSync(resolved);
@@ -15728,7 +15759,7 @@ function resolveRefs(input, workspace) {
15728
15759
  const data = fs33.readFileSync(resolved).toString("base64");
15729
15760
  images.push({ data, mimeType: imageMimeFromExt(ext) ?? "application/octet-stream" });
15730
15761
  } else {
15731
- const relPath = path39.relative(workspace, resolved);
15762
+ const relPath = path40.relative(workspace, resolved);
15732
15763
  if (stat.size > MAX_FILE_BYTES) {
15733
15764
  const buf = Buffer.alloc(MAX_FILE_BYTES);
15734
15765
  const fd = fs33.openSync(resolved, "r");
@@ -16020,7 +16051,7 @@ var init_streaming = __esm({
16020
16051
 
16021
16052
  // src/tui/history.ts
16022
16053
  import fs34 from "node:fs";
16023
- import path40 from "node:path";
16054
+ import path41 from "node:path";
16024
16055
  import os2 from "node:os";
16025
16056
  function loadHistory() {
16026
16057
  try {
@@ -16037,7 +16068,7 @@ function appendHistory(entry) {
16037
16068
  const arr = loadHistory();
16038
16069
  if (arr[arr.length - 1] === entry) return;
16039
16070
  arr.push(entry);
16040
- fs34.mkdirSync(path40.dirname(HISTORY_FILE), { recursive: true });
16071
+ fs34.mkdirSync(path41.dirname(HISTORY_FILE), { recursive: true });
16041
16072
  fs34.writeFileSync(HISTORY_FILE, JSON.stringify(arr.slice(-MAX_ENTRIES)));
16042
16073
  } catch {
16043
16074
  }
@@ -16046,7 +16077,7 @@ var HISTORY_FILE, MAX_ENTRIES;
16046
16077
  var init_history = __esm({
16047
16078
  "src/tui/history.ts"() {
16048
16079
  "use strict";
16049
- HISTORY_FILE = path40.join(os2.homedir(), ".halo", "global", "tui-history.json");
16080
+ HISTORY_FILE = path41.join(os2.homedir(), ".halo", "global", "tui-history.json");
16050
16081
  MAX_ENTRIES = 100;
16051
16082
  }
16052
16083
  });
@@ -16151,7 +16182,7 @@ var init_path_suggest = __esm({
16151
16182
 
16152
16183
  // src/tui/path-suggest-engine.ts
16153
16184
  import fs35 from "node:fs";
16154
- import path41 from "node:path";
16185
+ import path42 from "node:path";
16155
16186
  function detectActiveRef(value) {
16156
16187
  const reExplicit = /@(file|image|scope)\s+(?:"([^"]*)|(\S*))$/;
16157
16188
  const mExplicit = value.match(reExplicit);
@@ -16180,13 +16211,13 @@ function resolveDirAndPrefix(partial, workspace) {
16180
16211
  if (partial === "") {
16181
16212
  return { absDir: workspace, displayDir: ".", prefix: "" };
16182
16213
  }
16183
- const absBase = path41.isAbsolute(partial) ? partial : path41.join(workspace, partial);
16214
+ const absBase = path42.isAbsolute(partial) ? partial : path42.join(workspace, partial);
16184
16215
  if (partial.endsWith("/")) {
16185
16216
  return { absDir: absBase, displayDir: partial.replace(/\/$/, "") || "/", prefix: "" };
16186
16217
  }
16187
- const dirAbs = path41.dirname(absBase);
16188
- const prefix = path41.basename(absBase);
16189
- const dirRel = path41.dirname(partial);
16218
+ const dirAbs = path42.dirname(absBase);
16219
+ const prefix = path42.basename(absBase);
16220
+ const dirRel = path42.dirname(partial);
16190
16221
  return { absDir: dirAbs, displayDir: dirRel === "." ? "." : dirRel, prefix };
16191
16222
  }
16192
16223
  function scanCandidates(partial, workspace, kind) {
@@ -16202,17 +16233,17 @@ function scanCandidates(partial, workspace, kind) {
16202
16233
  if (e.name.startsWith(".")) continue;
16203
16234
  if (prefix && !e.name.toLowerCase().startsWith(prefix.toLowerCase())) continue;
16204
16235
  const isDir = e.isDirectory();
16205
- const ext = path41.extname(e.name).toLowerCase();
16236
+ const ext = path42.extname(e.name).toLowerCase();
16206
16237
  const isImage = IMAGE_EXTS.has(ext);
16207
16238
  if (kind === "image" && !isDir && !isImage) continue;
16208
16239
  if (kind === "scope" && !isDir) continue;
16209
16240
  let insertPath;
16210
- if (path41.isAbsolute(partial)) {
16211
- insertPath = path41.join(absDir, e.name);
16241
+ if (path42.isAbsolute(partial)) {
16242
+ insertPath = path42.join(absDir, e.name);
16212
16243
  } else if (partial.endsWith("/") || partial === "") {
16213
16244
  insertPath = partial + e.name;
16214
16245
  } else {
16215
- const dirRel = path41.dirname(partial);
16246
+ const dirRel = path42.dirname(partial);
16216
16247
  insertPath = (dirRel === "." ? "" : dirRel + "/") + e.name;
16217
16248
  }
16218
16249
  items.push({ name: e.name, insertPath, isDir, isImage });
@@ -16231,8 +16262,8 @@ function applyPathPick(value, ref, pick) {
16231
16262
  const before = value.slice(0, markerStart);
16232
16263
  const kind = ref.kind === "scope" ? "scope" : ref.kind === "image" ? "image" : pick.isImage ? "image" : "file";
16233
16264
  const needsQuote = pick.insertPath.includes(" ");
16234
- const path73 = pick.isDir ? needsQuote ? `"${pick.insertPath}/` : `${pick.insertPath}/` : needsQuote ? `"${pick.insertPath}" ` : `${pick.insertPath} `;
16235
- return `${before}@${kind} ${path73}`;
16265
+ const path74 = pick.isDir ? needsQuote ? `"${pick.insertPath}/` : `${pick.insertPath}/` : needsQuote ? `"${pick.insertPath}" ` : `${pick.insertPath} `;
16266
+ return `${before}@${kind} ${path74}`;
16236
16267
  }
16237
16268
  var init_path_suggest_engine = __esm({
16238
16269
  "src/tui/path-suggest-engine.ts"() {
@@ -17713,15 +17744,15 @@ var init_cli = __esm({
17713
17744
  });
17714
17745
 
17715
17746
  // ../server/dist/channels/shared/media.js
17716
- import path42 from "node:path";
17747
+ import path43 from "node:path";
17717
17748
  import os3 from "node:os";
17718
17749
  function tempDir() {
17719
- return path42.resolve(os3.tmpdir());
17750
+ return path43.resolve(os3.tmpdir());
17720
17751
  }
17721
17752
  function isInTempDir(filePath) {
17722
- const resolved = path42.resolve(filePath);
17753
+ const resolved = path43.resolve(filePath);
17723
17754
  const tmp = tempDir();
17724
- return resolved === tmp || resolved.startsWith(tmp + path42.sep);
17755
+ return resolved === tmp || resolved.startsWith(tmp + path43.sep);
17725
17756
  }
17726
17757
  function extractMediaPaths(text6) {
17727
17758
  const mediaPaths = [];
@@ -17739,9 +17770,9 @@ function extractMediaMessage(text6) {
17739
17770
  function isMediaPathAllowed(filePath, workspacePath, accessLevel) {
17740
17771
  if (accessLevel === "full")
17741
17772
  return true;
17742
- const resolved = path42.resolve(filePath);
17743
- const ws = path42.resolve(workspacePath);
17744
- return resolved === ws || resolved.startsWith(ws + path42.sep) || isInTempDir(resolved);
17773
+ const resolved = path43.resolve(filePath);
17774
+ const ws = path43.resolve(workspacePath);
17775
+ return resolved === ws || resolved.startsWith(ws + path43.sep) || isInTempDir(resolved);
17745
17776
  }
17746
17777
  function assertMediaPathAllowed(filePath, account) {
17747
17778
  if (!isMediaPathAllowed(filePath, account.workspacePath, account.accessLevel)) {
@@ -17756,12 +17787,12 @@ async function sendMediaOrReport(opts) {
17756
17787
  } catch (err) {
17757
17788
  const error = err instanceof Error ? err.message : String(err);
17758
17789
  console.log(`[${opts.logTag}] sendMedia ${filePath} failed: ${error}`);
17759
- await opts.reply(t("handler.upload_failed", getLang(account), { name: path42.basename(filePath), error })).catch(() => {
17790
+ await opts.reply(t("handler.upload_failed", getLang(account), { name: path43.basename(filePath), error })).catch(() => {
17760
17791
  });
17761
17792
  }
17762
17793
  }
17763
17794
  function classifyMedia(filePath) {
17764
- const ext = path42.extname(filePath).toLowerCase();
17795
+ const ext = path43.extname(filePath).toLowerCase();
17765
17796
  if (IMAGE_EXTS.has(ext))
17766
17797
  return "image";
17767
17798
  if (VIDEO_EXTS.has(ext))
@@ -17785,9 +17816,9 @@ var init_media = __esm({
17785
17816
  // ../server/dist/routes/workspace-path.js
17786
17817
  import fs36 from "node:fs/promises";
17787
17818
  import { existsSync as existsSync2, realpathSync as realpathSync3 } from "node:fs";
17788
- import path43 from "node:path";
17819
+ import path44 from "node:path";
17789
17820
  async function resolveProjectPath(projectId) {
17790
- if (path43.isAbsolute(projectId)) {
17821
+ if (path44.isAbsolute(projectId)) {
17791
17822
  try {
17792
17823
  await fs36.access(projectId);
17793
17824
  return projectId;
@@ -17798,13 +17829,13 @@ async function resolveProjectPath(projectId) {
17798
17829
  return null;
17799
17830
  }
17800
17831
  function realpathBounded2(filePath) {
17801
- let prefix = path43.resolve(filePath);
17832
+ let prefix = path44.resolve(filePath);
17802
17833
  const tail = [];
17803
17834
  while (!existsSync2(prefix)) {
17804
- const parent = path43.dirname(prefix);
17835
+ const parent = path44.dirname(prefix);
17805
17836
  if (parent === prefix)
17806
17837
  break;
17807
- tail.unshift(path43.basename(prefix));
17838
+ tail.unshift(path44.basename(prefix));
17808
17839
  prefix = parent;
17809
17840
  }
17810
17841
  let realPrefix;
@@ -17813,12 +17844,12 @@ function realpathBounded2(filePath) {
17813
17844
  } catch {
17814
17845
  realPrefix = prefix;
17815
17846
  }
17816
- return tail.length > 0 ? path43.join(realPrefix, ...tail) : realPrefix;
17847
+ return tail.length > 0 ? path44.join(realPrefix, ...tail) : realPrefix;
17817
17848
  }
17818
17849
  function validatePath(filePath, projectPath) {
17819
- const resolved = realpathBounded2(path43.resolve(projectPath, filePath));
17850
+ const resolved = realpathBounded2(path44.resolve(projectPath, filePath));
17820
17851
  const proj = realpathBounded2(projectPath);
17821
- return resolved === proj || resolved.startsWith(proj + path43.sep);
17852
+ return resolved === proj || resolved.startsWith(proj + path44.sep);
17822
17853
  }
17823
17854
  function isSafeIdSegment(id) {
17824
17855
  if (id === "." || id === "..")
@@ -17836,7 +17867,7 @@ import { Hono } from "hono";
17836
17867
  import fs37 from "node:fs/promises";
17837
17868
  import { createReadStream } from "node:fs";
17838
17869
  import { Readable } from "node:stream";
17839
- import path44 from "node:path";
17870
+ import path45 from "node:path";
17840
17871
  import { homedir as homedir17 } from "node:os";
17841
17872
  async function resolveProjectFile(projectId, filePath) {
17842
17873
  const projectPath = await resolveProjectPath(projectId);
@@ -17844,7 +17875,7 @@ async function resolveProjectFile(projectId, filePath) {
17844
17875
  return { error: "Project not found", status: 404 };
17845
17876
  if (!validatePath(filePath, projectPath))
17846
17877
  return { error: "Path traversal not allowed", status: 403 };
17847
- return { projectPath, absolutePath: path44.resolve(projectPath, filePath) };
17878
+ return { projectPath, absolutePath: path45.resolve(projectPath, filePath) };
17848
17879
  }
17849
17880
  function createFileRoutes() {
17850
17881
  const app2 = new Hono();
@@ -17889,8 +17920,8 @@ function createFileRoutes() {
17889
17920
  for (const entry of sorted) {
17890
17921
  if (isSkippedName(entry.name))
17891
17922
  continue;
17892
- const fullPath = path44.join(dirPath, entry.name);
17893
- const relativePath = path44.relative(basePath, fullPath).split(path44.sep).join("/");
17923
+ const fullPath = path45.join(dirPath, entry.name);
17924
+ const relativePath = path45.relative(basePath, fullPath).split(path45.sep).join("/");
17894
17925
  if (entry.isDirectory()) {
17895
17926
  nodes.push({
17896
17927
  name: entry.name,
@@ -17922,7 +17953,7 @@ function createFileRoutes() {
17922
17953
  if (relPath && !validatePath(relPath, projectPath)) {
17923
17954
  return c.json({ error: "Path traversal not allowed" }, 403);
17924
17955
  }
17925
- const targetPath = relPath ? path44.resolve(projectPath, relPath) : projectPath;
17956
+ const targetPath = relPath ? path45.resolve(projectPath, relPath) : projectPath;
17926
17957
  try {
17927
17958
  const stat = await fs37.stat(targetPath);
17928
17959
  if (!stat.isDirectory()) {
@@ -17934,7 +17965,7 @@ function createFileRoutes() {
17934
17965
  const tree = await listDir(targetPath, projectPath);
17935
17966
  return c.json({
17936
17967
  projectId,
17937
- root: path44.basename(projectPath),
17968
+ root: path45.basename(projectPath),
17938
17969
  path: relPath,
17939
17970
  tree
17940
17971
  });
@@ -17977,11 +18008,11 @@ function createFileRoutes() {
17977
18008
  if (isSkippedName(entry.name))
17978
18009
  continue;
17979
18010
  scanned++;
17980
- const fullPath = path44.join(dirPath, entry.name);
18011
+ const fullPath = path45.join(dirPath, entry.name);
17981
18012
  if (entry.isDirectory()) {
17982
18013
  if (dirsOnly) {
17983
18014
  const name = entry.name.toLowerCase();
17984
- const rel = path44.relative(projectRoot, fullPath);
18015
+ const rel = path45.relative(projectRoot, fullPath);
17985
18016
  if (!q || name.includes(q) || rel.toLowerCase().includes(q)) {
17986
18017
  matches.push({ name: entry.name, path: rel });
17987
18018
  }
@@ -17989,7 +18020,7 @@ function createFileRoutes() {
17989
18020
  await walk(fullPath);
17990
18021
  } else if (entry.isFile() && !dirsOnly) {
17991
18022
  const name = entry.name.toLowerCase();
17992
- const rel = path44.relative(projectRoot, fullPath);
18023
+ const rel = path45.relative(projectRoot, fullPath);
17993
18024
  if (!q || name.includes(q) || rel.toLowerCase().includes(q)) {
17994
18025
  matches.push({ name: entry.name, path: rel });
17995
18026
  }
@@ -18033,12 +18064,12 @@ function createFileRoutes() {
18033
18064
  }
18034
18065
  const uploaded = [];
18035
18066
  for (const file of files) {
18036
- const relPath = targetDir ? path44.join(targetDir, file.name) : file.name;
18067
+ const relPath = targetDir ? path45.join(targetDir, file.name) : file.name;
18037
18068
  if (!validatePath(relPath, projectPath)) {
18038
18069
  continue;
18039
18070
  }
18040
- const destPath = path44.resolve(projectPath, relPath);
18041
- const destDir = path44.dirname(destPath);
18071
+ const destPath = path45.resolve(projectPath, relPath);
18072
+ const destDir = path45.dirname(destPath);
18042
18073
  await fs37.mkdir(destDir, { recursive: true });
18043
18074
  const buffer = Buffer.from(await file.arrayBuffer());
18044
18075
  await fs37.writeFile(destPath, buffer);
@@ -18124,7 +18155,7 @@ function createFileRoutes() {
18124
18155
  if ("error" in resolved)
18125
18156
  return c.json({ error: resolved.error }, resolved.status);
18126
18157
  const { absolutePath } = resolved;
18127
- await fs37.mkdir(path44.dirname(absolutePath), { recursive: true });
18158
+ await fs37.mkdir(path45.dirname(absolutePath), { recursive: true });
18128
18159
  try {
18129
18160
  await fs37.access(absolutePath);
18130
18161
  return c.json({ error: "File already exists" }, 409);
@@ -18170,9 +18201,9 @@ function createFileRoutes() {
18170
18201
  if (!validatePath(oldPath, projectPath) || !validatePath(newPath, projectPath)) {
18171
18202
  return c.json({ error: "Path traversal not allowed" }, 403);
18172
18203
  }
18173
- const absOld = path44.resolve(projectPath, oldPath);
18174
- const absNew = path44.resolve(projectPath, newPath);
18175
- await fs37.mkdir(path44.dirname(absNew), { recursive: true });
18204
+ const absOld = path45.resolve(projectPath, oldPath);
18205
+ const absNew = path45.resolve(projectPath, newPath);
18206
+ await fs37.mkdir(path45.dirname(absNew), { recursive: true });
18176
18207
  await fs37.rename(absOld, absNew);
18177
18208
  return c.json({ ok: true, oldPath, newPath });
18178
18209
  } catch (err) {
@@ -18192,7 +18223,7 @@ function createFileRoutes() {
18192
18223
  if ("error" in resolved)
18193
18224
  return c.json({ error: resolved.error }, resolved.status);
18194
18225
  const { projectPath, absolutePath } = resolved;
18195
- if (absolutePath === path44.resolve(projectPath)) {
18226
+ if (absolutePath === path45.resolve(projectPath)) {
18196
18227
  return c.json({ error: "Cannot delete project root" }, 403);
18197
18228
  }
18198
18229
  await fs37.rm(absolutePath, { recursive: true, force: true });
@@ -18230,16 +18261,16 @@ function createFileRoutes() {
18230
18261
  if (!projectPath) {
18231
18262
  return c.json({ error: "Project not found" }, 404);
18232
18263
  }
18233
- const isTmp = path44.isAbsolute(filePath) && isInTempDir(filePath);
18264
+ const isTmp = path45.isAbsolute(filePath) && isInTempDir(filePath);
18234
18265
  if (!isTmp && !validatePath(filePath, projectPath)) {
18235
18266
  return c.json({ error: "Path traversal not allowed" }, 403);
18236
18267
  }
18237
- const absolutePath = isTmp ? path44.resolve(filePath) : path44.resolve(projectPath, filePath);
18268
+ const absolutePath = isTmp ? path45.resolve(filePath) : path45.resolve(projectPath, filePath);
18238
18269
  const stat = await fs37.stat(absolutePath);
18239
18270
  if (stat.isDirectory()) {
18240
18271
  return c.json({ error: "Cannot download a directory" }, 400);
18241
18272
  }
18242
- const fileName = path44.basename(filePath);
18273
+ const fileName = path45.basename(filePath);
18243
18274
  const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
18244
18275
  const mime = inline ? imageMimeFromExt(ext) ?? MIME_MAP[ext] ?? "application/octet-stream" : "application/octet-stream";
18245
18276
  const disposition = inline ? `inline; filename="${encodeURIComponent(fileName)}"` : `attachment; filename="${encodeURIComponent(fileName)}"`;
@@ -18351,7 +18382,7 @@ function createFileRoutes() {
18351
18382
  const body = await c.req.json();
18352
18383
  if (!body.path)
18353
18384
  return c.json({ error: "path required" }, 400);
18354
- const resolved = path44.resolve(body.path);
18385
+ const resolved = path45.resolve(body.path);
18355
18386
  let stat;
18356
18387
  try {
18357
18388
  stat = await fs37.stat(resolved);
@@ -18360,7 +18391,7 @@ function createFileRoutes() {
18360
18391
  }
18361
18392
  if (!stat.isDirectory())
18362
18393
  return c.json({ error: "not a directory" }, 400);
18363
- if (resolved === path44.parse(resolved).root) {
18394
+ if (resolved === path45.parse(resolved).root) {
18364
18395
  return c.json({ error: "filesystem root cannot be a workspace" }, 400);
18365
18396
  }
18366
18397
  const { ensureWorkspaceHalo: ensureWorkspaceHalo2 } = await Promise.resolve().then(() => (init_init(), init_exports));
@@ -18369,7 +18400,7 @@ function createFileRoutes() {
18369
18400
  });
18370
18401
  app2.get("/fs/exists", async (c) => {
18371
18402
  const target = c.req.query("path");
18372
- if (!target || !path44.isAbsolute(target))
18403
+ if (!target || !path45.isAbsolute(target))
18373
18404
  return c.json({ exists: false, reason: "absolute path required" });
18374
18405
  try {
18375
18406
  const stat = await fs37.stat(target);
@@ -18380,13 +18411,13 @@ function createFileRoutes() {
18380
18411
  });
18381
18412
  app2.get("/fs/browse", async (c) => {
18382
18413
  const target = c.req.query("path") ?? homedir17();
18383
- if (!path44.isAbsolute(target))
18414
+ if (!path45.isAbsolute(target))
18384
18415
  return c.json({ error: "absolute path required" }, 400);
18385
18416
  try {
18386
- const resolved = path44.resolve(target);
18417
+ const resolved = path45.resolve(target);
18387
18418
  const entries = await fs37.readdir(resolved, { withFileTypes: true });
18388
- const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: path44.join(resolved, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
18389
- return c.json({ path: resolved, parent: path44.dirname(resolved), entries: dirs });
18419
+ const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: path45.join(resolved, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
18420
+ return c.json({ path: resolved, parent: path45.dirname(resolved), entries: dirs });
18390
18421
  } catch (err) {
18391
18422
  return c.json({ error: err.message }, 404);
18392
18423
  }
@@ -18406,7 +18437,7 @@ var init_files = __esm({
18406
18437
  import { Hono as Hono2 } from "hono";
18407
18438
  import fs38 from "node:fs/promises";
18408
18439
  import { createReadStream as createReadStream2 } from "node:fs";
18409
- import path45 from "node:path";
18440
+ import path46 from "node:path";
18410
18441
  import Database5 from "better-sqlite3";
18411
18442
  import { asyncBufferFromFile, parquetMetadataAsync, parquetSchema, parquetReadObjects } from "hyparquet";
18412
18443
  import { compressors } from "hyparquet-compressors";
@@ -18521,7 +18552,7 @@ function createDataPreviewRoutes() {
18521
18552
  return { error: "Project not found", status: 404 };
18522
18553
  if (!validatePath(filePath, projectPath))
18523
18554
  return { error: "Path traversal not allowed", status: 403 };
18524
- const absolutePath = path45.resolve(projectPath, filePath);
18555
+ const absolutePath = path46.resolve(projectPath, filePath);
18525
18556
  try {
18526
18557
  const stat = await fs38.stat(absolutePath);
18527
18558
  if (stat.isDirectory())
@@ -18730,7 +18761,7 @@ var init_data_preview = __esm({
18730
18761
 
18731
18762
  // ../server/dist/git-credentials.js
18732
18763
  import fs39 from "node:fs";
18733
- import path46 from "node:path";
18764
+ import path47 from "node:path";
18734
18765
  import { homedir as homedir18 } from "node:os";
18735
18766
  function writeGitCredentialsFile(host, username, token) {
18736
18767
  const line = `https://${encodeURIComponent(username)}:${encodeURIComponent(token)}@${host}`;
@@ -18796,14 +18827,14 @@ var GIT_CREDENTIALS_PATH;
18796
18827
  var init_git_credentials = __esm({
18797
18828
  "../server/dist/git-credentials.js"() {
18798
18829
  "use strict";
18799
- GIT_CREDENTIALS_PATH = path46.join(homedir18(), ".git-credentials");
18830
+ GIT_CREDENTIALS_PATH = path47.join(homedir18(), ".git-credentials");
18800
18831
  }
18801
18832
  });
18802
18833
 
18803
18834
  // ../server/dist/git-ssh.js
18804
18835
  import fs40 from "node:fs";
18805
18836
  import os4 from "node:os";
18806
- import path47 from "node:path";
18837
+ import path48 from "node:path";
18807
18838
  import { spawnSync as spawnSync2 } from "node:child_process";
18808
18839
  function looksLikePrivateKey(name, abs) {
18809
18840
  if (name.endsWith(".pub"))
@@ -18838,7 +18869,7 @@ function listSshKeys() {
18838
18869
  }
18839
18870
  const keys = [];
18840
18871
  for (const name of entries) {
18841
- const abs = path47.join(SSH_DIR, name);
18872
+ const abs = path48.join(SSH_DIR, name);
18842
18873
  try {
18843
18874
  if (!fs40.statSync(abs).isFile())
18844
18875
  continue;
@@ -18865,8 +18896,8 @@ function unlockSshKey(keyPath, passphrase) {
18865
18896
  return { ok: false, error: "ssh-agent is not running" };
18866
18897
  let dir = "";
18867
18898
  try {
18868
- dir = fs40.mkdtempSync(path47.join(os4.tmpdir(), "halo-askpass-"));
18869
- const askpass = path47.join(dir, "askpass.sh");
18899
+ dir = fs40.mkdtempSync(path48.join(os4.tmpdir(), "halo-askpass-"));
18900
+ const askpass = path48.join(dir, "askpass.sh");
18870
18901
  fs40.writeFileSync(askpass, ASKPASS_SCRIPT, { mode: 448 });
18871
18902
  const r = spawnSync2("ssh-add", [keyPath], {
18872
18903
  encoding: "utf-8",
@@ -18883,10 +18914,10 @@ function unlockSshKey(keyPath, passphrase) {
18883
18914
  }
18884
18915
  });
18885
18916
  if (r.status === 0) {
18886
- console.log(`[GitSsh] unlocked key ${path47.basename(keyPath)}`);
18917
+ console.log(`[GitSsh] unlocked key ${path48.basename(keyPath)}`);
18887
18918
  return { ok: true };
18888
18919
  }
18889
- console.log(`[GitSsh] unlock failed for ${path47.basename(keyPath)} (exit ${r.status ?? "killed"})`);
18920
+ console.log(`[GitSsh] unlock failed for ${path48.basename(keyPath)} (exit ${r.status ?? "killed"})`);
18890
18921
  if (r.status === 2)
18891
18922
  return { ok: false, error: "ssh-agent is not reachable" };
18892
18923
  return { ok: false, error: "Incorrect passphrase, or the key could not be unlocked" };
@@ -18979,7 +19010,7 @@ var SSH_DIR, NON_KEY_NAMES, ownedAgentPid, ASKPASS_SCRIPT;
18979
19010
  var init_git_ssh = __esm({
18980
19011
  "../server/dist/git-ssh.js"() {
18981
19012
  "use strict";
18982
- SSH_DIR = path47.join(os4.homedir(), ".ssh");
19013
+ SSH_DIR = path48.join(os4.homedir(), ".ssh");
18983
19014
  NON_KEY_NAMES = /* @__PURE__ */ new Set(["known_hosts", "known_hosts2", "authorized_keys", "config"]);
18984
19015
  ownedAgentPid = null;
18985
19016
  ASKPASS_SCRIPT = `#!/bin/sh
@@ -18993,7 +19024,7 @@ printf '%s\\n' "$HALO_SSH_PASSPHRASE"
18993
19024
  // ../server/dist/routes/git.js
18994
19025
  import { Hono as Hono3 } from "hono";
18995
19026
  import os5 from "node:os";
18996
- import path48 from "node:path";
19027
+ import path49 from "node:path";
18997
19028
  function notifyGitChanged(workspacePath) {
18998
19029
  broadcastToWorkspace(workspacePath, { type: "file:changed", path: ".git", action: "change" });
18999
19030
  }
@@ -19296,9 +19327,9 @@ function createGitRoutes() {
19296
19327
  if (!keyPath || typeof passphrase !== "string") {
19297
19328
  return c.json({ error: "keyPath and passphrase are required" }, 400);
19298
19329
  }
19299
- const sshDir = path48.join(os5.homedir(), ".ssh");
19300
- const resolved = path48.resolve(keyPath);
19301
- if (path48.dirname(resolved) !== path48.resolve(sshDir)) {
19330
+ const sshDir = path49.join(os5.homedir(), ".ssh");
19331
+ const resolved = path49.resolve(keyPath);
19332
+ if (path49.dirname(resolved) !== path49.resolve(sshDir)) {
19302
19333
  return c.json({ error: "keyPath must be inside ~/.ssh" }, 400);
19303
19334
  }
19304
19335
  return c.json(unlockSshKey(resolved, passphrase));
@@ -19375,7 +19406,7 @@ var init_mtime_cache = __esm({
19375
19406
  // ../server/dist/routes/agent-configs.js
19376
19407
  import { Hono as Hono4 } from "hono";
19377
19408
  import fs41 from "node:fs/promises";
19378
- import path49 from "node:path";
19409
+ import path50 from "node:path";
19379
19410
  import { homedir as homedir19 } from "node:os";
19380
19411
  import YAML11 from "yaml";
19381
19412
  function getAvailableTools() {
@@ -19434,9 +19465,9 @@ function registryModelBlock() {
19434
19465
  return modelBlockFromProvider(provider.id, provider);
19435
19466
  }
19436
19467
  async function scaffoldModelBlock(scope, projectId) {
19437
- const candidates = [path49.join(GLOBAL_AGENTS_DIR4, "default", "agent.yaml")];
19468
+ const candidates = [path50.join(GLOBAL_AGENTS_DIR4, "default", "agent.yaml")];
19438
19469
  if (scope === "workspace" && projectId)
19439
- candidates.unshift(path49.join(projectId, ".halo", "agents", "default", "agent.yaml"));
19470
+ candidates.unshift(path50.join(projectId, ".halo", "agents", "default", "agent.yaml"));
19440
19471
  for (const yamlPath of candidates) {
19441
19472
  let content;
19442
19473
  try {
@@ -19490,10 +19521,10 @@ async function ensureDir2(dir) {
19490
19521
  }
19491
19522
  function resolveAgentDir(id, scope, projectId) {
19492
19523
  if (scope === "global")
19493
- return { agentDir: path49.join(GLOBAL_AGENTS_DIR4, id) };
19524
+ return { agentDir: path50.join(GLOBAL_AGENTS_DIR4, id) };
19494
19525
  if (scope !== "workspace")
19495
19526
  return { error: "Invalid scope" };
19496
- return projectId ? { agentDir: path49.join(projectId, ".halo", "agents", id) } : { error: "projectId required for workspace agents" };
19527
+ return projectId ? { agentDir: path50.join(projectId, ".halo", "agents", id) } : { error: "projectId required for workspace agents" };
19497
19528
  }
19498
19529
  async function scanAgentsDir(dir, scope) {
19499
19530
  const agents = [];
@@ -19504,7 +19535,7 @@ async function scanAgentsDir(dir, scope) {
19504
19535
  return agents;
19505
19536
  }
19506
19537
  for (const entryName of names) {
19507
- const agentDir = path49.join(dir, entryName);
19538
+ const agentDir = path50.join(dir, entryName);
19508
19539
  try {
19509
19540
  const stat = await fs41.stat(agentDir);
19510
19541
  if (!stat.isDirectory())
@@ -19512,7 +19543,7 @@ async function scanAgentsDir(dir, scope) {
19512
19543
  } catch {
19513
19544
  continue;
19514
19545
  }
19515
- const yamlPath = path49.join(agentDir, "agent.yaml");
19546
+ const yamlPath = path50.join(agentDir, "agent.yaml");
19516
19547
  try {
19517
19548
  const yamlStat = await fs41.stat(yamlPath);
19518
19549
  const parsed = _yamlCache.get(yamlPath, yamlStat.mtimeMs) ?? _yamlCache.set(yamlPath, yamlStat.mtimeMs, parseAgentYaml(await fs41.readFile(yamlPath, "utf-8")));
@@ -19536,8 +19567,8 @@ async function scanAgentsDir(dir, scope) {
19536
19567
  return agents;
19537
19568
  }
19538
19569
  async function ensureDefaultAgent(dir) {
19539
- const defaultDir = path49.join(dir, "default");
19540
- const yamlPath = path49.join(defaultDir, "agent.yaml");
19570
+ const defaultDir = path50.join(dir, "default");
19571
+ const yamlPath = path50.join(defaultDir, "agent.yaml");
19541
19572
  try {
19542
19573
  await fs41.access(yamlPath);
19543
19574
  } catch {
@@ -19560,7 +19591,7 @@ function createAgentConfigRoutes() {
19560
19591
  const projectId = c.req.query("projectId");
19561
19592
  let workspaceAgents = [];
19562
19593
  if (projectId) {
19563
- const wsAgentsDir = path49.join(projectId, ".halo", "agents");
19594
+ const wsAgentsDir = path50.join(projectId, ".halo", "agents");
19564
19595
  workspaceAgents = await scanAgentsDir(wsAgentsDir, "workspace");
19565
19596
  }
19566
19597
  const wsIds = new Set(workspaceAgents.map((a) => a.id));
@@ -19594,11 +19625,11 @@ function createAgentConfigRoutes() {
19594
19625
  if (scope === "workspace") {
19595
19626
  if (!body.projectId)
19596
19627
  return c.json({ error: "projectId required for workspace agents" }, 400);
19597
- baseDir = path49.join(body.projectId, ".halo", "agents");
19628
+ baseDir = path50.join(body.projectId, ".halo", "agents");
19598
19629
  } else {
19599
19630
  baseDir = GLOBAL_AGENTS_DIR4;
19600
19631
  }
19601
- const agentDir = path49.join(baseDir, id);
19632
+ const agentDir = path50.join(baseDir, id);
19602
19633
  try {
19603
19634
  await fs41.access(agentDir);
19604
19635
  return c.json({ error: "Agent already exists" }, 409);
@@ -19607,21 +19638,21 @@ function createAgentConfigRoutes() {
19607
19638
  let conflictScope = null;
19608
19639
  if (scope === "workspace") {
19609
19640
  try {
19610
- await fs41.access(path49.join(GLOBAL_AGENTS_DIR4, id));
19641
+ await fs41.access(path50.join(GLOBAL_AGENTS_DIR4, id));
19611
19642
  conflictScope = "global";
19612
19643
  } catch {
19613
19644
  }
19614
19645
  } else if (body.projectId) {
19615
19646
  try {
19616
- await fs41.access(path49.join(body.projectId, ".halo", "agents", id));
19647
+ await fs41.access(path50.join(body.projectId, ".halo", "agents", id));
19617
19648
  conflictScope = "workspace";
19618
19649
  } catch {
19619
19650
  }
19620
19651
  }
19621
19652
  const scaffoldModel = await scaffoldModelBlock(scope, body.projectId);
19622
19653
  await ensureDir2(agentDir);
19623
- await fs41.writeFile(path49.join(agentDir, "agent.yaml"), defaultAgentYaml(body.name, body.description ?? "", scaffoldModel), "utf-8");
19624
- await fs41.writeFile(path49.join(agentDir, "AGENT.md"), `# ${body.name}
19654
+ await fs41.writeFile(path50.join(agentDir, "agent.yaml"), defaultAgentYaml(body.name, body.description ?? "", scaffoldModel), "utf-8");
19655
+ await fs41.writeFile(path50.join(agentDir, "AGENT.md"), `# ${body.name}
19625
19656
 
19626
19657
  ${body.description ?? ""}
19627
19658
  `, "utf-8");
@@ -19638,7 +19669,7 @@ ${body.description ?? ""}
19638
19669
  if ("error" in resolved)
19639
19670
  return c.json({ error: resolved.error }, 400);
19640
19671
  const { agentDir } = resolved;
19641
- const yamlPath = path49.join(agentDir, "agent.yaml");
19672
+ const yamlPath = path50.join(agentDir, "agent.yaml");
19642
19673
  try {
19643
19674
  const content = await fs41.readFile(yamlPath, "utf-8");
19644
19675
  return c.json({ yaml: content });
@@ -19661,7 +19692,7 @@ ${body.description ?? ""}
19661
19692
  if ("error" in resolved)
19662
19693
  return c.json({ error: resolved.error }, 400);
19663
19694
  const { agentDir } = resolved;
19664
- const yamlPath = path49.join(agentDir, "agent.yaml");
19695
+ const yamlPath = path50.join(agentDir, "agent.yaml");
19665
19696
  try {
19666
19697
  await fs41.access(agentDir);
19667
19698
  } catch {
@@ -19779,10 +19810,10 @@ ${body.description ?? ""}
19779
19810
  });
19780
19811
  function getSessionsDir(agentId, _source, projectId) {
19781
19812
  if (agentId.startsWith("__") && agentId.endsWith("__")) {
19782
- return path49.join(homedir19(), ".halo", "global", "internal-sessions", agentId);
19813
+ return path50.join(homedir19(), ".halo", "global", "internal-sessions", agentId);
19783
19814
  }
19784
- const base = projectId ? path49.join(projectId, ".halo") : path49.join(homedir19(), ".halo");
19785
- return path49.join(base, "sessions", agentId);
19815
+ const base = projectId ? path50.join(projectId, ".halo") : path50.join(homedir19(), ".halo");
19816
+ return path50.join(base, "sessions", agentId);
19786
19817
  }
19787
19818
  app2.get("/agent-configs/:id/sessions", async (c) => {
19788
19819
  const agentId = c.req.param("id");
@@ -19802,7 +19833,7 @@ ${body.description ?? ""}
19802
19833
  if (!f.endsWith(".json"))
19803
19834
  continue;
19804
19835
  try {
19805
- const raw = await fs41.readFile(path49.join(dir, f), "utf-8");
19836
+ const raw = await fs41.readFile(path50.join(dir, f), "utf-8");
19806
19837
  const session = JSON.parse(raw);
19807
19838
  sessions.push({
19808
19839
  id: session.id,
@@ -19829,7 +19860,7 @@ ${body.description ?? ""}
19829
19860
  return null;
19830
19861
  const projectId = c.req.query("projectId");
19831
19862
  const source = c.req.query("source") || "test-chat";
19832
- return path49.join(getSessionsDir(agentId, source, projectId || void 0), `${sessionId}.json`);
19863
+ return path50.join(getSessionsDir(agentId, source, projectId || void 0), `${sessionId}.json`);
19833
19864
  }
19834
19865
  app2.get("/agent-configs/:id/sessions/:sessionId", async (c) => {
19835
19866
  const filePath = sessionFileFromParams(c);
@@ -19855,7 +19886,7 @@ ${body.description ?? ""}
19855
19886
  const dir = getSessionsDir(agentId, source, projectId || void 0);
19856
19887
  if (agentId === GOAL_AGENT_ID && !config.goalModeEnabled) {
19857
19888
  try {
19858
- await fs41.access(path49.join(dir, `${sessionId}.json`));
19889
+ await fs41.access(path50.join(dir, `${sessionId}.json`));
19859
19890
  } catch {
19860
19891
  return c.json({ error: "Goal mode is disabled." }, 403);
19861
19892
  }
@@ -19877,7 +19908,7 @@ ${body.description ?? ""}
19877
19908
  agentSnapshot: body.agentSnapshot ?? {},
19878
19909
  messages: messages2
19879
19910
  };
19880
- await fs41.writeFile(path49.join(dir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf-8");
19911
+ await fs41.writeFile(path50.join(dir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf-8");
19881
19912
  return c.json({ id: sessionId, session: { ...session, messages: void 0 } });
19882
19913
  });
19883
19914
  app2.delete("/agent-configs/:id/sessions", async (c) => {
@@ -19893,7 +19924,7 @@ ${body.description ?? ""}
19893
19924
  for (const f of files) {
19894
19925
  if (!f.endsWith(".json"))
19895
19926
  continue;
19896
- await fs41.rm(path49.join(dir, f));
19927
+ await fs41.rm(path50.join(dir, f));
19897
19928
  deleted++;
19898
19929
  }
19899
19930
  } catch {
@@ -19928,7 +19959,7 @@ var init_agent_configs = __esm({
19928
19959
  init_workspace_path();
19929
19960
  init_mtime_cache();
19930
19961
  _cachedTools = null;
19931
- GLOBAL_AGENTS_DIR4 = path49.join(homedir19(), ".halo", "global", "agents");
19962
+ GLOBAL_AGENTS_DIR4 = path50.join(homedir19(), ".halo", "global", "agents");
19932
19963
  FALLBACK_SCAFFOLD_PROVIDER = "aws-bedrock-claude-invoke";
19933
19964
  _yamlCache = createMtimeCache();
19934
19965
  }
@@ -19937,7 +19968,7 @@ var init_agent_configs = __esm({
19937
19968
  // ../server/dist/routes/skills.js
19938
19969
  import { Hono as Hono5 } from "hono";
19939
19970
  import fs42 from "node:fs/promises";
19940
- import path50 from "node:path";
19971
+ import path51 from "node:path";
19941
19972
  import { homedir as homedir20 } from "node:os";
19942
19973
  import YAML12 from "yaml";
19943
19974
  function defaultSkillSettings() {
@@ -19968,7 +19999,7 @@ async function readYamlFile3(filePath) {
19968
19999
  }
19969
20000
  }
19970
20001
  async function writeYamlFile(filePath, data) {
19971
- await fs42.mkdir(path50.dirname(filePath), { recursive: true });
20002
+ await fs42.mkdir(path51.dirname(filePath), { recursive: true });
19972
20003
  await fs42.writeFile(filePath, YAML12.stringify(data, { lineWidth: 120 }), "utf-8");
19973
20004
  }
19974
20005
  async function addSkillSettings(settingsPath, skillId) {
@@ -20014,7 +20045,7 @@ async function scanSkillsDir(dir, scope) {
20014
20045
  return skills;
20015
20046
  }
20016
20047
  for (const entryName of names) {
20017
- const skillDir = path50.join(dir, entryName);
20048
+ const skillDir = path51.join(dir, entryName);
20018
20049
  try {
20019
20050
  const stat = await fs42.stat(skillDir);
20020
20051
  if (!stat.isDirectory())
@@ -20022,7 +20053,7 @@ async function scanSkillsDir(dir, scope) {
20022
20053
  } catch {
20023
20054
  continue;
20024
20055
  }
20025
- const skillMdPath = path50.join(skillDir, "SKILL.md");
20056
+ const skillMdPath = path51.join(skillDir, "SKILL.md");
20026
20057
  try {
20027
20058
  const mdStat = await fs42.stat(skillMdPath);
20028
20059
  let meta = _skillMdCache.get(skillMdPath, mdStat.mtimeMs);
@@ -20032,7 +20063,7 @@ async function scanSkillsDir(dir, scope) {
20032
20063
  }
20033
20064
  skills.push({ id: entryName, name: meta.name, description: meta.description, path: skillDir, scope, command: meta.command });
20034
20065
  } catch {
20035
- const yamlPath = path50.join(skillDir, "skill.yaml");
20066
+ const yamlPath = path51.join(skillDir, "skill.yaml");
20036
20067
  try {
20037
20068
  const yamlContent = await fs42.readFile(yamlPath, "utf-8");
20038
20069
  const m = yamlContent.match(/^name:\s*(.+)$/m);
@@ -20053,7 +20084,7 @@ function createSkillRoutes() {
20053
20084
  const projectId = c.req.query("projectId");
20054
20085
  let workspaceSkills = [];
20055
20086
  if (projectId) {
20056
- const wsSkillsDir = path50.join(projectId, ".halo", "skills");
20087
+ const wsSkillsDir = path51.join(projectId, ".halo", "skills");
20057
20088
  workspaceSkills = await scanSkillsDir(wsSkillsDir, "workspace");
20058
20089
  }
20059
20090
  const wsIds = new Set(workspaceSkills.map((s) => s.id));
@@ -20076,19 +20107,19 @@ function createSkillRoutes() {
20076
20107
  if (scope === "workspace") {
20077
20108
  if (!body.projectId)
20078
20109
  return c.json({ error: "projectId required for workspace skills" }, 400);
20079
- baseDir = path50.join(body.projectId, ".halo", "skills");
20110
+ baseDir = path51.join(body.projectId, ".halo", "skills");
20080
20111
  } else {
20081
20112
  baseDir = GLOBAL_SKILLS_DIR2;
20082
20113
  }
20083
- const skillDir = path50.join(baseDir, id);
20114
+ const skillDir = path51.join(baseDir, id);
20084
20115
  try {
20085
20116
  await fs42.access(skillDir);
20086
20117
  return c.json({ error: "Skill already exists" }, 409);
20087
20118
  } catch {
20088
20119
  }
20089
20120
  await ensureDir3(skillDir);
20090
- await fs42.writeFile(path50.join(skillDir, "SKILL.md"), toSkillMd(body.name, body.description ?? ""), "utf-8");
20091
- const settingsPath = scope === "workspace" && body.projectId ? path50.join(body.projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
20121
+ await fs42.writeFile(path51.join(skillDir, "SKILL.md"), toSkillMd(body.name, body.description ?? ""), "utf-8");
20122
+ const settingsPath = scope === "workspace" && body.projectId ? path51.join(body.projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
20092
20123
  await addSkillSettings(settingsPath, id);
20093
20124
  const skill = { id, name: body.name, description: body.description ?? "", path: skillDir, scope };
20094
20125
  return c.json({ skill }, 201);
@@ -20101,9 +20132,9 @@ function createSkillRoutes() {
20101
20132
  const projectId = c.req.query("projectId");
20102
20133
  let skillDir;
20103
20134
  if (scope === "workspace" && projectId) {
20104
- skillDir = path50.join(projectId, ".halo", "skills", id);
20135
+ skillDir = path51.join(projectId, ".halo", "skills", id);
20105
20136
  } else {
20106
- skillDir = path50.join(GLOBAL_SKILLS_DIR2, id);
20137
+ skillDir = path51.join(GLOBAL_SKILLS_DIR2, id);
20107
20138
  }
20108
20139
  try {
20109
20140
  await fs42.access(skillDir);
@@ -20111,7 +20142,7 @@ function createSkillRoutes() {
20111
20142
  return c.json({ error: "Skill not found" }, 404);
20112
20143
  }
20113
20144
  await fs42.rm(skillDir, { recursive: true, force: true });
20114
- const settingsPath = scope === "workspace" && projectId ? path50.join(projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
20145
+ const settingsPath = scope === "workspace" && projectId ? path51.join(projectId, ".halo", "settings.yaml") : GLOBAL_SETTINGS_PATH;
20115
20146
  await removeSkillSettings(settingsPath, id);
20116
20147
  return c.json({ ok: true });
20117
20148
  });
@@ -20135,7 +20166,7 @@ var init_skills = __esm({
20135
20166
  init_db();
20136
20167
  init_workspace_path();
20137
20168
  init_mtime_cache();
20138
- GLOBAL_SETTINGS_PATH = path50.join(homedir20(), ".halo", "secrets", "settings.yaml");
20169
+ GLOBAL_SETTINGS_PATH = path51.join(homedir20(), ".halo", "secrets", "settings.yaml");
20139
20170
  _skillMdCache = createMtimeCache();
20140
20171
  }
20141
20172
  });
@@ -20143,7 +20174,7 @@ var init_skills = __esm({
20143
20174
  // ../server/dist/routes/settings.js
20144
20175
  import { Hono as Hono6 } from "hono";
20145
20176
  import fs43 from "node:fs/promises";
20146
- import path51 from "node:path";
20177
+ import path52 from "node:path";
20147
20178
  import { homedir as homedir21 } from "node:os";
20148
20179
  import YAML13 from "yaml";
20149
20180
  async function readSettingsFile(filePath) {
@@ -20155,7 +20186,7 @@ async function readSettingsFile(filePath) {
20155
20186
  }
20156
20187
  }
20157
20188
  async function writeSettingsFile(filePath, data) {
20158
- await fs43.mkdir(path51.dirname(filePath), { recursive: true });
20189
+ await fs43.mkdir(path52.dirname(filePath), { recursive: true });
20159
20190
  await fs43.writeFile(filePath, YAML13.stringify(data, { lineWidth: 120 }), { encoding: "utf-8", mode: 384 });
20160
20191
  try {
20161
20192
  await fs43.chmod(filePath, 384);
@@ -20274,8 +20305,8 @@ function detectOrphans(tree, declared) {
20274
20305
  if (!block || typeof block !== "object")
20275
20306
  continue;
20276
20307
  for (const k of Object.keys(block)) {
20277
- const path73 = `${namespaceId}.${kindWord}.${k}`;
20278
- if (!declaredMap.has(path73)) {
20308
+ const path74 = `${namespaceId}.${kindWord}.${k}`;
20309
+ if (!declaredMap.has(path74)) {
20279
20310
  out.push({ namespaceId, kind: kindWord === "params" ? "param" : "secret", key: k });
20280
20311
  }
20281
20312
  }
@@ -20290,7 +20321,7 @@ function createSettingsRoutes() {
20290
20321
  const globalTree = await readSettingsFile(GLOBAL_SETTINGS_PATH2);
20291
20322
  let workspaceTree = null;
20292
20323
  if (projectId) {
20293
- workspaceTree = await readSettingsFile(path51.join(projectId, ".halo", "settings.yaml"));
20324
+ workspaceTree = await readSettingsFile(path52.join(projectId, ".halo", "settings.yaml"));
20294
20325
  }
20295
20326
  const declared = loadSettingsSchema(projectId ?? void 0);
20296
20327
  const sections = resolveSections(declared, globalTree, workspaceTree);
@@ -20325,7 +20356,7 @@ function createSettingsRoutes() {
20325
20356
  const projectPath = await resolveProjectPath(projectId);
20326
20357
  if (!projectPath)
20327
20358
  return { error: "Project not found", status: 404 };
20328
- return { filePath: path51.join(projectPath, ".halo", "settings.yaml") };
20359
+ return { filePath: path52.join(projectPath, ".halo", "settings.yaml") };
20329
20360
  }
20330
20361
  function flattenDotted(tree, prefix = "") {
20331
20362
  const out = [];
@@ -20413,21 +20444,21 @@ var init_settings = __esm({
20413
20444
  "use strict";
20414
20445
  init_settings_schema();
20415
20446
  init_workspace_path();
20416
- GLOBAL_SETTINGS_PATH2 = path51.join(homedir21(), ".halo", "secrets", "settings.yaml");
20447
+ GLOBAL_SETTINGS_PATH2 = path52.join(homedir21(), ".halo", "secrets", "settings.yaml");
20417
20448
  _changeListeners = [];
20418
20449
  }
20419
20450
  });
20420
20451
 
20421
20452
  // ../server/dist/evolution/archive.js
20422
20453
  import fs44 from "node:fs";
20423
- import path52 from "node:path";
20454
+ import path53 from "node:path";
20424
20455
  import { pipeline } from "node:stream/promises";
20425
20456
  import JSZip from "jszip";
20426
20457
  import { eq as eq11, and as and8, isNull as isNull5, lte, isNotNull as isNotNull3, lt as lt2 } from "drizzle-orm";
20427
20458
  async function zipDir(srcDir, outZip) {
20428
20459
  if (!fs44.existsSync(srcDir))
20429
20460
  return false;
20430
- fs44.mkdirSync(path52.dirname(outZip), { recursive: true });
20461
+ fs44.mkdirSync(path53.dirname(outZip), { recursive: true });
20431
20462
  const zip = new JSZip();
20432
20463
  const walk = (dir, rel) => {
20433
20464
  const entries = fs44.readdirSync(dir, { withFileTypes: true });
@@ -20436,7 +20467,7 @@ async function zipDir(srcDir, outZip) {
20436
20467
  return;
20437
20468
  }
20438
20469
  for (const e of entries) {
20439
- const abs = path52.join(dir, e.name);
20470
+ const abs = path53.join(dir, e.name);
20440
20471
  const childRel = rel ? `${rel}/${e.name}` : e.name;
20441
20472
  if (e.isDirectory()) {
20442
20473
  walk(abs, childRel);
@@ -20617,7 +20648,7 @@ var init_raw_sqlite = __esm({
20617
20648
  import { Hono as Hono7 } from "hono";
20618
20649
  import { eq as eq12, desc as desc2, lt as lt3, and as and9, or, sql } from "drizzle-orm";
20619
20650
  import fs45 from "node:fs/promises";
20620
- import path53 from "node:path";
20651
+ import path54 from "node:path";
20621
20652
  function createEvolutionRoutes() {
20622
20653
  const router = new Hono7();
20623
20654
  router.get("/evolution/runs", async (c) => {
@@ -20682,12 +20713,12 @@ function createEvolutionRoutes() {
20682
20713
  const dir = wsEvoRunDir(row.workspacePath, id);
20683
20714
  let patchMd = null;
20684
20715
  try {
20685
- patchMd = await fs45.readFile(path53.join(dir, "patch.md"), "utf-8");
20716
+ patchMd = await fs45.readFile(path54.join(dir, "patch.md"), "utf-8");
20686
20717
  } catch {
20687
20718
  }
20688
20719
  let scoreJson = null;
20689
20720
  try {
20690
- const raw = await fs45.readFile(path53.join(dir, "score.json"), "utf-8");
20721
+ const raw = await fs45.readFile(path54.join(dir, "score.json"), "utf-8");
20691
20722
  scoreJson = JSON.parse(raw);
20692
20723
  } catch {
20693
20724
  }
@@ -20698,17 +20729,17 @@ function createEvolutionRoutes() {
20698
20729
  }
20699
20730
  let subCliLog = null;
20700
20731
  try {
20701
- subCliLog = await fs45.readFile(path53.join(dir, "sub-cli.log"), "utf-8");
20732
+ subCliLog = await fs45.readFile(path54.join(dir, "sub-cli.log"), "utf-8");
20702
20733
  } catch {
20703
20734
  }
20704
20735
  let skipReasonMd = null;
20705
20736
  try {
20706
- skipReasonMd = await fs45.readFile(path53.join(dir, ".skip.md"), "utf-8");
20737
+ skipReasonMd = await fs45.readFile(path54.join(dir, ".skip.md"), "utf-8");
20707
20738
  } catch {
20708
20739
  }
20709
20740
  let snapshotSummary = null;
20710
20741
  try {
20711
- const raw = await fs45.readFile(path53.join(dir, "source-snapshot.json"), "utf-8");
20742
+ const raw = await fs45.readFile(path54.join(dir, "source-snapshot.json"), "utf-8");
20712
20743
  const parsed = JSON.parse(raw);
20713
20744
  const msgs = parsed.rawMessages ?? [];
20714
20745
  const firstUser = msgs.find((m) => m.role === "user");
@@ -21370,14 +21401,14 @@ function createWebChannel(deps) {
21370
21401
  return findActiveSessionId(sm, accountId, prefix, activeOverrides2, "full") ?? void 0;
21371
21402
  }
21372
21403
  function resolveWorkspace(account, override) {
21373
- let path73 = account.workspacePath;
21404
+ let path74 = account.workspacePath;
21374
21405
  if (override && override !== account.workspacePath) {
21375
21406
  if (account.accessLevel !== "full") {
21376
21407
  return { ok: false, error: "workspace override requires a full-access token" };
21377
21408
  }
21378
- path73 = override;
21409
+ path74 = override;
21379
21410
  }
21380
- const resolved = resolveAccountWorkspace({ ...account, workspacePath: path73 });
21411
+ const resolved = resolveAccountWorkspace({ ...account, workspacePath: path74 });
21381
21412
  if (!resolved)
21382
21413
  return { ok: false, error: "workspace not found" };
21383
21414
  return { ok: true, path: resolved };
@@ -21777,7 +21808,7 @@ var init_session_types = __esm({
21777
21808
  // ../server/dist/routes/halo-city.js
21778
21809
  import { Hono as Hono10 } from "hono";
21779
21810
  import fs46 from "node:fs";
21780
- import path54 from "node:path";
21811
+ import path55 from "node:path";
21781
21812
  import Database6 from "better-sqlite3";
21782
21813
  function scanSkills2(dir) {
21783
21814
  let names;
@@ -21788,7 +21819,7 @@ function scanSkills2(dir) {
21788
21819
  }
21789
21820
  const out = [];
21790
21821
  for (const entry of names) {
21791
- const skillMd = path54.join(dir, entry, "SKILL.md");
21822
+ const skillMd = path55.join(dir, entry, "SKILL.md");
21792
21823
  let stat;
21793
21824
  try {
21794
21825
  stat = fs46.statSync(skillMd);
@@ -21886,10 +21917,10 @@ function snapshotWorkspace(sm, wsPath, label) {
21886
21917
  messageCount: meta.messageCount
21887
21918
  };
21888
21919
  });
21889
- const wsSkills = scanSkills2(path54.join(wsPath, ".halo", "skills"));
21920
+ const wsSkills = scanSkills2(path55.join(wsPath, ".halo", "skills"));
21890
21921
  return {
21891
21922
  path: wsPath,
21892
- key: path54.basename(wsPath) || wsPath,
21923
+ key: path55.basename(wsPath) || wsPath,
21893
21924
  label,
21894
21925
  sessions,
21895
21926
  counts,
@@ -21907,7 +21938,7 @@ function roReader(rawPath) {
21907
21938
  const cached = _roReaders.get(wsPath);
21908
21939
  if (cached)
21909
21940
  return cached;
21910
- const dbPath = path54.join(wsPath, ".halo", "halo.db");
21941
+ const dbPath = path55.join(wsPath, ".halo", "halo.db");
21911
21942
  if (!fs46.existsSync(dbPath))
21912
21943
  return null;
21913
21944
  try {
@@ -21984,12 +22015,12 @@ function snapshotWorkspaceReadonly(wsPath, label) {
21984
22015
  });
21985
22016
  return {
21986
22017
  path: wsPath,
21987
- key: path54.basename(wsPath) || wsPath,
22018
+ key: path55.basename(wsPath) || wsPath,
21988
22019
  label,
21989
22020
  sessions,
21990
22021
  counts,
21991
22022
  totalSessions: counts.total,
21992
- skills: scanSkills2(path54.join(wsPath, ".halo", "skills"))
22023
+ skills: scanSkills2(path55.join(wsPath, ".halo", "skills"))
21993
22024
  };
21994
22025
  }
21995
22026
  function readonlySessionCounts(wsPath, limit) {
@@ -22052,11 +22083,11 @@ function discoverWorkspaces(registry2) {
22052
22083
  }
22053
22084
  };
22054
22085
  for (const { workspacePath } of registry2.list())
22055
- add(workspacePath, path54.basename(workspacePath));
22086
+ add(workspacePath, path55.basename(workspacePath));
22056
22087
  try {
22057
22088
  const rows = getChannelDb().select().from(channelAccounts).all();
22058
22089
  for (const row of rows)
22059
- add(row.workspacePath, path54.basename(row.workspacePath));
22090
+ add(row.workspacePath, path55.basename(row.workspacePath));
22060
22091
  } catch {
22061
22092
  }
22062
22093
  return out;
@@ -22075,7 +22106,7 @@ function createShowRoutes(registry2) {
22075
22106
  return a.response;
22076
22107
  const { account } = a;
22077
22108
  const globalView = account.accessLevel === "full" || account.accessLevel === "observer";
22078
- const targets = globalView ? discoverWorkspaces(registry2) : /* @__PURE__ */ new Map([[account.workspacePath, path54.basename(account.workspacePath)]]);
22109
+ const targets = globalView ? discoverWorkspaces(registry2) : /* @__PURE__ */ new Map([[account.workspacePath, path55.basename(account.workspacePath)]]);
22079
22110
  const workspaces = [];
22080
22111
  for (const [wsPath, label] of targets) {
22081
22112
  try {
@@ -22304,7 +22335,7 @@ var init_commands3 = __esm({
22304
22335
 
22305
22336
  // ../server/dist/routes/agentcore.js
22306
22337
  import { Hono as Hono13 } from "hono";
22307
- import path55 from "node:path";
22338
+ import path56 from "node:path";
22308
22339
  import fs47 from "node:fs";
22309
22340
  function sanitizeRuntimeId(runtimeSessionId) {
22310
22341
  return runtimeSessionId.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80);
@@ -22313,7 +22344,7 @@ function haloSessionId(runtimeSessionId) {
22313
22344
  return `agentcore_${sanitizeRuntimeId(runtimeSessionId)}`;
22314
22345
  }
22315
22346
  function userWorkspace(base, runtimeSessionId) {
22316
- const dir = path55.join(base, "users", sanitizeRuntimeId(runtimeSessionId));
22347
+ const dir = path56.join(base, "users", sanitizeRuntimeId(runtimeSessionId));
22317
22348
  fs47.mkdirSync(dir, { recursive: true });
22318
22349
  return dir;
22319
22350
  }
@@ -22668,7 +22699,7 @@ var init_agentcore = __esm({
22668
22699
 
22669
22700
  // ../server/dist/ws/file-watcher.js
22670
22701
  import fs48 from "node:fs";
22671
- import path56 from "node:path";
22702
+ import path57 from "node:path";
22672
22703
  function enqueueNativeOp(op) {
22673
22704
  const result = nativeOpChain.then(op);
22674
22705
  nativeOpChain = result.catch(() => {
@@ -22676,12 +22707,12 @@ function enqueueNativeOp(op) {
22676
22707
  return result;
22677
22708
  }
22678
22709
  function isUnwatchablePath(absPath) {
22679
- const norm = path56.resolve(absPath);
22680
- if (norm === "/" || norm === path56.parse(norm).root)
22710
+ const norm = path57.resolve(absPath);
22711
+ if (norm === "/" || norm === path57.parse(norm).root)
22681
22712
  return "filesystem root";
22682
22713
  const banned = ["/System", "/Library", "/private", "/usr", "/bin", "/sbin", "/etc"];
22683
22714
  for (const b of banned) {
22684
- if (norm === b || norm.startsWith(b + path56.sep))
22715
+ if (norm === b || norm.startsWith(b + path57.sep))
22685
22716
  return `system path ${b}`;
22686
22717
  }
22687
22718
  return null;
@@ -22821,10 +22852,10 @@ var init_file_watcher = __esm({
22821
22852
  const root = this.workspaceRoot;
22822
22853
  if (!root)
22823
22854
  return;
22824
- const raw = path56.relative(root, absPath);
22855
+ const raw = path57.relative(root, absPath);
22825
22856
  if (!raw || raw.startsWith(".."))
22826
22857
  return;
22827
- const rel = raw.split(path56.sep).join("/");
22858
+ const rel = raw.split(path57.sep).join("/");
22828
22859
  if (IS_WIN32 && rel.split("/").some((seg) => IGNORED_SEGMENT_SET.has(seg)))
22829
22860
  return;
22830
22861
  let action;
@@ -22912,7 +22943,7 @@ var init_file_watcher = __esm({
22912
22943
 
22913
22944
  // ../server/dist/ws/git-dir-watcher.js
22914
22945
  import fs49 from "node:fs";
22915
- import path57 from "node:path";
22946
+ import path58 from "node:path";
22916
22947
  var GitDirWatcher;
22917
22948
  var init_git_dir_watcher = __esm({
22918
22949
  "../server/dist/ws/git-dir-watcher.js"() {
@@ -22936,7 +22967,7 @@ var init_git_dir_watcher = __esm({
22936
22967
  return;
22937
22968
  this.stop();
22938
22969
  this.workspaceRoot = workspaceRoot;
22939
- const gitDir = path57.join(workspaceRoot, ".git");
22970
+ const gitDir = path58.join(workspaceRoot, ".git");
22940
22971
  if (fs49.existsSync(gitDir)) {
22941
22972
  this.watchGitDir(gitDir);
22942
22973
  } else {
@@ -23001,7 +23032,7 @@ var init_git_dir_watcher = __esm({
23001
23032
  const root = this.workspaceRoot;
23002
23033
  if (!root)
23003
23034
  return;
23004
- const gitDir = path57.join(root, ".git");
23035
+ const gitDir = path58.join(root, ".git");
23005
23036
  if (!fs49.existsSync(gitDir))
23006
23037
  return;
23007
23038
  this.closeWatcher();
@@ -23399,7 +23430,7 @@ var init_terminal_manager = __esm({
23399
23430
  });
23400
23431
 
23401
23432
  // ../server/dist/ws/handler.js
23402
- import path58 from "node:path";
23433
+ import path59 from "node:path";
23403
23434
  function setupWebSocketHandler(deps) {
23404
23435
  const { wss: wss2, registry: registry2 } = deps;
23405
23436
  const clients = /* @__PURE__ */ new Set();
@@ -23422,7 +23453,7 @@ function setupWebSocketHandler(deps) {
23422
23453
  ackedChatIds.add(id);
23423
23454
  }
23424
23455
  function resolveProjectPath2(projectId) {
23425
- if (path58.isAbsolute(projectId))
23456
+ if (path59.isAbsolute(projectId))
23426
23457
  return projectId;
23427
23458
  return null;
23428
23459
  }
@@ -24023,11 +24054,11 @@ var init_handler2 = __esm({
24023
24054
  import Database7 from "better-sqlite3";
24024
24055
  import { drizzle as drizzle5 } from "drizzle-orm/better-sqlite3";
24025
24056
  import { sqliteTable as sqliteTable5, text as text5, integer as integer5 } from "drizzle-orm/sqlite-core";
24026
- import path59 from "node:path";
24057
+ import path60 from "node:path";
24027
24058
  import fs50 from "node:fs";
24028
24059
  function createCronDb(globalDir2) {
24029
24060
  fs50.mkdirSync(globalDir2, { recursive: true });
24030
- const dbPath = path59.join(globalDir2, "cron.db");
24061
+ const dbPath = path60.join(globalDir2, "cron.db");
24031
24062
  const sqlite = new Database7(dbPath);
24032
24063
  sqlite.pragma("journal_mode = WAL");
24033
24064
  sqlite.exec(CREATE_SQL4);
@@ -24228,7 +24259,7 @@ var init_dispatcher = __esm({
24228
24259
  // ../server/dist/cron/runner.js
24229
24260
  import { spawn as spawn3, execFileSync } from "node:child_process";
24230
24261
  import fs51 from "node:fs";
24231
- import path60 from "node:path";
24262
+ import path61 from "node:path";
24232
24263
  import { homedir as homedir23 } from "node:os";
24233
24264
  import { Cron } from "croner";
24234
24265
  import { eq as eq14, lt as lt5 } from "drizzle-orm";
@@ -24306,7 +24337,7 @@ function sessionKeyFor(workspacePath, sessionId) {
24306
24337
  try {
24307
24338
  ws = fs51.realpathSync(workspacePath);
24308
24339
  } catch {
24309
- ws = path60.resolve(workspacePath);
24340
+ ws = path61.resolve(workspacePath);
24310
24341
  }
24311
24342
  return `${ws}
24312
24343
  ${sessionId}`;
@@ -24315,10 +24346,10 @@ function setCronSessionRegistry(r) {
24315
24346
  _sessionRegistry = r;
24316
24347
  }
24317
24348
  function logsDir() {
24318
- return path60.join(homedir23(), ".halo", "global", "logs", "cron");
24349
+ return path61.join(homedir23(), ".halo", "global", "logs", "cron");
24319
24350
  }
24320
24351
  function logPathFor(runId) {
24321
- return path60.join(logsDir(), `${runId}.log`);
24352
+ return path61.join(logsDir(), `${runId}.log`);
24322
24353
  }
24323
24354
  function newRunId() {
24324
24355
  return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${Math.random().toString(36).slice(2, 8)}`;
@@ -24786,7 +24817,7 @@ function pruneOldLogs() {
24786
24817
  return;
24787
24818
  const cutoff = Date.now() - LOG_RETENTION_DAYS * DAY_MS2;
24788
24819
  for (const name of fs51.readdirSync(dir)) {
24789
- const p = path60.join(dir, name);
24820
+ const p = path61.join(dir, name);
24790
24821
  try {
24791
24822
  const stat = fs51.statSync(p);
24792
24823
  if (stat.mtimeMs < cutoff)
@@ -25133,7 +25164,7 @@ var init_cron = __esm({
25133
25164
 
25134
25165
  // ../server/dist/extensions/registry.js
25135
25166
  import fs53 from "node:fs";
25136
- import path61 from "node:path";
25167
+ import path62 from "node:path";
25137
25168
  function isExtensionId(id) {
25138
25169
  return ID_RE.test(id);
25139
25170
  }
@@ -25199,17 +25230,17 @@ function parseManifest(raw, dirName, entryExists) {
25199
25230
  ...m.license !== void 0 ? { license: m.license } : {}
25200
25231
  };
25201
25232
  }
25202
- function readExtensionDir(dir, id = path61.basename(dir)) {
25233
+ function readExtensionDir(dir, id = path62.basename(dir)) {
25203
25234
  let raw;
25204
25235
  try {
25205
- raw = JSON.parse(fs53.readFileSync(path61.join(dir, MANIFEST_FILE), "utf-8"));
25236
+ raw = JSON.parse(fs53.readFileSync(path62.join(dir, MANIFEST_FILE), "utf-8"));
25206
25237
  } catch (err) {
25207
25238
  const code = err.code;
25208
25239
  return { id, error: code === "ENOENT" ? `${MANIFEST_FILE} missing` : `${MANIFEST_FILE} unreadable: ${err instanceof Error ? err.message : String(err)}` };
25209
25240
  }
25210
25241
  const parsed = parseManifest(raw, id, (entry) => {
25211
25242
  try {
25212
- return fs53.statSync(path61.join(dir, entry)).isFile();
25243
+ return fs53.statSync(path62.join(dir, entry)).isFile();
25213
25244
  } catch {
25214
25245
  return false;
25215
25246
  }
@@ -25239,7 +25270,7 @@ function scanExtensions() {
25239
25270
  errors.push({ id: entry.name, error: "directory name is not a valid extension id" });
25240
25271
  continue;
25241
25272
  }
25242
- const result = readExtensionDir(path61.join(root, entry.name));
25273
+ const result = readExtensionDir(path62.join(root, entry.name));
25243
25274
  if ("error" in result)
25244
25275
  errors.push(result);
25245
25276
  else
@@ -25278,7 +25309,7 @@ var init_registry2 = __esm({
25278
25309
 
25279
25310
  // ../server/dist/extensions/install.js
25280
25311
  import fs54 from "node:fs";
25281
- import path62 from "node:path";
25312
+ import path63 from "node:path";
25282
25313
  import { randomBytes as randomBytes2 } from "node:crypto";
25283
25314
  import JSZip2 from "jszip";
25284
25315
  function safeEntryPath(name) {
@@ -25309,26 +25340,26 @@ async function unpackZip(zipBuf, dest) {
25309
25340
  const perm = entry.unixPermissions;
25310
25341
  if (typeof perm === "number" && (perm & S_IFMT) === S_IFLNK)
25311
25342
  throw new ExtensionInstallError(`zip entry is a symlink: ${entry.name}`);
25312
- const abs = path62.join(dest, rel);
25343
+ const abs = path63.join(dest, rel);
25313
25344
  if (entry.dir) {
25314
25345
  fs54.mkdirSync(abs, { recursive: true });
25315
25346
  continue;
25316
25347
  }
25317
- fs54.mkdirSync(path62.dirname(abs), { recursive: true });
25348
+ fs54.mkdirSync(path63.dirname(abs), { recursive: true });
25318
25349
  fs54.writeFileSync(abs, await entry.async("nodebuffer"));
25319
25350
  }
25320
25351
  }
25321
25352
  function stripWrapperDir(dir) {
25322
- if (fs54.existsSync(path62.join(dir, MANIFEST_FILE)))
25353
+ if (fs54.existsSync(path63.join(dir, MANIFEST_FILE)))
25323
25354
  return;
25324
25355
  const children = fs54.readdirSync(dir, { withFileTypes: true });
25325
25356
  if (children.length !== 1 || !children[0].isDirectory())
25326
25357
  return;
25327
- const inner = path62.join(dir, children[0].name);
25328
- if (!fs54.existsSync(path62.join(inner, MANIFEST_FILE)))
25358
+ const inner = path63.join(dir, children[0].name);
25359
+ if (!fs54.existsSync(path63.join(inner, MANIFEST_FILE)))
25329
25360
  return;
25330
25361
  for (const name of fs54.readdirSync(inner)) {
25331
- fs54.renameSync(path62.join(inner, name), path62.join(dir, name));
25362
+ fs54.renameSync(path63.join(inner, name), path63.join(dir, name));
25332
25363
  }
25333
25364
  fs54.rmdirSync(inner);
25334
25365
  }
@@ -25338,14 +25369,14 @@ function rand() {
25338
25369
  async function installExtensionZip(zipBuf, expectId) {
25339
25370
  const root = extensionsRoot();
25340
25371
  fs54.mkdirSync(root, { recursive: true });
25341
- const stage = path62.join(root, `.tmp-unpack-${rand()}`);
25372
+ const stage = path63.join(root, `.tmp-unpack-${rand()}`);
25342
25373
  fs54.mkdirSync(stage);
25343
25374
  try {
25344
25375
  await unpackZip(zipBuf, stage);
25345
25376
  stripWrapperDir(stage);
25346
25377
  let manifestId;
25347
25378
  try {
25348
- const raw = JSON.parse(fs54.readFileSync(path62.join(stage, MANIFEST_FILE), "utf-8"));
25379
+ const raw = JSON.parse(fs54.readFileSync(path63.join(stage, MANIFEST_FILE), "utf-8"));
25349
25380
  manifestId = typeof raw.id === "string" ? raw.id : "";
25350
25381
  } catch (err) {
25351
25382
  throw new ExtensionInstallError(`${MANIFEST_FILE} missing or unreadable at the zip root: ${err instanceof Error ? err.message : String(err)}`);
@@ -25357,8 +25388,8 @@ async function installExtensionZip(zipBuf, expectId) {
25357
25388
  const verdict = readExtensionDir(stage, manifestId);
25358
25389
  if ("error" in verdict)
25359
25390
  throw new ExtensionInstallError(`invalid extension: ${verdict.error}`);
25360
- const target = path62.join(root, manifestId);
25361
- const old = path62.join(root, `.old-${manifestId}-${rand()}`);
25391
+ const target = path63.join(root, manifestId);
25392
+ const old = path63.join(root, `.old-${manifestId}-${rand()}`);
25362
25393
  const hadOld = fs54.existsSync(target);
25363
25394
  if (hadOld)
25364
25395
  fs54.renameSync(target, old);
@@ -25383,7 +25414,7 @@ async function installExtensionZip(zipBuf, expectId) {
25383
25414
  function uninstallExtension(id) {
25384
25415
  if (!isExtensionId(id))
25385
25416
  throw new ExtensionInstallError(`"${id}" is not a valid extension id`);
25386
- const dir = path62.join(extensionsRoot(), id);
25417
+ const dir = path63.join(extensionsRoot(), id);
25387
25418
  if (!fs54.existsSync(dir))
25388
25419
  return false;
25389
25420
  fs54.rmSync(dir, { recursive: true, force: true });
@@ -25666,17 +25697,17 @@ function passwordStrengthError(pw) {
25666
25697
  }
25667
25698
  function authMiddleware() {
25668
25699
  return async (c, next) => {
25669
- const path73 = c.req.path;
25670
- if (PUBLIC_PATHS.some((p) => path73 === p || path73.startsWith(p + "?"))) {
25700
+ const path74 = c.req.path;
25701
+ if (PUBLIC_PATHS.some((p) => path74 === p || path74.startsWith(p + "?"))) {
25671
25702
  return next();
25672
25703
  }
25673
- if (EXTENSION_ASSET_PATH.test(path73)) {
25704
+ if (EXTENSION_ASSET_PATH.test(path74)) {
25674
25705
  return next();
25675
25706
  }
25676
25707
  const token = getCookie(c, COOKIE_NAME);
25677
25708
  const payload = validateToken(token);
25678
25709
  if (!payload) {
25679
- if (!path73.startsWith("/api/")) {
25710
+ if (!path74.startsWith("/api/")) {
25680
25711
  return next();
25681
25712
  }
25682
25713
  return c.json({ error: "Unauthorized" }, 401);
@@ -25711,7 +25742,7 @@ var init_auth = __esm({
25711
25742
  // ../server/dist/routes/extensions.js
25712
25743
  import { Hono as Hono16 } from "hono";
25713
25744
  import fs56 from "node:fs";
25714
- import path63 from "node:path";
25745
+ import path64 from "node:path";
25715
25746
  import { Readable as Readable2 } from "node:stream";
25716
25747
  function assetMime(fileName) {
25717
25748
  const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
@@ -25729,12 +25760,12 @@ function resolveAsset(id, version, rest) {
25729
25760
  let dir;
25730
25761
  let real;
25731
25762
  try {
25732
- dir = fs56.realpathSync(path63.join(extensionsRoot(), id));
25733
- real = fs56.realpathSync(path63.join(dir, ...segments));
25763
+ dir = fs56.realpathSync(path64.join(extensionsRoot(), id));
25764
+ real = fs56.realpathSync(path64.join(dir, ...segments));
25734
25765
  } catch {
25735
25766
  return null;
25736
25767
  }
25737
- if (real !== dir && !real.startsWith(dir + path63.sep))
25768
+ if (real !== dir && !real.startsWith(dir + path64.sep))
25738
25769
  return null;
25739
25770
  try {
25740
25771
  if (!fs56.statSync(real).isFile())
@@ -26096,7 +26127,7 @@ var init_ticker = __esm({
26096
26127
  });
26097
26128
 
26098
26129
  // ../server/dist/evolution/spawn.js
26099
- import path64 from "node:path";
26130
+ import path65 from "node:path";
26100
26131
  import { spawn as spawn4 } from "node:child_process";
26101
26132
  import { fileURLToPath as fileURLToPath2 } from "node:url";
26102
26133
  var __dirname2, WRAPPER_PATH, realEvoSpawner;
@@ -26104,8 +26135,8 @@ var init_spawn = __esm({
26104
26135
  "../server/dist/evolution/spawn.js"() {
26105
26136
  "use strict";
26106
26137
  init_child_env();
26107
- __dirname2 = path64.dirname(fileURLToPath2(import.meta.url));
26108
- WRAPPER_PATH = path64.resolve(__dirname2, "evo-wrapper.js");
26138
+ __dirname2 = path65.dirname(fileURLToPath2(import.meta.url));
26139
+ WRAPPER_PATH = path65.resolve(__dirname2, "evo-wrapper.js");
26109
26140
  realEvoSpawner = (mode, id) => {
26110
26141
  const child = spawn4(process.execPath, [WRAPPER_PATH, `--mode=${mode}`, `--id=${id}`], {
26111
26142
  detached: true,
@@ -27584,7 +27615,7 @@ var init_cdn = __esm({
27584
27615
 
27585
27616
  // ../server/dist/channels/wechat/send-media.js
27586
27617
  import fs58 from "node:fs/promises";
27587
- import path65 from "node:path";
27618
+ import path66 from "node:path";
27588
27619
  function inferKind(filePath) {
27589
27620
  const cls = classifyMedia(filePath);
27590
27621
  if (cls === "image")
@@ -27634,7 +27665,7 @@ async function sendMediaFile(params) {
27634
27665
  aesKeyBase64,
27635
27666
  fileSizeCiphertext: prep.filesize,
27636
27667
  fileSize: prep.rawsize,
27637
- fileName: path65.basename(filePath)
27668
+ fileName: path66.basename(filePath)
27638
27669
  });
27639
27670
  const clientId = `halo-media-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
27640
27671
  const body = {
@@ -27798,7 +27829,7 @@ var init_login = __esm({
27798
27829
 
27799
27830
  // ../server/dist/channels/wechat/handler.js
27800
27831
  import fs59 from "node:fs";
27801
- import path66 from "node:path";
27832
+ import path67 from "node:path";
27802
27833
  import QRCode from "qrcode";
27803
27834
  function wxRoute(fromUserId, contextToken, contextTokenAt) {
27804
27835
  return (prev) => {
@@ -28219,7 +28250,7 @@ async function handleSlashCommand(args) {
28219
28250
  await reply(login.message);
28220
28251
  return true;
28221
28252
  }
28222
- const tmpPath = path66.join(tempDir(), `halo-qr-${login.sessionKey}.png`);
28253
+ const tmpPath = path67.join(tempDir(), `halo-qr-${login.sessionKey}.png`);
28223
28254
  await QRCode.toFile(tmpPath, login.qrcodeUrl, { width: 256, margin: 2 });
28224
28255
  await sendMediaFile({
28225
28256
  baseUrl: account.baseUrl,
@@ -28308,7 +28339,7 @@ var init_handler4 = __esm({
28308
28339
 
28309
28340
  // ../server/dist/routes/wechat.js
28310
28341
  import { Hono as Hono18 } from "hono";
28311
- import path67 from "node:path";
28342
+ import path68 from "node:path";
28312
28343
  import fs60 from "node:fs";
28313
28344
  function createWechatRoutes(deps) {
28314
28345
  const { db, channel } = deps;
@@ -28334,7 +28365,7 @@ function createWechatRoutes(deps) {
28334
28365
  return c.json({ error: "sessionKey required" }, 400);
28335
28366
  if (!body.workspacePath)
28336
28367
  return c.json({ error: "workspacePath required" }, 400);
28337
- if (!path67.isAbsolute(body.workspacePath))
28368
+ if (!path68.isAbsolute(body.workspacePath))
28338
28369
  return c.json({ error: "workspacePath must be absolute" }, 400);
28339
28370
  const levelError = accessLevelError(body.accessLevel, CHAT_ACCESS_LEVELS);
28340
28371
  if (levelError)
@@ -28712,10 +28743,10 @@ function createWebRoutes(deps) {
28712
28743
  const filePath = c.req.query("path");
28713
28744
  if (!filePath)
28714
28745
  return c.json({ error: "path required" }, 400);
28715
- const path73 = await import("node:path");
28716
- const resolved = path73.default.resolve(account.workspacePath, filePath);
28717
- const root = path73.default.resolve(account.workspacePath);
28718
- if (resolved !== root && !resolved.startsWith(root + path73.default.sep)) {
28746
+ const path74 = await import("node:path");
28747
+ const resolved = path74.default.resolve(account.workspacePath, filePath);
28748
+ const root = path74.default.resolve(account.workspacePath);
28749
+ if (resolved !== root && !resolved.startsWith(root + path74.default.sep)) {
28719
28750
  return c.json({ error: "path traversal not allowed" }, 403);
28720
28751
  }
28721
28752
  let real;
@@ -28726,7 +28757,7 @@ function createWebRoutes(deps) {
28726
28757
  } catch {
28727
28758
  return c.json({ error: "file not found" }, 404);
28728
28759
  }
28729
- if (real !== realRoot && !real.startsWith(realRoot + path73.default.sep)) {
28760
+ if (real !== realRoot && !real.startsWith(realRoot + path74.default.sep)) {
28730
28761
  return c.json({ error: "path traversal not allowed" }, 403);
28731
28762
  }
28732
28763
  if (isHiddenWorkspacePath(real, realRoot)) {
@@ -28735,7 +28766,7 @@ function createWebRoutes(deps) {
28735
28766
  const stat = fs61.statSync(real);
28736
28767
  if (stat.isDirectory())
28737
28768
  return c.json({ error: "cannot serve directory" }, 400);
28738
- const ext = path73.default.extname(real).toLowerCase();
28769
+ const ext = path74.default.extname(real).toLowerCase();
28739
28770
  const nonImageMimes = {
28740
28771
  ".mp4": "video/mp4",
28741
28772
  ".webm": "video/webm",
@@ -28743,7 +28774,7 @@ function createWebRoutes(deps) {
28743
28774
  ".txt": "text/plain"
28744
28775
  };
28745
28776
  const contentType = imageMimeFromExt(ext) || nonImageMimes[ext] || "application/octet-stream";
28746
- const fileName = path73.default.basename(resolved);
28777
+ const fileName = path74.default.basename(resolved);
28747
28778
  const data = fs61.readFileSync(real);
28748
28779
  c.header("content-type", contentType);
28749
28780
  c.header("content-disposition", `inline; filename="${fileName}"`);
@@ -28965,8 +28996,8 @@ async function authTest(botToken) {
28965
28996
  }
28966
28997
  async function uploadFile(args) {
28967
28998
  const fs65 = await import("node:fs");
28968
- const path73 = await import("node:path");
28969
- const filename = args.filename ?? path73.basename(args.filePath);
28999
+ const path74 = await import("node:path");
29000
+ const filename = args.filename ?? path74.basename(args.filePath);
28970
29001
  const stat = fs65.statSync(args.filePath);
28971
29002
  if (!stat.isFile())
28972
29003
  throw new Error(`[slack:uploadFile] not a regular file: ${args.filePath}`);
@@ -29130,7 +29161,7 @@ var init_api2 = __esm({
29130
29161
  });
29131
29162
 
29132
29163
  // ../server/dist/channels/slack/handler.js
29133
- import path68 from "node:path";
29164
+ import path69 from "node:path";
29134
29165
  import { WebSocket } from "ws";
29135
29166
  function slashToBang(text6, commandNames) {
29136
29167
  if (commandNames.length === 0)
@@ -29261,7 +29292,7 @@ function startSlackChannel(deps) {
29261
29292
  const route = bridge.getRoute(sessionId);
29262
29293
  if (!account || !route)
29263
29294
  return;
29264
- const resolved = path68.resolve(filePath);
29295
+ const resolved = path69.resolve(filePath);
29265
29296
  await sendMediaOrReport({
29266
29297
  filePath,
29267
29298
  account,
@@ -29904,9 +29935,9 @@ async function replyMessage(args) {
29904
29935
  }
29905
29936
  async function uploadImage(args) {
29906
29937
  const fs65 = await import("node:fs");
29907
- const path73 = await import("node:path");
29938
+ const path74 = await import("node:path");
29908
29939
  const buf = fs65.readFileSync(args.filePath);
29909
- const filename = path73.basename(args.filePath);
29940
+ const filename = path74.basename(args.filePath);
29910
29941
  const send = async (token) => {
29911
29942
  const form = new FormData();
29912
29943
  form.append("image_type", "message");
@@ -29923,9 +29954,9 @@ async function uploadImage(args) {
29923
29954
  }
29924
29955
  async function uploadFile2(args) {
29925
29956
  const fs65 = await import("node:fs");
29926
- const path73 = await import("node:path");
29957
+ const path74 = await import("node:path");
29927
29958
  const buf = fs65.readFileSync(args.filePath);
29928
- const filename = args.filename ?? path73.basename(args.filePath);
29959
+ const filename = args.filename ?? path74.basename(args.filePath);
29929
29960
  const send = async (token) => {
29930
29961
  const form = new FormData();
29931
29962
  form.append("file_type", args.fileType);
@@ -29992,7 +30023,7 @@ var init_api3 = __esm({
29992
30023
  });
29993
30024
 
29994
30025
  // ../server/dist/channels/feishu/handler.js
29995
- import path69 from "node:path";
30026
+ import path70 from "node:path";
29996
30027
  import * as Lark from "@larksuiteoapi/node-sdk";
29997
30028
  function buildSessionPrefixForThread2(chatId, rootId) {
29998
30029
  return sessionPrefix("feishu", `${chatId}:${rootId}`);
@@ -30195,7 +30226,7 @@ function startFeishuChannel(deps) {
30195
30226
  const route = bridge.getRoute(sessionId);
30196
30227
  if (!account || !route)
30197
30228
  return;
30198
- const resolved = path69.resolve(filePath);
30229
+ const resolved = path70.resolve(filePath);
30199
30230
  await sendMediaOrReport({
30200
30231
  filePath,
30201
30232
  account,
@@ -30362,7 +30393,7 @@ async function replyToInbound(args) {
30362
30393
  async function sendFeishuMedia(args) {
30363
30394
  const { account, inboundMessageId, isP2P, chatId, filePath } = args;
30364
30395
  const cls = classifyMedia(filePath);
30365
- const ext = path69.extname(filePath).toLowerCase();
30396
+ const ext = path70.extname(filePath).toLowerCase();
30366
30397
  if (cls === "image") {
30367
30398
  const { imageKey } = await uploadImage({ appId: account.appId, appSecret: account.appSecret, filePath });
30368
30399
  await replyToInboundRaw({
@@ -30751,7 +30782,7 @@ var init_event_adapter5 = __esm({
30751
30782
  });
30752
30783
 
30753
30784
  // ../server/dist/channels/wecom/handler.js
30754
- import path70 from "node:path";
30785
+ import path71 from "node:path";
30755
30786
  import fs62 from "node:fs/promises";
30756
30787
  import { WSClient as WSClient2, generateReqId } from "@wecom/aibot-node-sdk";
30757
30788
  function pickConversation(body) {
@@ -30882,13 +30913,13 @@ async function replyText(wsClient, route, text6) {
30882
30913
  }
30883
30914
  async function sendWecomMedia(args) {
30884
30915
  const { wsClient, route, filePath } = args;
30885
- const ext = path70.extname(filePath).toLowerCase();
30916
+ const ext = path71.extname(filePath).toLowerCase();
30886
30917
  const cls = classifyMedia(filePath);
30887
30918
  const type = cls === "image" && [".png", ".jpg", ".jpeg", ".gif"].includes(ext) ? "image" : cls === "video" && ext === ".mp4" ? "video" : "file";
30888
30919
  const buf = await fs62.readFile(filePath);
30889
30920
  if (buf.length > MAX_WECOM_DOWNLOAD_BYTES)
30890
30921
  throw new Error("file exceeds 20MB");
30891
- const { media_id: mediaId } = await wsClient.uploadMedia(buf, { type, filename: path70.basename(filePath) });
30922
+ const { media_id: mediaId } = await wsClient.uploadMedia(buf, { type, filename: path71.basename(filePath) });
30892
30923
  if (route.reqId)
30893
30924
  await wsClient.replyMedia({ headers: { req_id: route.reqId } }, type, mediaId);
30894
30925
  else
@@ -30933,7 +30964,7 @@ function startWecomChannel(deps) {
30933
30964
  const wsClient = states.get(accountId)?.wsClient;
30934
30965
  if (!account || !route || !wsClient)
30935
30966
  return;
30936
- const resolved = path70.resolve(filePath);
30967
+ const resolved = path71.resolve(filePath);
30937
30968
  await sendMediaOrReport({
30938
30969
  filePath,
30939
30970
  account,
@@ -31342,7 +31373,7 @@ import { compress } from "hono/compress";
31342
31373
  import { serve } from "@hono/node-server";
31343
31374
  import { serveStatic } from "@hono/node-server/serve-static";
31344
31375
  import { WebSocketServer } from "ws";
31345
- import path71 from "node:path";
31376
+ import path72 from "node:path";
31346
31377
  import fs63 from "node:fs";
31347
31378
  import { spawnSync as spawnSync3 } from "node:child_process";
31348
31379
  import { homedir as homedir24 } from "node:os";
@@ -31350,9 +31381,9 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
31350
31381
  function findProjectRoot() {
31351
31382
  let dir = process.cwd();
31352
31383
  for (let i = 0; i < 32; i++) {
31353
- if (fs63.existsSync(path71.join(dir, "pnpm-workspace.yaml")))
31384
+ if (fs63.existsSync(path72.join(dir, "pnpm-workspace.yaml")))
31354
31385
  return dir;
31355
- const parent = path71.dirname(dir);
31386
+ const parent = path72.dirname(dir);
31356
31387
  if (parent === dir)
31357
31388
  break;
31358
31389
  dir = parent;
@@ -31361,7 +31392,7 @@ function findProjectRoot() {
31361
31392
  return process.cwd();
31362
31393
  }
31363
31394
  function acquireSingleInstanceLock(lockFile) {
31364
- fs63.mkdirSync(path71.dirname(lockFile), { recursive: true });
31395
+ fs63.mkdirSync(path72.dirname(lockFile), { recursive: true });
31365
31396
  const fd = fs63.openSync(lockFile, fs63.constants.O_RDWR | fs63.constants.O_CREAT, 420);
31366
31397
  const result = spawnSync3("flock", ["-n", "3"], {
31367
31398
  stdio: ["ignore", "ignore", "ignore", fd]
@@ -31418,7 +31449,7 @@ function isProcessAlive(pid) {
31418
31449
  }
31419
31450
  }
31420
31451
  function readGitSha() {
31421
- if (!fs63.existsSync(path71.join(PROJECT_ROOT, "pnpm-workspace.yaml")) || !fs63.existsSync(path71.join(PROJECT_ROOT, ".git")))
31452
+ if (!fs63.existsSync(path72.join(PROJECT_ROOT, "pnpm-workspace.yaml")) || !fs63.existsSync(path72.join(PROJECT_ROOT, ".git")))
31422
31453
  return null;
31423
31454
  try {
31424
31455
  const rev = spawnSync3("git", ["rev-parse", "--short", "HEAD"], { cwd: PROJECT_ROOT, encoding: "utf-8" });
@@ -31439,14 +31470,14 @@ function applySandboxPaths() {
31439
31470
  }
31440
31471
  function resolveFrontendDir() {
31441
31472
  if (process.env.HALO_FRONTEND_DIR)
31442
- return path71.resolve(process.env.HALO_FRONTEND_DIR);
31443
- const bundled = path71.resolve(__dirname3, "..", "admin-out");
31444
- if (fs63.existsSync(path71.join(bundled, "index.html")))
31473
+ return path72.resolve(process.env.HALO_FRONTEND_DIR);
31474
+ const bundled = path72.resolve(__dirname3, "..", "admin-out");
31475
+ if (fs63.existsSync(path72.join(bundled, "index.html")))
31445
31476
  return bundled;
31446
- const sibling = path71.resolve(__dirname3, "..", "..", "admin", "out");
31447
- if (fs63.existsSync(path71.join(sibling, "index.html")))
31477
+ const sibling = path72.resolve(__dirname3, "..", "..", "admin", "out");
31478
+ if (fs63.existsSync(path72.join(sibling, "index.html")))
31448
31479
  return sibling;
31449
- return path71.resolve(PROJECT_ROOT, "packages", "admin", "out");
31480
+ return path72.resolve(PROJECT_ROOT, "packages", "admin", "out");
31450
31481
  }
31451
31482
  async function gracefulShutdown(signal) {
31452
31483
  console.log(`[Server] Received ${signal}, shutting down gracefully...`);
@@ -31518,34 +31549,24 @@ var init_dist2 = __esm({
31518
31549
  init_sandbox();
31519
31550
  init_init();
31520
31551
  init_git_ssh();
31521
- __dirname3 = path71.dirname(fileURLToPath3(import.meta.url));
31552
+ __dirname3 = path72.dirname(fileURLToPath3(import.meta.url));
31522
31553
  PORT = config.server.port;
31523
- HALO_VERSION = "1.5.6";
31554
+ HALO_VERSION = "1.5.7";
31524
31555
  PROJECT_ROOT = findProjectRoot();
31525
- HALO_HOME4 = path71.join(homedir24(), ".halo");
31556
+ HALO_HOME4 = path72.join(homedir24(), ".halo");
31526
31557
  GIT_SHA = readGitSha();
31527
- if (!fs63.existsSync(path71.join(HALO_HOME4, "global", ".template-version"))) {
31558
+ if (!fs63.existsSync(path72.join(HALO_HOME4, "global", ".template-version"))) {
31528
31559
  process.stderr.write("\x1B[31m[Server] ~/.halo/global/ not initialized. Run `halo setup` first.\x1B[0m\n");
31529
31560
  process.exit(1);
31530
31561
  }
31531
- {
31532
- const seedVersion = readSeedVersion(HALO_HOME4);
31533
- if (seedVersion > 0 && seedVersion < TEMPLATE_VERSION) {
31534
- console.log(`[Server] Templates outdated (v${seedVersion} \u2192 v${TEMPLATE_VERSION}), refreshing ~/.halo/global/`);
31535
- try {
31536
- ensureHaloHome(HALO_HOME4);
31537
- } catch (err) {
31538
- console.error(`[Server] Template refresh failed: ${err instanceof Error ? err.message : String(err)}`);
31539
- }
31540
- }
31541
- }
31562
+ refreshTemplatesIfOutdated(HALO_HOME4, "Server");
31542
31563
  AGENTCORE = config.server.runtimeMode === "agentcore";
31543
31564
  if (!AGENTCORE && (!config.server.password && !config.server.passwordEnvPlaintext || !config.server.jwtSecret)) {
31544
31565
  process.stderr.write("\x1B[31m[Server] admin password not configured. Run `halo setup` to set one, or set the HALO_PASSWORD env.\x1B[0m\n");
31545
31566
  process.exit(1);
31546
31567
  }
31547
31568
  if (!AGENTCORE)
31548
- acquireSingleInstanceLock(path71.join(HALO_HOME4, "global", "server.lock"));
31569
+ acquireSingleInstanceLock(path72.join(HALO_HOME4, "global", "server.lock"));
31549
31570
  commandRegistry.assertDispatchCommands(DISPATCH_COMMANDS);
31550
31571
  await initObservability();
31551
31572
  initLogger();
@@ -31607,11 +31628,11 @@ var init_dist2 = __esm({
31607
31628
  app.route("/api", extensionRoutes);
31608
31629
  if (!AGENTCORE)
31609
31630
  start();
31610
- channelDb = createChannelDb(path71.join(HALO_HOME4, "secrets"));
31631
+ channelDb = createChannelDb(path72.join(HALO_HOME4, "secrets"));
31611
31632
  setChannelDb(channelDb);
31612
- setEvoDb(createEvoDb(path71.join(HALO_HOME4, "global")));
31613
- setCronDb(createCronDb(path71.join(HALO_HOME4, "global")));
31614
- setRunsDb(createRunsDb(path71.join(HALO_HOME4, "global")));
31633
+ setEvoDb(createEvoDb(path72.join(HALO_HOME4, "global")));
31634
+ setCronDb(createCronDb(path72.join(HALO_HOME4, "global")));
31635
+ setRunsDb(createRunsDb(path72.join(HALO_HOME4, "global")));
31615
31636
  setEvoSpawner(realEvoSpawner);
31616
31637
  if (!AGENTCORE) {
31617
31638
  startEvoTicker();
@@ -31626,7 +31647,7 @@ var init_dist2 = __esm({
31626
31647
  setCronSessionRegistry(registry);
31627
31648
  if (OWNS_RUNTIMES) {
31628
31649
  for (const ws of listRunningWorkspaces()) {
31629
- if (!fs63.existsSync(path71.join(ws, ".halo")))
31650
+ if (!fs63.existsSync(path72.join(ws, ".halo")))
31630
31651
  continue;
31631
31652
  if (!claimWorkspaceRuntime(ws))
31632
31653
  continue;
@@ -31656,9 +31677,9 @@ var init_dist2 = __esm({
31656
31677
  console.log(`[Server] AgentCore runtime mode \u2014 workspace: ${config.server.agentcoreWorkspace}`);
31657
31678
  }
31658
31679
  FRONTEND_DIR = resolveFrontendDir();
31659
- app.use("/*", serveStatic({ root: path71.relative(process.cwd(), FRONTEND_DIR) }));
31680
+ app.use("/*", serveStatic({ root: path72.relative(process.cwd(), FRONTEND_DIR) }));
31660
31681
  app.get("/*", (c) => {
31661
- const indexPath = path71.join(FRONTEND_DIR, "index.html");
31682
+ const indexPath = path72.join(FRONTEND_DIR, "index.html");
31662
31683
  try {
31663
31684
  const html = fs63.readFileSync(indexPath, "utf-8");
31664
31685
  return c.html(html);
@@ -32450,11 +32471,11 @@ process.emitWarning = (warning, ...args) => {
32450
32471
 
32451
32472
  // src/index.ts
32452
32473
  init_server_supervisor();
32453
- import path72 from "node:path";
32474
+ import path73 from "node:path";
32454
32475
  import fs64 from "node:fs";
32455
32476
  import { homedir as homedir25 } from "node:os";
32456
32477
  import { parseArgs as parseArgs2 } from "node:util";
32457
- var VERSION = "1.5.6";
32478
+ var VERSION = "1.5.7";
32458
32479
  var HELP_TOP = `Usage: halo <command> [options]
32459
32480
 
32460
32481
  Commands:
@@ -32713,8 +32734,8 @@ async function cmdSetup(argv = []) {
32713
32734
  const { buildBindProviderPlan: buildBindProviderPlan2, KEEP_CURRENT: KEEP_CURRENT2, AWS_CHAIN_PROVIDER: AWS_CHAIN_PROVIDER2 } = await Promise.resolve().then(() => (init_setup_bind_provider(), setup_bind_provider_exports));
32714
32735
  const { promptText: promptText2, promptPassword: promptPassword2, promptSelect: promptSelect2, promptMultiSelect: promptMultiSelect2 } = await Promise.resolve().then(() => (init_setup_prompts(), setup_prompts_exports));
32715
32736
  const nonInteractive = argv.includes("--non-interactive") || argv.includes("-y");
32716
- const haloHome2 = path72.join(homedir25(), ".halo");
32717
- const globalDir2 = path72.join(haloHome2, "global");
32737
+ const haloHome2 = path73.join(homedir25(), ".halo");
32738
+ const globalDir2 = path73.join(haloHome2, "global");
32718
32739
  ensureHaloHome2(haloHome2);
32719
32740
  process.stderr.write(`[setup] ${haloHome2}/ ready
32720
32741
  `);
@@ -33057,7 +33078,7 @@ async function cmdUpgrade() {
33057
33078
  `);
33058
33079
  process.exit(1);
33059
33080
  }
33060
- const broken = verifyNativeModules2(path72.join(rootDir, PKG));
33081
+ const broken = verifyNativeModules2(path73.join(rootDir, PKG));
33061
33082
  if (broken.length > 0) {
33062
33083
  process.stderr.write(
33063
33084
  `
@@ -33088,7 +33109,7 @@ halo: upgrade failed (npm exited ${install.status}). ${hint}
33088
33109
  async function cmdAgents(flags) {
33089
33110
  const { initRuntime: initRuntime2, listAgents: listAgents2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
33090
33111
  await initRuntime2();
33091
- const workspace = path72.resolve(flags.workspace ?? process.cwd());
33112
+ const workspace = path73.resolve(flags.workspace ?? process.cwd());
33092
33113
  const agents = await listAgents2(workspace);
33093
33114
  if (agents.length === 0) {
33094
33115
  process.stderr.write("No agents found.\n");
@@ -33104,7 +33125,7 @@ async function cmdAgents(flags) {
33104
33125
  async function cmdSessions(flags) {
33105
33126
  const { initRuntime: initRuntime2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_harness(), harness_exports));
33106
33127
  await initRuntime2();
33107
- const workspace = path72.resolve(flags.workspace ?? process.cwd());
33128
+ const workspace = path73.resolve(flags.workspace ?? process.cwd());
33108
33129
  const sessions = listSessions2(workspace);
33109
33130
  if (sessions.length === 0) {
33110
33131
  process.stderr.write("No sessions found.\n");
@@ -33217,8 +33238,8 @@ ${stdinContent}` : stdinContent;
33217
33238
  harness.destroy();
33218
33239
  }
33219
33240
  }
33220
- var SERVER_LOCK_PATH = path72.join(homedir25(), ".halo", "global", "server.lock");
33221
- var SERVER_LOG_PATH = path72.join(homedir25(), ".halo", "logs", "server.log");
33241
+ var SERVER_LOCK_PATH = path73.join(homedir25(), ".halo", "global", "server.lock");
33242
+ var SERVER_LOG_PATH = path73.join(homedir25(), ".halo", "logs", "server.log");
33222
33243
  function readServerPid() {
33223
33244
  try {
33224
33245
  const txt = fs64.readFileSync(SERVER_LOCK_PATH, "utf-8").trim();
@@ -33308,7 +33329,7 @@ async function cmdServerStart(argv, _unused) {
33308
33329
  return;
33309
33330
  }
33310
33331
  const { spawn: spawn5 } = await import("node:child_process");
33311
- fs64.mkdirSync(path72.dirname(SERVER_LOG_PATH), { recursive: true });
33332
+ fs64.mkdirSync(path73.dirname(SERVER_LOG_PATH), { recursive: true });
33312
33333
  const out = fs64.openSync(SERVER_LOG_PATH, "a");
33313
33334
  const child = spawn5(process.argv[0], [process.argv[1], "server", "start"], {
33314
33335
  detached: true,
@@ -33428,7 +33449,7 @@ function exitMissingSetup() {
33428
33449
  process.exit(1);
33429
33450
  }
33430
33451
  function isHaloHomeReady() {
33431
- return fs64.existsSync(path72.join(homedir25(), ".halo", "global", ".template-version"));
33452
+ return fs64.existsSync(path73.join(homedir25(), ".halo", "global", ".template-version"));
33432
33453
  }
33433
33454
  async function main2() {
33434
33455
  const argv = process.argv.slice(2);