very-happy-cli 0.2.151 → 0.2.153

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 (65) hide show
  1. package/dist/{AcpBackend-blE9IEod.cjs → AcpBackend-DSt8nTYm.cjs} +2 -2
  2. package/dist/{AcpBackend-BjTA5gUB.mjs → AcpBackend-YDGv4E-g.mjs} +2 -2
  3. package/dist/{AcpSessionManager-D-9mrp40.mjs → AcpSessionManager-Cxf5HQX1.mjs} +1 -1
  4. package/dist/{AcpSessionManager-bF8PYu_B.cjs → AcpSessionManager-uov6I9C8.cjs} +9 -9
  5. package/dist/agentGuidance-BM4kMNyr.cjs +553 -0
  6. package/dist/agentGuidance-C8unGWp7.mjs +529 -0
  7. package/dist/{agentHome-D6MSfwnp.cjs → agentHome-BJ7bde35.cjs} +4 -4
  8. package/dist/{agentHome-CeFDNzvq.mjs → agentHome-D68Xh2ae.mjs} +4 -4
  9. package/dist/auto-C-90ojIH.cjs +472 -0
  10. package/dist/auto-CcXxfqkC.mjs +466 -0
  11. package/dist/codex/happyMcpStdioBridge.cjs +4 -2
  12. package/dist/codex/happyMcpStdioBridge.mjs +4 -2
  13. package/dist/{config-C-A1XF62.mjs → config-CHJ189UJ.mjs} +6 -6
  14. package/dist/{config-Cadw1sAk.cjs → config-D23kF0_u.cjs} +6 -6
  15. package/dist/{index-DYQgcgL2.mjs → index-BXA9fxbK.mjs} +6 -6
  16. package/dist/{index-CIyjJy8D.mjs → index-BacwoXzs.mjs} +748 -55
  17. package/dist/{index-DEp6CmmS.mjs → index-BgYZGODP.mjs} +20 -20
  18. package/dist/{index-vyRqLjDv.cjs → index-D49WvbDT.cjs} +6 -6
  19. package/dist/{index-BKGspSWk.cjs → index-DEOVn46R.cjs} +21 -21
  20. package/dist/{index-CqtfRzug.cjs → index-DHrwHxHf.cjs} +805 -105
  21. package/dist/index.cjs +5 -5
  22. package/dist/index.mjs +5 -5
  23. package/dist/install-BiOXBnnS.cjs +118 -0
  24. package/dist/install-Ca7_Z3bV.mjs +114 -0
  25. package/dist/{installPiTools-C-Lxv4Bt.mjs → installPiTools-Cj7yU9Fc.mjs} +2 -1
  26. package/dist/{installPiTools-BKnXjrVu.cjs → installPiTools-DuZ6jZvX.cjs} +3 -2
  27. package/dist/{installTerminalHooks-Bf_adbl3.cjs → installTerminalHooks-BF0kL0ae.cjs} +8 -8
  28. package/dist/{installTerminalHooks-Ch_yTtEA.mjs → installTerminalHooks-Bpl32K75.mjs} +7 -7
  29. package/dist/lib.cjs +4 -4
  30. package/dist/lib.mjs +4 -4
  31. package/dist/limits-BtAuKRl2.mjs +2080 -0
  32. package/dist/limits-DY_sFA3J.cjs +2175 -0
  33. package/dist/{mcp-zu8n8TkR.mjs → mcp-DQkmk4VV.mjs} +15 -8
  34. package/dist/{mcp-CMxEZ6-0.cjs → mcp-JpqI2POW.cjs} +15 -7
  35. package/dist/piExtension.d.mts +4 -1
  36. package/dist/piExtension.mjs +24 -1
  37. package/dist/{piRuntime-Cu6cXZ6N.mjs → piRuntime-DBpe12NN.mjs} +2 -2
  38. package/dist/{piRuntime-D3ny0mVg.cjs → piRuntime-DRbbeQ72.cjs} +2 -2
  39. package/dist/{piTerminal-M677UP7y.mjs → piTerminal-BoHvPEZ_.mjs} +8 -7
  40. package/dist/{piTerminal-kDSd_r9X.cjs → piTerminal-qdJ9KZVL.cjs} +8 -7
  41. package/dist/{runGemini-DuwdVV1O.cjs → runGemini-CtOEFvhf.cjs} +9 -9
  42. package/dist/{runGemini-DvJwQVGp.mjs → runGemini-YqH7a-7O.mjs} +8 -8
  43. package/dist/{runOpenClaw-BG82bF18.mjs → runOpenClaw-DSSyFQBb.mjs} +7 -7
  44. package/dist/{runOpenClaw-BsvtCE_H.cjs → runOpenClaw-DoqZXBvL.cjs} +7 -7
  45. package/dist/{send-aUkjhey7.cjs → send-9075kHZP.cjs} +6 -6
  46. package/dist/{send-o6WM-1G8.mjs → send-Cs-RwqNf.mjs} +6 -6
  47. package/dist/{sessions-DlU3foY_.mjs → sessions-iBnYzVUK.mjs} +6 -6
  48. package/dist/{sessions-B3vP18kz.cjs → sessions-qULzUzlI.cjs} +6 -6
  49. package/dist/{spawn-DxHIIqhi.mjs → spawn-C6fRkedx.mjs} +7 -32
  50. package/dist/{spawn-DH6_p4-f.cjs → spawn-od9V23cc.cjs} +8 -33
  51. package/dist/{teams-DoAA_CcN.mjs → teams-CkrWQ4uM.mjs} +16 -73
  52. package/dist/{teams-BW8D1n3o.cjs → teams-CrCzU-AV.cjs} +14 -71
  53. package/dist/terminalTitleSuggest-DKgX3osW.mjs +38 -0
  54. package/dist/terminalTitleSuggest-Dji8I0Jy.cjs +45 -0
  55. package/dist/{todo-Bcc2XxGh.cjs → todo-BCT_ZPuV.cjs} +4 -23
  56. package/dist/{todo-BhJbG5eq.mjs → todo-aCZmSt27.mjs} +2 -21
  57. package/dist/{types-BN3eiipi.mjs → types-BvqOVUOW.mjs} +214 -1022
  58. package/dist/{types-CPubg05Z.cjs → types-CUCU7Vw8.cjs} +43 -863
  59. package/package.json +7 -7
  60. package/dist/agentGuidance-BXa9ikrZ.cjs +0 -267
  61. package/dist/agentGuidance-Dvo3cZMW.mjs +0 -251
  62. package/dist/limits-DS-bnYYp.mjs +0 -1165
  63. package/dist/limits-DwcY3kyi.cjs +0 -1236
  64. package/dist/projectPath-CSpqWdxp.mjs +0 -10
  65. package/dist/projectPath-ChLhdOyt.cjs +0 -13
@@ -3,14 +3,14 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var index = require('./types-CPubg05Z.cjs');
7
- var persistence = require('./limits-DwcY3kyi.cjs');
6
+ var index = require('./types-CUCU7Vw8.cjs');
7
+ var persistence = require('./limits-DY_sFA3J.cjs');
8
8
  var spawn = require('cross-spawn');
9
9
  var path = require('node:path');
10
10
  var node_readline = require('node:readline');
11
11
  var node_fs = require('node:fs');
12
- var projectPath = require('./projectPath-ChLhdOyt.cjs');
13
- var agentGuidance = require('./agentGuidance-BXa9ikrZ.cjs');
12
+ var terminalTitleSuggest = require('./terminalTitleSuggest-Dji8I0Jy.cjs');
13
+ var agentGuidance = require('./agentGuidance-BM4kMNyr.cjs');
14
14
  var promises = require('node:fs/promises');
15
15
  var fs = require('fs/promises');
16
16
  var ink = require('ink');
@@ -492,7 +492,7 @@ function shouldIncludeCoAuthoredBy() {
492
492
 
493
493
  const BASE_SYSTEM_PROMPT = (() => agentGuidance.trimIdent(`
494
494
  This chat is titled automatically from the user's first message. Call "mcp__happy__change_title" only when the topic has clearly moved on or the current title is too generic to find this chat later. Do not call it as a ritual at the start of a chat, and not for a quick question you can just answer.
495
- `))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" + index.BUILTIN_TODO_DISCOVERY;
495
+ `))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" + persistence.BUILTIN_TODO_DISCOVERY;
496
496
  const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
497
497
  When you yourself create a git commit, instead of just giving co-credit to Claude, also give credit to Happy like so:
498
498
 
@@ -522,7 +522,7 @@ class ExitCodeError extends Error {
522
522
  this.exitCode = exitCode;
523
523
  }
524
524
  }
525
- const claudeCliPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_local_launcher.cjs"));
525
+ const claudeCliPath = path.resolve(path.join(terminalTitleSuggest.projectPath(), "scripts", "claude_local_launcher.cjs"));
526
526
  function quoteShellArg(value) {
527
527
  return `'${value.replace(/'/g, `'\\''`)}'`;
528
528
  }
@@ -3507,7 +3507,7 @@ class StreamRelay {
3507
3507
  }
3508
3508
  this.pending.text += text;
3509
3509
  this.pending.bytes += Buffer.byteLength(text, "utf8");
3510
- if (this.pending.bytes >= STREAM_FLUSH_MAX_BYTES || this.pending.text.length >= index.STREAM_DELTA_MAX_CHARS) {
3510
+ if (this.pending.bytes >= STREAM_FLUSH_MAX_BYTES || this.pending.text.length >= persistence.STREAM_DELTA_MAX_CHARS) {
3511
3511
  this.flushPending();
3512
3512
  return;
3513
3513
  }
@@ -4878,8 +4878,8 @@ function createSessionMetadata(opts) {
4878
4878
  ...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
4879
4879
  homeDir: os.homedir(),
4880
4880
  happyHomeDir: persistence.configuration.happyHomeDir,
4881
- happyLibDir: projectPath.projectPath(),
4882
- happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
4881
+ happyLibDir: terminalTitleSuggest.projectPath(),
4882
+ happyToolsDir: path.resolve(terminalTitleSuggest.projectPath(), "tools", "unpacked"),
4883
4883
  startedFromDaemon: opts.startedBy === "daemon",
4884
4884
  hostPid: process.pid,
4885
4885
  startedBy: opts.startedBy || "terminal",
@@ -5206,7 +5206,7 @@ function printCliUpdateStatus(state) {
5206
5206
  console.log(chalk.yellow(" \u25B3 A newer CLI is available."));
5207
5207
  }
5208
5208
  if (summary.installCommand) console.log(` Update: ${summary.installCommand}`);
5209
- const facts = gatherInstallLocationFacts(projectPath.projectPath());
5209
+ const facts = gatherInstallLocationFacts(terminalTitleSuggest.projectPath());
5210
5210
  for (const warning of facts ? installLocationWarnings(facts, state?.cliUpdate?.recommendedVersion ?? void 0) : []) {
5211
5211
  console.log(chalk.yellow(` \u26A0 ${warning}`));
5212
5212
  }
@@ -5338,7 +5338,7 @@ ${typeLabels[type] || type}:`));
5338
5338
  console.log(chalk.yellow("No log files found"));
5339
5339
  }
5340
5340
  console.log(chalk.bold("\n\u{1F527} Daemon Spawn Diagnostics"));
5341
- const projectRoot = projectPath.projectPath();
5341
+ const projectRoot = terminalTitleSuggest.projectPath();
5342
5342
  const wrapperPath = path.join(projectRoot, "bin", "very-happy.mjs");
5343
5343
  const cliEntrypoint = path.join(projectRoot, "dist", "index.mjs");
5344
5344
  console.log(`Project Root: ${chalk.blue(projectRoot)}`);
@@ -5402,8 +5402,8 @@ ${typeLabels[type] || type}:`));
5402
5402
  }
5403
5403
  console.log(chalk.gray(" An external claude command is only needed for native terminal/mirror use."));
5404
5404
  try {
5405
- const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-D6MSfwnp.cjs'); });
5406
- const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-CPubg05Z.cjs'); }).then(function (n) { return n.index; });
5405
+ const { resolveAgentHomesForCli } = await Promise.resolve().then(function () { return require('./agentHome-BJ7bde35.cjs'); });
5406
+ const { describeAgentHome } = await Promise.resolve().then(function () { return require('./types-CUCU7Vw8.cjs'); }).then(function (n) { return n.index; });
5407
5407
  const homes = await resolveAgentHomesForCli();
5408
5408
  console.log(chalk.green(`\u2713 Claude config dir: ${describeAgentHome(homes.claudeConfigDir)}`));
5409
5409
  console.log(chalk.green(`\u2713 Codex home: ${describeAgentHome(homes.codexHome)}`));
@@ -5514,7 +5514,7 @@ async function registryVersion(name) {
5514
5514
  return typeof data.version === "string" ? stableVersion(data.version) : null;
5515
5515
  }
5516
5516
  async function detectAgentVersions(deps = { probe: probeAgentVersion, registryVersion }, now = Date.now()) {
5517
- const agents = await Promise.all(index.AGENT_VERSION_SOURCES.map(async (source) => {
5517
+ const agents = await Promise.all(persistence.AGENT_VERSION_SOURCES.map(async (source) => {
5518
5518
  const local = source.command ? await deps.probe(source.command).catch(() => ({ version: null, missing: false })) : { version: stableVersion(persistence.packageJson.dependencies["@anthropic-ai/claude-agent-sdk"]), missing: false };
5519
5519
  if (local.missing) return { id: source.id, installed: null, latest: null, status: "not-installed" };
5520
5520
  const latest = await deps.registryVersion(source.package).catch(() => null);
@@ -5953,7 +5953,7 @@ function getRuntime() {
5953
5953
  const isBun = () => getRuntime() === "bun";
5954
5954
 
5955
5955
  function spawnHappyCLI(args, options = {}) {
5956
- const projectRoot = projectPath.projectPath();
5956
+ const projectRoot = terminalTitleSuggest.projectPath();
5957
5957
  const entrypoint = path.join(projectRoot, "dist", "index.mjs");
5958
5958
  let directory;
5959
5959
  if ("cwd" in options) {
@@ -6230,7 +6230,7 @@ const DENIED_BASENAME_PATTERNS = [
6230
6230
  const DENIED_PATH_SUFFIXES = [
6231
6231
  `${path.sep}.git${path.sep}config`
6232
6232
  ];
6233
- function expandHome(input, home) {
6233
+ function expandHome$1(input, home) {
6234
6234
  if (input === "~") return home;
6235
6235
  if (input.startsWith(`~${path.sep}`) || input.startsWith("~/")) {
6236
6236
  return path.resolve(home, input.slice(2));
@@ -6253,7 +6253,7 @@ function checkPreviewPath(input, opts) {
6253
6253
  if (input.includes("\0")) {
6254
6254
  return { resolved: "", deniedReason: "path contains a NUL byte" };
6255
6255
  }
6256
- const expanded = expandHome(input.trim(), home);
6256
+ const expanded = expandHome$1(input.trim(), home);
6257
6257
  const resolved = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
6258
6258
  for (const dir of DENIED_HOME_DIRS) {
6259
6259
  if (isUnder(resolved, path.resolve(home, dir))) {
@@ -7784,7 +7784,7 @@ const defaults = {
7784
7784
  npm: runNpm,
7785
7785
  fs: { lstat: promises.lstat, readlink: promises.readlink, unlink: promises.unlink, rm: promises.rm, readFile: promises.readFile, symlink: promises.symlink, access: promises.access },
7786
7786
  platform: process.platform,
7787
- runningPackageDir: projectPath.projectPath()
7787
+ runningPackageDir: terminalTitleSuggest.projectPath()
7788
7788
  };
7789
7789
  async function runningVersion(deps) {
7790
7790
  if (!deps.runningPackageDir) return null;
@@ -8144,7 +8144,7 @@ function parseJsonObject(text) {
8144
8144
  return null;
8145
8145
  }
8146
8146
  }
8147
- function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href))).resolve(s)) {
8147
+ function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href))).resolve(s)) {
8148
8148
  const platformArch = `${platform}-${arch}`;
8149
8149
  const candidates = [
8150
8150
  `@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
@@ -8857,8 +8857,8 @@ function createMirrorManager(deps) {
8857
8857
  machineId: deps.machineId,
8858
8858
  homeDir: os.homedir(),
8859
8859
  happyHomeDir: persistence.configuration.happyHomeDir,
8860
- happyLibDir: projectPath.projectPath(),
8861
- happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
8860
+ happyLibDir: terminalTitleSuggest.projectPath(),
8861
+ happyToolsDir: path.resolve(terminalTitleSuggest.projectPath(), "tools", "unpacked"),
8862
8862
  startedBy: "terminal",
8863
8863
  lifecycleState: "running",
8864
8864
  lifecycleStateSince: Date.now(),
@@ -9380,6 +9380,20 @@ export default async function(pi) {
9380
9380
  return data.result;
9381
9381
  }
9382
9382
  await rpc('initialize', {protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'very-happy-pi-teams',version:'1'}});
9383
+ if (!managed && pi.setSessionName) {
9384
+ // B-500: name the pi session from its first prompt (managed sessions are titled by TitleGenerator).
9385
+ let titleArmed = false, generation = 0;
9386
+ pi.on('session_start', async () => { generation += 1; titleArmed = !pi.getSessionName?.(); });
9387
+ pi.on('before_agent_start', async (event) => {
9388
+ const prompt = event && typeof event.prompt === 'string' ? event.prompt : '';
9389
+ if (!titleArmed || !prompt.trim()) return;
9390
+ titleArmed = false;
9391
+ const expected = generation;
9392
+ rpc('very-happy/terminal-title-suggest', {prompt}).then(result => {
9393
+ if (result && result.title && expected === generation && !pi.getSessionName?.()) pi.setSessionName(result.title);
9394
+ }).catch(() => {});
9395
+ });
9396
+ }
9383
9397
  const available = await rpc('tools/list', {});
9384
9398
  for (const tool of available.tools || []) {
9385
9399
  pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
@@ -9539,11 +9553,14 @@ function agentPiece(body) {
9539
9553
  }
9540
9554
  return null;
9541
9555
  }
9542
- function analyzeLatestTurn(entries) {
9556
+ function userPromptText(body) {
9557
+ return isUserPrompt(body) ? body.content.text : null;
9558
+ }
9559
+ function analyzeLatestTurn(entries, options = {}) {
9543
9560
  const lastSeq = entries.length > 0 ? entries[entries.length - 1].seq : 0;
9544
9561
  let userIndex = -1;
9545
9562
  for (let i = entries.length - 1; i >= 0; i--) {
9546
- if (isUserPrompt(entries[i].body)) {
9563
+ if (isUserPrompt(entries[i].body) && (!options.anchor || options.anchor(entries[i].body))) {
9547
9564
  userIndex = i;
9548
9565
  break;
9549
9566
  }
@@ -9557,6 +9574,10 @@ function analyzeLatestTurn(entries) {
9557
9574
  const emitsStarts = entries.some((entry) => sessionEnvelope(entry.body)?.ev?.t === "turn-start");
9558
9575
  for (let i = userIndex + 1; i < entries.length; i++) {
9559
9576
  const body = entries[i].body;
9577
+ if (options.anchor && isUserPrompt(body)) {
9578
+ if (ended) break;
9579
+ continue;
9580
+ }
9560
9581
  const env = sessionEnvelope(body);
9561
9582
  if (env?.ev?.t === "turn-start") {
9562
9583
  lastMarker = "start";
@@ -10049,7 +10070,7 @@ function readReceipt(home, id) {
10049
10070
  throw error;
10050
10071
  }
10051
10072
  }
10052
- function writeReceipt(home, receipt) {
10073
+ function writeReceipt$1(home, receipt) {
10053
10074
  persistence.ensurePrivateDirectorySync(path.join(home, "teams", "effects"));
10054
10075
  const target = receiptPath(home, receipt.operationId);
10055
10076
  const tmp = `${target}.${process.pid}.tmp`;
@@ -10126,7 +10147,7 @@ function createTeamWorker(deps) {
10126
10147
  const action = async (teamId, requestId, value) => (await http.post(`/v1/teams/${encodeURIComponent(teamId)}/actions`, { requestId, action: value })).data;
10127
10148
  async function complete(op, receipt) {
10128
10149
  await action(op.teamId, `complete-${op.id}`, { type: "complete-operation", operationId: op.id, machineId: deps.machineId, claimId: receipt.claimId, ...receipt.sessionId ? { sessionId: receipt.sessionId } : {}, ...op.type === "spawn" && receipt.directory ? { workingDirectory: receipt.directory } : {} });
10129
- writeReceipt(home, { ...receipt, phase: "completed" });
10150
+ writeReceipt$1(home, { ...receipt, phase: "completed" });
10130
10151
  }
10131
10152
  async function execute(op) {
10132
10153
  let receipt = readReceipt(home, op.id);
@@ -10143,7 +10164,7 @@ function createTeamWorker(deps) {
10143
10164
  op = claimed.operation;
10144
10165
  credential = claimed.credential;
10145
10166
  receipt = { operationId: op.id, teamId: op.teamId, claimId: op.claimId, phase: "prepared" };
10146
- writeReceipt(home, receipt);
10167
+ writeReceipt$1(home, receipt);
10147
10168
  }
10148
10169
  if (receipt.phase === "completed") return;
10149
10170
  try {
@@ -10156,7 +10177,7 @@ function createTeamWorker(deps) {
10156
10177
  if (!task) throw new Error("Task state unavailable; refusing to start");
10157
10178
  if (task.currentAttemptId !== op.attemptId || ["done", "cancelled"].includes(task.status)) {
10158
10179
  await action(op.teamId, `cancelled-before-spawn-${op.id}`, { type: "fail-operation", operationId: op.id, machineId: deps.machineId, claimId: receipt.claimId, error: "task_closed_before_spawn" });
10159
- writeReceipt(home, { ...receipt, phase: "completed" });
10180
+ writeReceipt$1(home, { ...receipt, phase: "completed" });
10160
10181
  return;
10161
10182
  }
10162
10183
  if (!op.directory) throw new Error("No repository selected for worker");
@@ -10166,17 +10187,17 @@ function createTeamWorker(deps) {
10166
10187
  const scopeFile = path.join(scopes, `${op.id}.json`);
10167
10188
  persistence.writePrivateFileSync(scopeFile, JSON.stringify({ serverUrl: server, scopeToken: credential.token, teamId: op.teamId, botId: op.botId, taskId: op.taskId, attemptId: op.attemptId }));
10168
10189
  receipt = { ...receipt, ...resource, scopeFile, phase: "spawning" };
10169
- writeReceipt(home, receipt);
10190
+ writeReceipt$1(home, receipt);
10170
10191
  const spawned = await deps.spawn({ directory: resource.directory, agent: op.assistant === "pi-acp" ? "pi" : op.assistant, permissionMode: op.permissionMode === "bypassPermissions" ? "bypassPermissions" : "default", spawnedBy: "teams", environmentVariables: { VH_TEAM_SCOPE_FILE: scopeFile, VH_TEAM_OPERATION_ID: op.id } });
10171
10192
  if (spawned.type !== "success") throw new Error(spawned.type === "error" ? spawned.errorMessage : "Directory approval required");
10172
10193
  receipt = { ...receipt, sessionId: spawned.sessionId, phase: "spawned" };
10173
- writeReceipt(home, receipt);
10194
+ writeReceipt$1(home, receipt);
10174
10195
  }
10175
10196
  if (receipt.phase === "spawning") {
10176
10197
  const matches = Object.entries(persistence.readPersistedSessions()).filter(([, entry]) => entry.metadata?.teamOperationId === op.id);
10177
10198
  if (matches.length !== 1) throw new Error("Spawn outcome unknown; reconcile the worker before retrying");
10178
10199
  receipt = { ...receipt, sessionId: matches[0][0], phase: "spawned" };
10179
- writeReceipt(home, receipt);
10200
+ writeReceipt$1(home, receipt);
10180
10201
  }
10181
10202
  if (receipt.phase === "spawned") {
10182
10203
  const current = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
@@ -10191,14 +10212,14 @@ function createTeamWorker(deps) {
10191
10212
  const key = await waitForSessionKey(receipt.sessionId, 1e4);
10192
10213
  await sendUserMessage(receipt.sessionId, key, teamInitialPrompt(op.prompt, bot?.root === true, receipt.directory), "teams", { localId: `teams-initial-${op.id}`, sentFrom: "team", ...op.model ? { model: op.model } : {} });
10193
10214
  receipt = { ...receipt, phase: "delivered" };
10194
- writeReceipt(home, receipt);
10215
+ writeReceipt$1(home, receipt);
10195
10216
  }
10196
10217
  await complete(op, receipt);
10197
10218
  } else {
10198
10219
  const team = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
10199
10220
  const latest = team.operations.find((candidate) => candidate.id === op.id);
10200
10221
  if (latest?.status === "completed") {
10201
- writeReceipt(home, { ...receipt, phase: "completed" });
10222
+ writeReceipt$1(home, { ...receipt, phase: "completed" });
10202
10223
  return;
10203
10224
  }
10204
10225
  const bot = team.bots.find((candidate) => candidate.id === op.botId);
@@ -10328,6 +10349,643 @@ Use the teams tools to inspect current task state before acting. This message is
10328
10349
  } };
10329
10350
  }
10330
10351
 
10352
+ function resolveSpawnPermissionMode(agent, explicit) {
10353
+ return explicit ?? persistence.getAgentCodeDefaults(agent ?? "claude").permissionMode;
10354
+ }
10355
+ function outboundPermissionMode(agent, mode) {
10356
+ const key = persistence.normalizeAgentKey(agent ?? "claude");
10357
+ if ((key === "claude" || key === "pi") && mode === "yolo") return "bypassPermissions";
10358
+ return mode;
10359
+ }
10360
+ function resolveFirstMessageMeta(input) {
10361
+ const meta = {
10362
+ permissionMode: outboundPermissionMode(input.agent, input.permissionMode)
10363
+ };
10364
+ if (input.explicitModel !== void 0) {
10365
+ meta.model = input.explicitModel === "default" ? null : input.explicitModel;
10366
+ }
10367
+ if (persistence.normalizeAgentKey(input.agent ?? "claude") !== "claude") return meta;
10368
+ if (meta.model === void 0) {
10369
+ const model = persistence.getAgentCodeDefaults("claude").modelMode;
10370
+ const runnable = !persistence.isClaudeOpus55Model(model) || persistence.supportsClaudeOpus55({ capabilities: input.capabilities ?? null });
10371
+ meta.model = runnable && model !== "default" ? model : null;
10372
+ }
10373
+ meta.effort = null;
10374
+ return meta;
10375
+ }
10376
+
10377
+ function payloadJson(payload) {
10378
+ if (!payload) return void 0;
10379
+ try {
10380
+ return JSON.parse(payload);
10381
+ } catch {
10382
+ return void 0;
10383
+ }
10384
+ }
10385
+ function lookupPath(root, path) {
10386
+ let current = root;
10387
+ for (const segment of path) {
10388
+ if (current === null || typeof current !== "object") return void 0;
10389
+ current = current[segment];
10390
+ }
10391
+ return current;
10392
+ }
10393
+ function scalar(value) {
10394
+ if (value === void 0 || value === null) return "";
10395
+ if (typeof value === "string") return value;
10396
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
10397
+ try {
10398
+ return JSON.stringify(value);
10399
+ } catch {
10400
+ return "";
10401
+ }
10402
+ }
10403
+ function formatNow(now, tz) {
10404
+ if (!tz) return new Date(now).toISOString();
10405
+ try {
10406
+ const parts = new Intl.DateTimeFormat("en-CA", { timeZone: tz, hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: "longOffset" }).formatToParts(new Date(now));
10407
+ const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
10408
+ const offset = get("timeZoneName").replace(/^GMT$/, "+00:00").replace(/^GMT/, "");
10409
+ return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}${offset || "Z"}`;
10410
+ } catch {
10411
+ return new Date(now).toISOString();
10412
+ }
10413
+ }
10414
+ function renderTemplate(text, context) {
10415
+ const json = payloadJson(context.payload);
10416
+ return text.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\s*\}\}/g, (whole, name) => {
10417
+ if (name === "payload") return context.payload ?? "";
10418
+ if (name.startsWith("payload.")) return scalar(lookupPath(json, name.slice("payload.".length).split(".")));
10419
+ if (name === "run.id") return context.runId;
10420
+ if (name === "automation.name") return context.automationName;
10421
+ if (name === "now") return formatNow(context.now ?? Date.now(), context.tz);
10422
+ return whole;
10423
+ });
10424
+ }
10425
+ function renderAction(action, context) {
10426
+ const r = (value) => renderTemplate(value, context);
10427
+ if (action.kind === "spawn") return { ...action, prompt: r(action.prompt) };
10428
+ if (action.kind === "send") return { ...action, prompt: r(action.prompt) };
10429
+ return { ...action, command: action.command.map(r), ...action.env ? { env: Object.fromEntries(Object.entries(action.env).map(([k, v]) => [k, r(v)])) } : {} };
10430
+ }
10431
+ function clipText(text, maxChars, keep = "head") {
10432
+ if (text.length <= maxChars) return text;
10433
+ return keep === "head" ? text.slice(0, maxChars) : text.slice(text.length - maxChars);
10434
+ }
10435
+ const DURATION_UNITS = { ms: 1, s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
10436
+ function parseDuration(raw) {
10437
+ const text = raw.trim();
10438
+ if (/^\d+$/.test(text)) return Number(text);
10439
+ const re = /(\d+(?:\.\d+)?)(ms|s|m|h|d)/gy;
10440
+ let total = 0;
10441
+ let matched = 0;
10442
+ let last = 0;
10443
+ let m;
10444
+ while ((m = re.exec(text)) !== null) {
10445
+ total += Number(m[1]) * DURATION_UNITS[m[2]];
10446
+ matched++;
10447
+ last = re.lastIndex;
10448
+ }
10449
+ if (matched === 0 || last !== text.length) throw new Error(`Invalid duration: ${raw} (use e.g. 90s, 5m, 1h30m, 2d)`);
10450
+ return Math.round(total);
10451
+ }
10452
+ function formatDuration(ms) {
10453
+ if (ms % 864e5 === 0) return `${ms / 864e5}d`;
10454
+ if (ms % 36e5 === 0) return `${ms / 36e5}h`;
10455
+ if (ms % 6e4 === 0) return `${ms / 6e4}m`;
10456
+ if (ms % 1e3 === 0) return `${ms / 1e3}s`;
10457
+ return `${ms}ms`;
10458
+ }
10459
+ function triggerFromFlags(flags, now = Date.now()) {
10460
+ const given = [flags.cron !== void 0, flags.every !== void 0, flags.at !== void 0, flags.manual === true].filter(Boolean).length;
10461
+ if (given === 0) {
10462
+ if (flags.tz !== void 0) throw new Error("--tz only applies with --cron");
10463
+ return void 0;
10464
+ }
10465
+ if (given > 1) throw new Error("Use exactly one of --cron, --every, --at, --manual");
10466
+ if (flags.cron !== void 0) {
10467
+ if (!flags.tz) throw new Error("--cron requires --tz <IANA zone> (e.g. Asia/Singapore)");
10468
+ return { kind: "cron", expr: flags.cron, tz: flags.tz };
10469
+ }
10470
+ if (flags.tz !== void 0) throw new Error("--tz only applies with --cron");
10471
+ if (flags.every !== void 0) return { kind: "interval", everyMs: parseDuration(flags.every), anchorAt: now };
10472
+ if (flags.at !== void 0) {
10473
+ const at = Date.parse(flags.at);
10474
+ if (!Number.isFinite(at)) throw new Error(`--at must be an ISO 8601 timestamp, got: ${flags.at}`);
10475
+ return { kind: "once", at };
10476
+ }
10477
+ return { kind: "manual" };
10478
+ }
10479
+ function describeTrigger(trigger) {
10480
+ switch (trigger.kind) {
10481
+ case "cron":
10482
+ return `cron ${trigger.expr} (${trigger.tz})`;
10483
+ case "interval":
10484
+ return `every ${formatDuration(trigger.everyMs)}`;
10485
+ case "once":
10486
+ return `once at ${new Date(trigger.at).toISOString()}`;
10487
+ default:
10488
+ return "manual";
10489
+ }
10490
+ }
10491
+ function describeAction(action) {
10492
+ if (action.kind === "spawn") return `spawn ${action.agent} in ${action.directory}${action.sticky ? ` (sticky ${action.sticky.key})` : ""}`;
10493
+ if (action.kind === "send") return `send to session ${action.sessionId}`;
10494
+ return `script ${action.command.join(" ")}`;
10495
+ }
10496
+
10497
+ const RECEIPT_RETENTION_MS = 7 * 864e5;
10498
+ const RECEIPT_MAX_ENTRIES = 2e3;
10499
+ function receiptsPath(home) {
10500
+ return path.join(home, "automation-receipts.json");
10501
+ }
10502
+ function readReceipts(home) {
10503
+ let raw;
10504
+ try {
10505
+ raw = node_fs.readFileSync(receiptsPath(home), "utf8");
10506
+ } catch (error) {
10507
+ if (error.code === "ENOENT") return {};
10508
+ throw error;
10509
+ }
10510
+ const parsed = JSON.parse(raw);
10511
+ if (!parsed || typeof parsed !== "object" || typeof parsed.receipts !== "object") throw new Error("Invalid automation receipts file");
10512
+ return parsed.receipts;
10513
+ }
10514
+ function writeReceipt(home, receipt, now = Date.now()) {
10515
+ if (!/^[a-zA-Z0-9_-]{1,128}$/.test(receipt.runId)) throw new Error("Invalid automation run id");
10516
+ const all = readReceipts(home);
10517
+ const next = { ...receipt, updatedAt: now };
10518
+ all[receipt.runId] = next;
10519
+ const entries = Object.values(all).sort((a, b) => b.updatedAt - a.updatedAt);
10520
+ const kept = {};
10521
+ let finished = 0;
10522
+ for (const entry of entries) {
10523
+ if (entry.phase === "finished") {
10524
+ if (now - entry.updatedAt > RECEIPT_RETENTION_MS || finished >= RECEIPT_MAX_ENTRIES) continue;
10525
+ finished++;
10526
+ }
10527
+ kept[entry.runId] = entry;
10528
+ }
10529
+ const target = receiptsPath(home);
10530
+ persistence.ensurePrivateDirectorySync(path.dirname(target));
10531
+ const tmp = `${target}.${process.pid}.tmp`;
10532
+ persistence.writePrivateFileSync(tmp, JSON.stringify({ version: 1, receipts: kept }));
10533
+ const file = node_fs.openSync(tmp, "r");
10534
+ try {
10535
+ node_fs.fsyncSync(file);
10536
+ } finally {
10537
+ node_fs.closeSync(file);
10538
+ }
10539
+ node_fs.renameSync(tmp, target);
10540
+ if (process.platform !== "win32") {
10541
+ const directory = node_fs.openSync(path.dirname(target), "r");
10542
+ try {
10543
+ node_fs.fsyncSync(directory);
10544
+ } finally {
10545
+ node_fs.closeSync(directory);
10546
+ }
10547
+ }
10548
+ return next;
10549
+ }
10550
+
10551
+ const SCRIPT_KILL_GRACE_MS = 1e4;
10552
+ const SCRIPT_DRAIN_MS = 2e3;
10553
+ class TailBuffer {
10554
+ constructor(maxChars) {
10555
+ this.maxChars = maxChars;
10556
+ }
10557
+ text = "";
10558
+ push(chunk) {
10559
+ this.text += chunk;
10560
+ if (this.text.length > this.maxChars * 2) this.text = this.text.slice(this.text.length - this.maxChars);
10561
+ }
10562
+ value() {
10563
+ return this.text.length > this.maxChars ? this.text.slice(this.text.length - this.maxChars) : this.text;
10564
+ }
10565
+ }
10566
+ function killScriptTree(child, signal) {
10567
+ if (child.exitCode !== null || child.signalCode !== null || !child.pid) return;
10568
+ try {
10569
+ if (process.platform !== "win32") process.kill(-child.pid, signal);
10570
+ else child.kill(signal);
10571
+ } catch {
10572
+ try {
10573
+ child.kill(signal);
10574
+ } catch {
10575
+ }
10576
+ }
10577
+ }
10578
+ function terminateScriptTree(child) {
10579
+ killScriptTree(child, "SIGTERM");
10580
+ const killer = setTimeout(() => killScriptTree(child, "SIGKILL"), SCRIPT_KILL_GRACE_MS);
10581
+ killer.unref();
10582
+ child.once("exit", () => clearTimeout(killer));
10583
+ }
10584
+ function runScript(options) {
10585
+ return new Promise((resolve) => {
10586
+ const tail = new TailBuffer(persistence.AUTOMATION_SUMMARY_MAX_CHARS);
10587
+ let timedOut = false;
10588
+ let settled = false;
10589
+ let child;
10590
+ const timers = [];
10591
+ const finish = (result) => {
10592
+ if (settled) return;
10593
+ settled = true;
10594
+ for (const t of timers) clearTimeout(t);
10595
+ resolve(result);
10596
+ };
10597
+ const timer = setTimeout(() => {
10598
+ timedOut = true;
10599
+ terminateScriptTree(child);
10600
+ }, options.timeoutMs);
10601
+ timer.unref();
10602
+ timers.push(timer);
10603
+ try {
10604
+ child = node_child_process.spawn(options.command[0], options.command.slice(1), {
10605
+ cwd: options.cwd,
10606
+ shell: false,
10607
+ windowsHide: true,
10608
+ stdio: ["ignore", "pipe", "pipe"],
10609
+ detached: process.platform !== "win32",
10610
+ env: { ...options.baseEnv ?? process.env, ...options.env ?? {} }
10611
+ });
10612
+ } catch (error) {
10613
+ finish({ exitCode: null, signal: null, timedOut: false, output: "", error: error instanceof Error ? error.message : String(error) });
10614
+ return;
10615
+ }
10616
+ options.onStart?.(child);
10617
+ child.stdout?.setEncoding("utf8").on("data", (chunk) => tail.push(chunk));
10618
+ child.stderr?.setEncoding("utf8").on("data", (chunk) => tail.push(chunk));
10619
+ child.on("error", (error) => finish({ exitCode: null, signal: null, timedOut, output: tail.value(), error: error.message }));
10620
+ child.on("close", (code, signal) => finish({ exitCode: code, signal, timedOut, output: tail.value() }));
10621
+ child.on("exit", (code, signal) => {
10622
+ const drain = setTimeout(() => {
10623
+ killScriptTree(child, "SIGKILL");
10624
+ finish({ exitCode: code, signal, timedOut, output: tail.value() });
10625
+ }, SCRIPT_DRAIN_MS);
10626
+ drain.unref();
10627
+ timers.push(drain);
10628
+ });
10629
+ });
10630
+ }
10631
+
10632
+ const AUTOMATION_TICK_MS = 5e3;
10633
+ const AUTOMATION_LEASE_RENEW_MS = 3e4;
10634
+ const AUTOMATION_LOG_READ_MS = 3e4;
10635
+ const AUTOMATION_UNAVAILABLE_LOG_MS = 10 * 6e4;
10636
+ const AUTOMATION_MAX_CONCURRENT = persistence.AUTOMATION_CLAIM_MAX_RUNS;
10637
+ const AUTOMATION_SPAWNED_BY = "automation";
10638
+ function automationPromptHeader(name, runId) {
10639
+ return `[Very Happy automation "${name}" run ${runId}; finish with automation_report (runId ${runId}) or \`very-happy auto report --run ${runId}\`]`;
10640
+ }
10641
+ function automationRunAnchor(runId) {
10642
+ const marker = ` run ${runId};`;
10643
+ return (body) => userPromptText(body)?.startsWith("[Very Happy automation ") === true && userPromptText(body).includes(marker);
10644
+ }
10645
+ async function defaultReadTurn(sessionId, runId) {
10646
+ const persisted = persistence.readPersistedSessions()[sessionId];
10647
+ if (!persisted) throw new Error(`No local key for session ${sessionId}`);
10648
+ return analyzeLatestTurn(await readSessionLog(sessionId, persisted, TURN_WINDOW), { anchor: automationRunAnchor(runId) });
10649
+ }
10650
+ async function defaultSend(sessionId, text, meta) {
10651
+ const persisted = await waitForSessionKey(sessionId, 15e3);
10652
+ await sendUserMessage(sessionId, persisted, text, "automation", { ...meta, sentFrom: "automation" });
10653
+ }
10654
+ function expandHome(path$1) {
10655
+ if (path$1 === "~") return os.homedir();
10656
+ if (path$1.startsWith("~/")) return path.join(os.homedir(), path$1.slice(2));
10657
+ return path$1;
10658
+ }
10659
+ function isSettledByServer(error) {
10660
+ if (!(error instanceof agentGuidance.AutomationsRequestError)) return false;
10661
+ if (error.status === 409 && (error.code === "run_finished" || error.code === "claim_mismatch")) return true;
10662
+ return error.status === 404 && error.code === "run_not_found";
10663
+ }
10664
+ function scriptAlive(item) {
10665
+ return !!item.child && item.child.exitCode === null && item.child.signalCode === null;
10666
+ }
10667
+ function createAutomationRunner(deps) {
10668
+ const home = deps.home ?? persistence.configuration.happyHomeDir;
10669
+ const client = deps.client ?? agentGuidance.createAutomationsClient({ serverUrl: deps.serverUrl ?? persistence.configuration.serverUrl, token: deps.token, client: "daemon-auto" });
10670
+ const runScript$1 = deps.runScript ?? runScript;
10671
+ const readTurn = deps.readTurn ?? defaultReadTurn;
10672
+ const send = deps.send ?? defaultSend;
10673
+ const prepareWorktree = deps.prepareWorktree ?? prepareTeamWorktree;
10674
+ const now = deps.now ?? (() => Date.now());
10675
+ const tracked = /* @__PURE__ */ new Map();
10676
+ let running = false;
10677
+ let renewing = false;
10678
+ let closed = false;
10679
+ let disabledUntil = 0;
10680
+ let lastUnavailableLogAt = -Infinity;
10681
+ let recovered = false;
10682
+ function persist(receipt) {
10683
+ writeReceipt(home, receipt, now());
10684
+ }
10685
+ async function finish(item, report2) {
10686
+ try {
10687
+ await client.report(item.runId, report2);
10688
+ } catch (error) {
10689
+ if (!isSettledByServer(error)) {
10690
+ item.pendingFinal = report2;
10691
+ deps.log(`Automation run ${item.runId} final report deferred`);
10692
+ return;
10693
+ }
10694
+ }
10695
+ persist({ runId: item.runId, automationId: item.automationId, kind: item.kind, phase: "finished", claimId: item.claimId, sessionId: item.sessionId });
10696
+ tracked.delete(item.runId);
10697
+ }
10698
+ function track(claimed, kind, sessionId, child) {
10699
+ const at = now();
10700
+ const item = { runId: claimed.run.id, automationId: claimed.automation.id, claimId: claimed.run.claimId, kind, sessionId, child, sawTurn: false, startedAt: at, lastLeaseAt: at, nextLogReadAt: at + AUTOMATION_LOG_READ_MS, attentionReported: false, exited: false };
10701
+ tracked.set(item.runId, item);
10702
+ return item;
10703
+ }
10704
+ async function failNow(claimed, kind, error, attentionReason, sessionId) {
10705
+ const item = track(claimed, kind, sessionId);
10706
+ await finish(item, { claimId: item.claimId, status: "failed", error: clipText(error, persistence.AUTOMATION_SUMMARY_MAX_CHARS), ...sessionId ? { sessionId } : {}, ...attentionReason ? { needsAttention: true, attentionReason } : {} });
10707
+ }
10708
+ async function reportRunning(item, sessionId, stickyKey) {
10709
+ const body = { claimId: item.claimId, status: "running", ...sessionId ? { sessionId } : {}, ...stickyKey ? { stickyKey } : {} };
10710
+ try {
10711
+ await client.report(item.runId, body);
10712
+ } catch (error) {
10713
+ if (isSettledByServer(error)) {
10714
+ settleExternally(item);
10715
+ return;
10716
+ }
10717
+ if (sessionId) item.pendingRunning = { sessionId, ...stickyKey ? { stickyKey } : {} };
10718
+ deps.log(`Automation run ${item.runId} running report deferred`);
10719
+ }
10720
+ }
10721
+ async function execute(claimed) {
10722
+ const { run, automation } = claimed;
10723
+ const existing = readReceipts(home)[run.id];
10724
+ if (existing) {
10725
+ if (existing.phase === "finished" || tracked.has(run.id)) return;
10726
+ if (existing.phase === "starting") {
10727
+ await failNow(claimed, existing.kind, "Local execution outcome unknown after restart; verify before re-running", "unknown_outcome");
10728
+ return;
10729
+ }
10730
+ if (existing.kind === "script") {
10731
+ await failNow(claimed, "script", "Daemon restarted while the script was running", "daemon_restarted");
10732
+ return;
10733
+ }
10734
+ track(claimed, existing.kind, existing.sessionId).sawTurn = true;
10735
+ return;
10736
+ }
10737
+ const context = { payload: run.payload, runId: run.id, automationName: automation.name, ...automation.trigger.kind === "cron" ? { tz: automation.trigger.tz } : {}, now: now() };
10738
+ const action = renderAction(automation.action, context);
10739
+ const env = { VH_AUTOMATION_RUN_ID: run.id, VH_AUTOMATION_NAME: automation.name };
10740
+ const header = automationPromptHeader(automation.name, run.id);
10741
+ if (action.kind === "script") {
10742
+ const timeoutMs = action.timeoutMs ?? automation.maxRuntimeMs;
10743
+ persist({ runId: run.id, automationId: automation.id, kind: "script", phase: "starting", claimId: run.claimId });
10744
+ const item2 = track(claimed, "script");
10745
+ const finished = runScript$1({
10746
+ command: action.command,
10747
+ cwd: action.cwd ? expandHome(action.cwd) : void 0,
10748
+ timeoutMs,
10749
+ env: { ...action.env ?? {}, ...env, ...run.payload ? { VH_AUTOMATION_PAYLOAD: run.payload } : {} },
10750
+ onStart: (child) => {
10751
+ item2.child = child;
10752
+ }
10753
+ });
10754
+ persist({ runId: run.id, automationId: automation.id, kind: "script", phase: "running", claimId: run.claimId });
10755
+ await reportRunning(item2);
10756
+ void finished.then(async (result) => {
10757
+ if (!tracked.has(run.id)) return;
10758
+ const ok = result.exitCode === 0 && !result.timedOut && !result.error;
10759
+ const error = result.error ? `Launch failed: ${result.error}` : result.timedOut ? `Timed out after ${timeoutMs}ms` : result.exitCode !== 0 ? `Exit ${result.exitCode ?? `signal ${result.signal}`}` : void 0;
10760
+ await finish(item2, { claimId: run.claimId, status: ok ? "done" : "failed", summary: clipText(result.output, persistence.AUTOMATION_SUMMARY_MAX_CHARS, "tail"), exitCode: result.exitCode, ...error ? { error } : {} });
10761
+ });
10762
+ return;
10763
+ }
10764
+ if (action.kind === "send") {
10765
+ if (!deps.live(action.sessionId)) {
10766
+ await failNow(claimed, "send", `Session ${action.sessionId} is not running on this machine`, "session_not_live", action.sessionId);
10767
+ return;
10768
+ }
10769
+ persist({ runId: run.id, automationId: automation.id, kind: "send", phase: "running", claimId: run.claimId, sessionId: action.sessionId });
10770
+ try {
10771
+ await send(action.sessionId, `${header}
10772
+ ${action.prompt}`, { localId: `automation-${run.id}` });
10773
+ } catch (error) {
10774
+ await failNow(claimed, "send", `Message delivery failed: ${error instanceof Error ? error.message : String(error)}`, "send_failed", action.sessionId);
10775
+ return;
10776
+ }
10777
+ await reportRunning(track(claimed, "send", action.sessionId), action.sessionId);
10778
+ return;
10779
+ }
10780
+ const agent = action.agent;
10781
+ if (!isSpawnAgent(agent)) {
10782
+ await failNow(claimed, "spawn", `Unknown agent "${agent}"`, "invalid_action");
10783
+ return;
10784
+ }
10785
+ if (action.permissionMode && sanitizeSpawnPermissionMode(action.permissionMode) === null) {
10786
+ await failNow(claimed, "spawn", `Invalid permission mode "${action.permissionMode}"`, "invalid_action");
10787
+ return;
10788
+ }
10789
+ const permissionMode = resolveSpawnPermissionMode(agent, action.permissionMode ?? void 0);
10790
+ const stickyKey = action.sticky ? renderTemplate(action.sticky.key, context) : void 0;
10791
+ if (stickyKey) {
10792
+ const bound = claimed.stickies.find((sticky) => sticky.key === stickyKey);
10793
+ if (bound && deps.live(bound.sessionId)) {
10794
+ persist({ runId: run.id, automationId: automation.id, kind: "send", phase: "running", claimId: run.claimId, sessionId: bound.sessionId, stickyKey });
10795
+ try {
10796
+ await send(bound.sessionId, `${header}
10797
+ ${action.prompt}`, { localId: `automation-${run.id}` });
10798
+ } catch (error) {
10799
+ await failNow(claimed, "send", `Sticky session delivery failed: ${error instanceof Error ? error.message : String(error)}`, "send_failed", bound.sessionId);
10800
+ return;
10801
+ }
10802
+ await reportRunning(track(claimed, "send", bound.sessionId), bound.sessionId, stickyKey);
10803
+ return;
10804
+ }
10805
+ if (bound) await client.deleteSticky(automation.id, stickyKey).catch(() => deps.log(`Automation ${automation.name}: stale sticky ${stickyKey} not cleared`));
10806
+ }
10807
+ let directory = expandHome(action.directory);
10808
+ if (!path.isAbsolute(directory)) {
10809
+ await failNow(claimed, "spawn", `Directory must be absolute: ${action.directory}`, "invalid_action");
10810
+ return;
10811
+ }
10812
+ if (action.worktree) {
10813
+ try {
10814
+ directory = (await prepareWorktree(home, directory, `auto-${run.id}`)).directory;
10815
+ } catch (error) {
10816
+ await failNow(claimed, "spawn", `Worktree preparation failed: ${error instanceof Error ? error.message : String(error)}`, "worktree_failed");
10817
+ return;
10818
+ }
10819
+ }
10820
+ persist({ runId: run.id, automationId: automation.id, kind: "spawn", phase: "starting", claimId: run.claimId, stickyKey, ...action.worktree ? { directory } : {} });
10821
+ const spawned = await deps.spawn({ directory, agent, permissionMode, spawnedBy: AUTOMATION_SPAWNED_BY, environmentVariables: env });
10822
+ if (spawned.type !== "success") {
10823
+ await failNow(claimed, "spawn", spawned.type === "error" ? spawned.errorMessage : "Directory approval required", "spawn_failed");
10824
+ return;
10825
+ }
10826
+ persist({ runId: run.id, automationId: automation.id, kind: "spawn", phase: "running", claimId: run.claimId, sessionId: spawned.sessionId, stickyKey, ...action.worktree ? { directory } : {} });
10827
+ const item = track(claimed, "spawn", spawned.sessionId);
10828
+ try {
10829
+ const capabilities = persistence.readPersistedSessions()[spawned.sessionId]?.metadata?.capabilities ?? null;
10830
+ const meta = resolveFirstMessageMeta({ agent, permissionMode, explicitModel: action.model ?? void 0, capabilities });
10831
+ await send(spawned.sessionId, `${header}
10832
+ ${action.prompt}`, { localId: `automation-${run.id}`, ...meta });
10833
+ } catch (error) {
10834
+ await finish(item, { claimId: run.claimId, status: "failed", sessionId: spawned.sessionId, error: `First message failed: ${error instanceof Error ? error.message : String(error)}`, needsAttention: true, attentionReason: "first_message_failed" });
10835
+ return;
10836
+ }
10837
+ await reportRunning(item, spawned.sessionId, stickyKey);
10838
+ }
10839
+ async function monitor() {
10840
+ for (const item of [...tracked.values()]) {
10841
+ if (closed) return;
10842
+ if (item.pendingFinal) {
10843
+ await finish(item, item.pendingFinal);
10844
+ continue;
10845
+ }
10846
+ if (item.kind === "script") continue;
10847
+ const sessionId = item.sessionId;
10848
+ const active = deps.turnActive(sessionId);
10849
+ if (active) item.sawTurn = true;
10850
+ const alive = deps.live(sessionId) && !item.exited;
10851
+ if (!alive) {
10852
+ const turn2 = await readTurn(sessionId, item.runId).catch(() => null);
10853
+ if (turn2?.ended && turn2.status !== "failed" && turn2.status !== "cancelled") await finish(item, { claimId: item.claimId, status: "done", sessionId, summary: clipText(turn2.answer, persistence.AUTOMATION_SUMMARY_MAX_CHARS) });
10854
+ else await finish(item, { claimId: item.claimId, status: "failed", sessionId, error: turn2?.error ?? "Session exited before the turn ended", needsAttention: true, attentionReason: "session_exited" });
10855
+ continue;
10856
+ }
10857
+ const edgeFell = item.sawTurn && !active;
10858
+ if (!edgeFell && now() < item.nextLogReadAt) continue;
10859
+ item.nextLogReadAt = now() + AUTOMATION_LOG_READ_MS;
10860
+ if (edgeFell) item.sawTurn = false;
10861
+ const turn = await readTurn(sessionId, item.runId).catch(() => null);
10862
+ if (!turn?.ended) continue;
10863
+ if (turn.status === "failed" || turn.status === "cancelled") await finish(item, { claimId: item.claimId, status: "failed", sessionId, error: turn.error ?? `Turn ${turn.status}`, needsAttention: true, attentionReason: `turn_${turn.status}` });
10864
+ else await finish(item, { claimId: item.claimId, status: "done", sessionId, summary: clipText(turn.answer, persistence.AUTOMATION_SUMMARY_MAX_CHARS) });
10865
+ }
10866
+ }
10867
+ async function renewAll() {
10868
+ if (renewing || closed) return;
10869
+ renewing = true;
10870
+ try {
10871
+ const at = now();
10872
+ for (const item of [...tracked.values()]) {
10873
+ if (closed) return;
10874
+ if (at - item.lastLeaseAt < AUTOMATION_LEASE_RENEW_MS) continue;
10875
+ item.lastLeaseAt = at;
10876
+ const body = { claimId: item.claimId, leaseMs: persistence.AUTOMATION_DEFAULT_LEASE_MS, ...item.pendingRunning ? { status: "running", ...item.pendingRunning } : {} };
10877
+ try {
10878
+ const run = await client.report(item.runId, body);
10879
+ item.pendingRunning = void 0;
10880
+ if (persistence.isAutomationRunTerminal(run.status)) settleExternally(item);
10881
+ } catch (error) {
10882
+ if (isSettledByServer(error)) settleExternally(item);
10883
+ else deps.log(`Automation run ${item.runId} lease renewal failed`);
10884
+ }
10885
+ }
10886
+ } finally {
10887
+ renewing = false;
10888
+ }
10889
+ }
10890
+ function settleExternally(item) {
10891
+ if (item.child && scriptAlive(item)) terminateScriptTree(item.child);
10892
+ persist({ runId: item.runId, automationId: item.automationId, kind: item.kind, phase: "finished", claimId: item.claimId, sessionId: item.sessionId });
10893
+ tracked.delete(item.runId);
10894
+ }
10895
+ async function recover() {
10896
+ recovered = true;
10897
+ let receipts;
10898
+ try {
10899
+ receipts = readReceipts(home);
10900
+ } catch {
10901
+ deps.log("Automation receipts unreadable; refusing to execute until repaired");
10902
+ closed = true;
10903
+ return;
10904
+ }
10905
+ for (const receipt of Object.values(receipts)) {
10906
+ if (receipt.phase === "finished") continue;
10907
+ const item = track({ run: { id: receipt.runId, claimId: receipt.claimId }, automation: { id: receipt.automationId } }, receipt.kind, receipt.sessionId);
10908
+ item.sawTurn = true;
10909
+ item.lastLeaseAt = 0;
10910
+ if (receipt.phase === "starting") await finish(item, { claimId: receipt.claimId, status: "failed", error: "Daemon restarted before the launch outcome was known", needsAttention: true, attentionReason: "unknown_outcome" });
10911
+ else if (receipt.kind === "script") await finish(item, { claimId: receipt.claimId, status: "failed", error: "Daemon restarted while the script was running", needsAttention: true, attentionReason: "daemon_restarted" });
10912
+ }
10913
+ }
10914
+ async function tick() {
10915
+ if (running || closed) return;
10916
+ running = true;
10917
+ try {
10918
+ if (!recovered) await recover();
10919
+ if (closed) return;
10920
+ await monitor();
10921
+ const capacity = AUTOMATION_MAX_CONCURRENT - tracked.size;
10922
+ if (now() >= disabledUntil && capacity > 0) {
10923
+ try {
10924
+ const claimed = await client.claim({ machineId: deps.machineId, limit: capacity, leaseMs: persistence.AUTOMATION_DEFAULT_LEASE_MS });
10925
+ await Promise.all(claimed.runs.map((entry) => closed ? Promise.resolve() : execute(entry).catch((error) => deps.log(`Automation run ${entry.run.id} failed to start: ${error instanceof Error ? error.message : String(error)}`))));
10926
+ } catch (error) {
10927
+ if (agentGuidance.isAutomationsUnavailable(error)) {
10928
+ disabledUntil = now() + 6e4;
10929
+ if (now() - lastUnavailableLogAt >= AUTOMATION_UNAVAILABLE_LOG_MS) {
10930
+ lastUnavailableLogAt = now();
10931
+ deps.log("Automations unavailable on this server (404); claim loop idle");
10932
+ }
10933
+ } else {
10934
+ disabledUntil = now() + 1e4;
10935
+ deps.log(`Automation claim failed${error instanceof agentGuidance.AutomationsRequestError ? ` (${error.status})` : ""}`);
10936
+ }
10937
+ }
10938
+ }
10939
+ } finally {
10940
+ running = false;
10941
+ }
10942
+ }
10943
+ function report(sessionId, event) {
10944
+ for (const item of tracked.values()) {
10945
+ if (item.sessionId !== sessionId) continue;
10946
+ if (event === "exited") item.exited = true;
10947
+ if (event === "blocked" && !item.attentionReported) {
10948
+ item.attentionReported = true;
10949
+ void client.report(item.runId, { claimId: item.claimId, needsAttention: true, attentionReason: "needs_input" }).catch(() => deps.log(`Automation run ${item.runId} attention report failed`));
10950
+ }
10951
+ }
10952
+ }
10953
+ const tickMs = deps.tickMs ?? AUTOMATION_TICK_MS;
10954
+ const timer = setInterval(() => {
10955
+ void tick();
10956
+ }, tickMs);
10957
+ timer.unref();
10958
+ const leaseTimer = setInterval(() => {
10959
+ void renewAll();
10960
+ }, tickMs);
10961
+ leaseTimer.unref();
10962
+ void tick();
10963
+ return {
10964
+ tick,
10965
+ renewAll,
10966
+ report,
10967
+ hasSession: (id) => [...tracked.values()].some((item) => item.sessionId === id),
10968
+ trackedRunIds: () => [...tracked.keys()],
10969
+ /** Executing a claim, or a script child is still alive — handover and auto-update wait for this. */
10970
+ get busy() {
10971
+ return running || [...tracked.values()].some(scriptAlive);
10972
+ },
10973
+ /**
10974
+ * Stop the loops. `killScripts` (real shutdown) terminates live script
10975
+ * trees; the next daemon's recovery reports them failed + attention from
10976
+ * the receipt, never re-running them. A handover waits on `busy` instead.
10977
+ */
10978
+ stop: (options = {}) => {
10979
+ closed = true;
10980
+ clearInterval(timer);
10981
+ clearInterval(leaseTimer);
10982
+ if (options.killScripts) {
10983
+ for (const item of tracked.values()) if (item.child && scriptAlive(item)) terminateScriptTree(item.child);
10984
+ }
10985
+ }
10986
+ };
10987
+ }
10988
+
10331
10989
  function daemonExitCode(source) {
10332
10990
  return source === "exception" ? 1 : 0;
10333
10991
  }
@@ -10349,7 +11007,7 @@ const initialMachineMetadata = {
10349
11007
  happyCliVersion: persistence.packageJson.version,
10350
11008
  homeDir: os$1.homedir(),
10351
11009
  happyHomeDir: persistence.configuration.happyHomeDir,
10352
- happyLibDir: projectPath.projectPath(),
11010
+ happyLibDir: terminalTitleSuggest.projectPath(),
10353
11011
  cliAvailability: startupCliAvailability,
10354
11012
  resumeSupport: { ...index.detectResumeSupport(), rpcAvailable: true }
10355
11013
  };
@@ -10488,6 +11146,7 @@ async function startDaemon() {
10488
11146
  const turnActivity = new index.TurnActivityTracker();
10489
11147
  const sessionIdToFinishedSession = /* @__PURE__ */ new Map();
10490
11148
  let teamWorker;
11149
+ let automationRunner;
10491
11150
  const persisted = persistence.readPersistedSessions();
10492
11151
  const liveHappyProcesses = await findAllHappyProcesses();
10493
11152
  for (const [id, s] of Object.entries(persisted)) {
@@ -10806,7 +11465,7 @@ async function startDaemon() {
10806
11465
  const sessionDesc = tmuxSessionName || "current/most recent session";
10807
11466
  persistence.logger.debug(`[DAEMON RUN] Attempting to spawn session in tmux: ${sessionDesc}`);
10808
11467
  const tmux = getTmuxUtilities(tmuxSessionName);
10809
- const cliPath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
11468
+ const cliPath = path$1.join(terminalTitleSuggest.projectPath(), "dist", "index.mjs");
10810
11469
  const agent = options.agent ?? "claude";
10811
11470
  if (!isSpawnAgent(agent)) {
10812
11471
  return {
@@ -11342,6 +12001,7 @@ async function startDaemon() {
11342
12001
  const onChildExited = (pid) => {
11343
12002
  const session = pidToTrackedSession.get(pid);
11344
12003
  if (session?.happySessionId && (session.spawnedBy === "teams" || teamWorker?.hasSession(session.happySessionId))) teamWorker?.report(session.happySessionId, "exited");
12004
+ if (session?.happySessionId && automationRunner?.hasSession(session.happySessionId)) automationRunner.report(session.happySessionId, "exited");
11345
12005
  if (session?.happySessionId) turnActivity.forget(session.happySessionId);
11346
12006
  if (session?.happySessionId && session.encryption) {
11347
12007
  sessionIdToFinishedSession.set(session.happySessionId, session);
@@ -11361,6 +12021,7 @@ async function startDaemon() {
11361
12021
  }
11362
12022
  };
11363
12023
  const onSessionStateEvent = (sessionId, event, spawnedByFromSession) => {
12024
+ if (automationRunner?.hasSession(sessionId)) automationRunner.report(sessionId, event === "completed" ? "idle" : "blocked");
11364
12025
  if (spawnedByFromSession === "teams" || teamWorker?.hasSession(sessionId) || [...pidToTrackedSession.values()].some((s) => s.happySessionId === sessionId && s.spawnedBy === "teams")) {
11365
12026
  teamWorker?.report(sessionId, event === "completed" ? "idle" : "blocked");
11366
12027
  return;
@@ -11457,7 +12118,7 @@ async function startDaemon() {
11457
12118
  };
11458
12119
  persistence.writeDaemonState(fileState);
11459
12120
  persistence.logger.debug("[DAEMON RUN] Daemon state written");
11460
- const bundlePath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
12121
+ const bundlePath = path$1.join(terminalTitleSuggest.projectPath(), "dist", "index.mjs");
11461
12122
  let initialBundleMtimeMs = 0;
11462
12123
  try {
11463
12124
  initialBundleMtimeMs = fs$1.statSync(bundlePath).mtimeMs;
@@ -11513,7 +12174,7 @@ async function startDaemon() {
11513
12174
  });
11514
12175
  const claudeAuthService = new ClaudeAuthService({
11515
12176
  happyHomeDir: persistence.configuration.happyHomeDir,
11516
- happyLibDir: projectPath.projectPath(),
12177
+ happyLibDir: terminalTitleSuggest.projectPath(),
11517
12178
  credentialSource: daemonClaudeCredentials.source,
11518
12179
  getCredentialStore: () => claudeCredentialStoreSetting,
11519
12180
  setCredentialStore: async (store) => {
@@ -11599,7 +12260,7 @@ async function startDaemon() {
11599
12260
  } catch {
11600
12261
  }
11601
12262
  }
11602
- if (bundleReplaced && !teamWorker?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
12263
+ if (bundleReplaced && !teamWorker?.busy && !automationRunner?.busy && !updateController.isRunning() && !turnActivity.hasActiveTurn()) {
11603
12264
  const preflight = await preflightNewBundle(bundlePath);
11604
12265
  if (preflight.action === "hold") {
11605
12266
  if (lastHandoverHold !== preflight.reason) {
@@ -11609,10 +12270,11 @@ async function startDaemon() {
11609
12270
  updateController.holdHandover(preflight.reason);
11610
12271
  return;
11611
12272
  }
11612
- if (teamWorker?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
12273
+ if (teamWorker?.busy || automationRunner?.busy || updateController.isRunning() || turnActivity.hasActiveTurn()) return;
11613
12274
  lastHandoverHold = null;
11614
12275
  persistence.logger.debug("[DAEMON RUN] Daemon bundle replaced on disk and verified, handing off to new daemon");
11615
12276
  teamWorker?.stop();
12277
+ automationRunner?.stop();
11616
12278
  clearInterval(restartOnStaleVersionAndHeartbeat);
11617
12279
  clearInterval(cliUpdateInterval);
11618
12280
  stopAgentVersionChecks();
@@ -11664,6 +12326,14 @@ async function startDaemon() {
11664
12326
  live: (id) => [...pidToTrackedSession.values()].some((s) => s.happySessionId === id && isPidAlive(s.pid)),
11665
12327
  log: (message) => persistence.logger.debug(`[DAEMON RUN] ${message}`)
11666
12328
  });
12329
+ automationRunner = createAutomationRunner({
12330
+ machineId,
12331
+ token: credentials.token,
12332
+ spawn: spawnSession,
12333
+ live: (id) => [...pidToTrackedSession.values()].some((s) => s.happySessionId === id && isPidAlive(s.pid)),
12334
+ turnActive: (id) => turnActivity.activeSessions().includes(id),
12335
+ log: (message) => persistence.logger.debug(`[DAEMON RUN] ${message}`)
12336
+ });
11667
12337
  void apiMachine.updateMachineMetadata((current) => ({
11668
12338
  ...current ?? initialMachineMetadata,
11669
12339
  teamsVersion: 1,
@@ -11672,6 +12342,7 @@ async function startDaemon() {
11672
12342
  const cleanupAndShutdown = async (source, errorMessage) => {
11673
12343
  persistence.logger.debug(`[DAEMON RUN] Starting proper cleanup (source: ${source}, errorMessage: ${errorMessage})...`);
11674
12344
  teamWorker?.stop();
12345
+ automationRunner?.stop({ killScripts: true });
11675
12346
  if (restartOnStaleVersionAndHeartbeat) {
11676
12347
  clearInterval(restartOnStaleVersionAndHeartbeat);
11677
12348
  persistence.logger.debug("[DAEMON RUN] Health check interval cleared");
@@ -12176,6 +12847,7 @@ function createMcpServer(handlers, options, sessionId) {
12176
12847
  version: "1.0.0"
12177
12848
  });
12178
12849
  agentGuidance.registerTeamsTools(mcp, sessionId);
12850
+ agentGuidance.registerAutomationTools(mcp);
12179
12851
  mcp.registerTool("change_title", {
12180
12852
  description: "Change the title of the current chat session",
12181
12853
  title: "Change Chat Title",
@@ -12370,7 +13042,7 @@ async function startHappyServer(client, options) {
12370
13042
  return;
12371
13043
  }
12372
13044
  }
12373
- const usage = index.ContextUsageSchema.parse({ ...JSON.parse(body), updatedAt: Date.now() });
13045
+ const usage = persistence.ContextUsageSchema.parse({ ...JSON.parse(body), updatedAt: Date.now() });
12374
13046
  options.onContextUsage(usage);
12375
13047
  res.writeHead(204).end();
12376
13048
  } catch {
@@ -12418,6 +13090,7 @@ async function startHappyServer(client, options) {
12418
13090
  agentGuidance.PREVIEW_TOOL_NAME,
12419
13091
  agentGuidance.REPORT_PROGRESS_TOOL_NAME,
12420
13092
  ...agentGuidance.TEAM_TOOL_NAMES,
13093
+ ...agentGuidance.AUTOMATION_TOOL_NAMES,
12421
13094
  ...options?.assistant && !process.env.VH_TEAM_SCOPE_FILE ? ASSISTANT_TOOL_NAMES : []
12422
13095
  ],
12423
13096
  stop: () => {
@@ -12517,7 +13190,7 @@ function generateHookSettingsFile(port) {
12517
13190
  node_fs.mkdirSync(hooksDir, { recursive: true });
12518
13191
  const filename = `session-hook-${process.pid}.json`;
12519
13192
  const filepath = path.join(hooksDir, filename);
12520
- const forwarderScript = path.resolve(projectPath.projectPath(), "scripts", "session_hook_forwarder.cjs");
13193
+ const forwarderScript = path.resolve(terminalTitleSuggest.projectPath(), "scripts", "session_hook_forwarder.cjs");
12521
13194
  const hookCommand = `node "${forwarderScript}" ${port}`;
12522
13195
  const settings = {
12523
13196
  hooks: {
@@ -12754,7 +13427,7 @@ function registerSideQuestionHandler(rpc, deps) {
12754
13427
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12755
13428
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12756
13429
  const fork = deps.run ?? (async (input) => {
12757
- const { query } = await Promise.resolve().then(function () { return require('./index-vyRqLjDv.cjs'); });
13430
+ const { query } = await Promise.resolve().then(function () { return require('./index-D49WvbDT.cjs'); });
12758
13431
  return runSideQuestion(query, input);
12759
13432
  });
12760
13433
  const run = async (input, live) => {
@@ -12873,8 +13546,8 @@ const MAX_TITLE_CHARS = 60;
12873
13546
  const GENERATION_TIMEOUT_MS = 3e4;
12874
13547
  function resolveClaudeBinary() {
12875
13548
  try {
12876
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href)));
12877
- const utilsPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_version_utils.cjs"));
13549
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href)));
13550
+ const utilsPath = path.resolve(path.join(terminalTitleSuggest.projectPath(), "scripts", "claude_version_utils.cjs"));
12878
13551
  const { getClaudeCliPath } = require$1(utilsPath);
12879
13552
  const path$1 = getClaudeCliPath();
12880
13553
  if (typeof path$1 === "string" && path$1.length > 0) return path$1;
@@ -12931,7 +13604,12 @@ function runClaudeOneShot(binary, prompt) {
12931
13604
  try {
12932
13605
  child = node_child_process.spawn(binary, ["-p", "--model", "haiku", prompt], {
12933
13606
  stdio: ["ignore", "pipe", "ignore"],
12934
- env: process.env
13607
+ // HAPPY_MANAGED=1: this claude is run BY happy. The user-wide
13608
+ // terminal-mirror SessionStart hook exits on it, so a one-shot
13609
+ // spawned from inside a vh web terminal (VH_TERMINAL_ID
13610
+ // inherited — B-500's pi terminal bridge, or `very-happy` typed
13611
+ // in a terminal) never binds a mirror session to itself.
13612
+ env: { ...process.env, HAPPY_MANAGED: "1" }
12935
13613
  });
12936
13614
  } catch (error) {
12937
13615
  persistence.logger.debug("[titleGenerator] spawn failed", { error: String(error) });
@@ -12966,6 +13644,18 @@ function runClaudeOneShot(binary, prompt) {
12966
13644
  });
12967
13645
  });
12968
13646
  }
13647
+ async function suggestTitleForPrompt(prompt) {
13648
+ const binary = resolveClaudeBinary();
13649
+ if (!binary) {
13650
+ persistence.logger.debug("[titleGenerator] no claude binary; skipping auto-title");
13651
+ return null;
13652
+ }
13653
+ const stdout = await runClaudeOneShot(binary, buildPrompt(prompt));
13654
+ if (stdout == null) return null;
13655
+ const title = sanitizeTitle(stdout);
13656
+ if (!title) persistence.logger.debug("[titleGenerator] empty/unusable title output; skipping");
13657
+ return title;
13658
+ }
12969
13659
  class TitleGenerator {
12970
13660
  constructor(session) {
12971
13661
  this.session = session;
@@ -12989,23 +13679,12 @@ class TitleGenerator {
12989
13679
  });
12990
13680
  }
12991
13681
  async generate(firstUserMessage) {
12992
- const binary = resolveClaudeBinary();
12993
- if (!binary) {
12994
- persistence.logger.debug("[titleGenerator] no claude binary; skipping auto-title");
12995
- return;
12996
- }
12997
13682
  if (this.session.getMetadata()?.summary?.text) {
12998
13683
  persistence.logger.debug("[titleGenerator] summary appeared before generation; skipping");
12999
13684
  return;
13000
13685
  }
13001
- const prompt = buildPrompt(firstUserMessage);
13002
- const stdout = await runClaudeOneShot(binary, prompt);
13003
- if (stdout == null) return;
13004
- const title = sanitizeTitle(stdout);
13005
- if (!title) {
13006
- persistence.logger.debug("[titleGenerator] empty/unusable title output; skipping");
13007
- return;
13008
- }
13686
+ const title = await suggestTitleForPrompt(firstUserMessage);
13687
+ if (!title) return;
13009
13688
  if (this.session.getMetadata()?.summary?.text) {
13010
13689
  persistence.logger.debug("[titleGenerator] summary set during generation; not overwriting");
13011
13690
  return;
@@ -13320,8 +13999,8 @@ async function runClaude(credentials, options = {}) {
13320
13999
  machineId,
13321
14000
  homeDir: os.homedir(),
13322
14001
  happyHomeDir: persistence.configuration.happyHomeDir,
13323
- happyLibDir: projectPath.projectPath(),
13324
- happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
14002
+ happyLibDir: terminalTitleSuggest.projectPath(),
14003
+ happyToolsDir: path.resolve(terminalTitleSuggest.projectPath(), "tools", "unpacked"),
13325
14004
  startedFromDaemon: options.startedBy === "daemon",
13326
14005
  hostPid: process.pid,
13327
14006
  startedBy: options.startedBy || "terminal",
@@ -15102,9 +15781,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
15102
15781
  };
15103
15782
  }
15104
15783
 
15105
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href)));
15784
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href)));
15106
15785
  const __dirname$1 = path.dirname(__filename$1);
15107
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CqtfRzug.cjs', document.baseURI).href)));
15786
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DHrwHxHf.cjs', document.baseURI).href)));
15108
15787
  const PRISMA_QUERY_ENGINE_FILES = {
15109
15788
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
15110
15789
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -15539,14 +16218,14 @@ function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagent
15539
16218
  if (!subagent || startedSubagents.has(subagent)) {
15540
16219
  return;
15541
16220
  }
15542
- envelopes.push(index.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
16221
+ envelopes.push(persistence.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
15543
16222
  startedSubagents.add(subagent);
15544
16223
  activeSubagents.add(subagent);
15545
16224
  }
15546
16225
  function emitSubagentStops(opts, startedSubagents, activeSubagents) {
15547
16226
  const envelopes = [];
15548
16227
  for (const subagent of activeSubagents) {
15549
- envelopes.push(index.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
16228
+ envelopes.push(persistence.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
15550
16229
  }
15551
16230
  activeSubagents.clear();
15552
16231
  startedSubagents.clear();
@@ -15642,7 +16321,7 @@ function turnStatus(turn) {
15642
16321
  function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
15643
16322
  const time = turnTimestampMs(turn);
15644
16323
  const opts = { turn: turn.id, time, codexItemId: item.id };
15645
- envelopes.push(index.createEnvelope("agent", {
16324
+ envelopes.push(persistence.createEnvelope("agent", {
15646
16325
  t: "tool-call-start",
15647
16326
  call: item.id,
15648
16327
  name,
@@ -15654,7 +16333,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
15654
16333
  id: `${item.id}:start`
15655
16334
  }));
15656
16335
  if (output && output.trim().length > 0) {
15657
- envelopes.push(index.createEnvelope("agent", {
16336
+ envelopes.push(persistence.createEnvelope("agent", {
15658
16337
  t: "text",
15659
16338
  text: output,
15660
16339
  thinking: true
@@ -15664,7 +16343,7 @@ function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output
15664
16343
  }));
15665
16344
  }
15666
16345
  if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
15667
- envelopes.push(index.createEnvelope("agent", {
16346
+ envelopes.push(persistence.createEnvelope("agent", {
15668
16347
  t: "tool-call-end",
15669
16348
  call: item.id
15670
16349
  }, {
@@ -15686,7 +16365,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
15686
16365
  agentsStates: item.agentsStates ?? {}
15687
16366
  };
15688
16367
  const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
15689
- const envelopes = [index.createEnvelope("agent", {
16368
+ const envelopes = [persistence.createEnvelope("agent", {
15690
16369
  t: "tool-call-start",
15691
16370
  call: item.id,
15692
16371
  name: "CodexCollaboration",
@@ -15695,7 +16374,7 @@ function mapCodexCollaborationItem(item, opts = {}, source = "history") {
15695
16374
  args
15696
16375
  }, { ...opts, id: startId })];
15697
16376
  if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
15698
- envelopes.push(index.createEnvelope("agent", {
16377
+ envelopes.push(persistence.createEnvelope("agent", {
15699
16378
  t: "tool-call-end",
15700
16379
  call: item.id,
15701
16380
  result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
@@ -15708,7 +16387,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
15708
16387
  for (const turn of thread.turns ?? []) {
15709
16388
  const startedAt = turnTimestampMs(turn);
15710
16389
  const completedAt = completedTimestampMs(turn);
15711
- envelopes.push(index.createEnvelope("agent", { t: "turn-start" }, {
16390
+ envelopes.push(persistence.createEnvelope("agent", { t: "turn-start" }, {
15712
16391
  id: `${turn.id}:start`,
15713
16392
  turn: turn.id,
15714
16393
  time: startedAt
@@ -15723,7 +16402,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
15723
16402
  case "userMessage": {
15724
16403
  const text = textFromInputItems(item.content);
15725
16404
  if (text) {
15726
- envelopes.push(index.createEnvelope("user", { t: "text", text }, {
16405
+ envelopes.push(persistence.createEnvelope("user", { t: "text", text }, {
15727
16406
  id: item.id,
15728
16407
  time: startedAt,
15729
16408
  codexItemId: item.id
@@ -15734,7 +16413,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
15734
16413
  case "agentMessage": {
15735
16414
  const text = typeof item.text === "string" ? item.text.trim() : "";
15736
16415
  if (text.length > 0) {
15737
- envelopes.push(index.createEnvelope("agent", { t: "text", text }, {
16416
+ envelopes.push(persistence.createEnvelope("agent", { t: "text", text }, {
15738
16417
  id: item.id,
15739
16418
  turn: turn.id,
15740
16419
  time: completedAt,
@@ -15746,7 +16425,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
15746
16425
  case "reasoning": {
15747
16426
  const text = reasoningText(item);
15748
16427
  if (text) {
15749
- envelopes.push(index.createEnvelope("agent", { t: "text", text, thinking: true }, {
16428
+ envelopes.push(persistence.createEnvelope("agent", { t: "text", text, thinking: true }, {
15750
16429
  id: item.id,
15751
16430
  turn: turn.id,
15752
16431
  time: startedAt,
@@ -15801,7 +16480,7 @@ function mapCodexThreadToSessionEnvelopes(thread) {
15801
16480
  }
15802
16481
  }
15803
16482
  }
15804
- envelopes.push(index.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
16483
+ envelopes.push(persistence.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
15805
16484
  id: `${turn.id}:end`,
15806
16485
  turn: turn.id,
15807
16486
  time: completedAt
@@ -15856,7 +16535,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15856
16535
  }
15857
16536
  if (type === "task_started") {
15858
16537
  const turnId = cuid2.createId();
15859
- const turnStart = index.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
16538
+ const turnStart = persistence.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
15860
16539
  startedSubagents.clear();
15861
16540
  activeSubagents.clear();
15862
16541
  providerSubagentToSessionSubagent.clear();
@@ -15887,7 +16566,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15887
16566
  providerSubagentToSessionSubagent,
15888
16567
  envelopes: [
15889
16568
  ...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
15890
- index.createEnvelope("agent", {
16569
+ persistence.createEnvelope("agent", {
15891
16570
  t: "turn-end",
15892
16571
  status: pickTurnEndStatus(message, type)
15893
16572
  }, lifecycleOpts)
@@ -15917,7 +16596,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15917
16596
  }
15918
16597
  const envelopes = [];
15919
16598
  maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
15920
- envelopes.push(index.createEnvelope("agent", { t: "text", text: message.message }, opts));
16599
+ envelopes.push(persistence.createEnvelope("agent", { t: "text", text: message.message }, opts));
15921
16600
  return {
15922
16601
  currentTurnId: state.currentTurnId,
15923
16602
  startedSubagents,
@@ -15939,7 +16618,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15939
16618
  }
15940
16619
  const envelopes = [];
15941
16620
  maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
15942
- envelopes.push(index.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
16621
+ envelopes.push(persistence.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
15943
16622
  return {
15944
16623
  currentTurnId: state.currentTurnId,
15945
16624
  startedSubagents,
@@ -15956,7 +16635,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15956
16635
  const envelopes = [];
15957
16636
  maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
15958
16637
  envelopes.push(
15959
- index.createEnvelope("agent", {
16638
+ persistence.createEnvelope("agent", {
15960
16639
  t: "tool-call-start",
15961
16640
  call,
15962
16641
  name: "CodexBash",
@@ -15977,7 +16656,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15977
16656
  const call = pickCallId(message);
15978
16657
  const envelopes = [];
15979
16658
  maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
15980
- envelopes.push(index.createEnvelope("agent", { t: "tool-call-end", call }, opts));
16659
+ envelopes.push(persistence.createEnvelope("agent", { t: "tool-call-end", call }, opts));
15981
16660
  return {
15982
16661
  currentTurnId: state.currentTurnId,
15983
16662
  startedSubagents,
@@ -15993,7 +16672,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
15993
16672
  const envelopes = [];
15994
16673
  maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
15995
16674
  envelopes.push(
15996
- index.createEnvelope("agent", {
16675
+ persistence.createEnvelope("agent", {
15997
16676
  t: "tool-call-start",
15998
16677
  call,
15999
16678
  name: "CodexPatch",
@@ -16017,7 +16696,7 @@ function mapCodexMcpMessageToSessionEnvelopes(message, state) {
16017
16696
  const call = pickCallId(message);
16018
16697
  const envelopes = [];
16019
16698
  maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
16020
- envelopes.push(index.createEnvelope("agent", { t: "tool-call-end", call }, opts));
16699
+ envelopes.push(persistence.createEnvelope("agent", { t: "tool-call-end", call }, opts));
16021
16700
  return {
16022
16701
  currentTurnId: state.currentTurnId,
16023
16702
  startedSubagents,
@@ -16038,7 +16717,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
16038
16717
  const toolLikeMessage = message;
16039
16718
  const opts = buildEnvelopeOptions(state.currentTurnId);
16040
16719
  if (message.type === "reasoning") {
16041
- return [index.createEnvelope("agent", {
16720
+ return [persistence.createEnvelope("agent", {
16042
16721
  t: "text",
16043
16722
  text: message.message,
16044
16723
  thinking: true
@@ -16046,7 +16725,7 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
16046
16725
  }
16047
16726
  if (message.type === "tool-call") {
16048
16727
  const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
16049
- return [index.createEnvelope("agent", {
16728
+ return [persistence.createEnvelope("agent", {
16050
16729
  t: "tool-call-start",
16051
16730
  call: toolLikeMessage.callId,
16052
16731
  name: toolLikeMessage.name || "unknown",
@@ -16059,13 +16738,13 @@ function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
16059
16738
  const envelopes = [];
16060
16739
  const content = toolLikeMessage.output?.content;
16061
16740
  if (typeof content === "string" && content.trim().length > 0) {
16062
- envelopes.push(index.createEnvelope("agent", {
16741
+ envelopes.push(persistence.createEnvelope("agent", {
16063
16742
  t: "text",
16064
16743
  text: content,
16065
16744
  thinking: true
16066
16745
  }, opts));
16067
16746
  }
16068
- envelopes.push(index.createEnvelope("agent", {
16747
+ envelopes.push(persistence.createEnvelope("agent", {
16069
16748
  t: "tool-call-end",
16070
16749
  call: toolLikeMessage.callId
16071
16750
  }, opts));
@@ -17061,7 +17740,7 @@ function buildCodexTurnPrompt(opts) {
17061
17740
  parts.push(opts.message);
17062
17741
  if (opts.includeTitleInstruction) {
17063
17742
  parts.push(CHANGE_TITLE_INSTRUCTION);
17064
- parts.push(index.BUILTIN_TODO_DISCOVERY);
17743
+ parts.push(persistence.BUILTIN_TODO_DISCOVERY);
17065
17744
  }
17066
17745
  return parts.join("\n\n");
17067
17746
  }
@@ -17620,7 +18299,7 @@ async function runCodex(opts) {
17620
18299
  }
17621
18300
  });
17622
18301
  const happyServer = await startHappyServer(session);
17623
- const bridgeEntrypoint = path.join(projectPath.projectPath(), "bin", "very-happy-mcp.mjs");
18302
+ const bridgeEntrypoint = path.join(terminalTitleSuggest.projectPath(), "bin", "very-happy-mcp.mjs");
17624
18303
  const mcpServers = {
17625
18304
  happy: {
17626
18305
  command: process.execPath,
@@ -18033,7 +18712,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18033
18712
  process.exit(0);
18034
18713
  } else if (subcommand === "install-pi-tools") {
18035
18714
  try {
18036
- const { handleInstallPiTools } = await Promise.resolve().then(function () { return require('./installPiTools-BKnXjrVu.cjs'); });
18715
+ const { handleInstallPiTools } = await Promise.resolve().then(function () { return require('./installPiTools-DuZ6jZvX.cjs'); });
18037
18716
  await handleInstallPiTools(args.slice(1));
18038
18717
  } catch (error) {
18039
18718
  console.error(error instanceof Error ? error.message : "Pi tools installation failed");
@@ -18042,7 +18721,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18042
18721
  return;
18043
18722
  } else if (subcommand === "install-terminal-hooks") {
18044
18723
  try {
18045
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-Bf_adbl3.cjs'); });
18724
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-BF0kL0ae.cjs'); });
18046
18725
  const command = parseTerminalHooksArgs(args.slice(1));
18047
18726
  if (command.action === "help") {
18048
18727
  console.log(TERMINAL_HOOKS_HELP);
@@ -18059,7 +18738,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18059
18738
  }
18060
18739
  } else if (subcommand === "agent-home") {
18061
18740
  try {
18062
- const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-D6MSfwnp.cjs'); });
18741
+ const { parseAgentHomeArgs, runAgentHomeCommand } = await Promise.resolve().then(function () { return require('./agentHome-BJ7bde35.cjs'); });
18063
18742
  await runAgentHomeCommand(parseAgentHomeArgs(args.slice(1)));
18064
18743
  process.exit(0);
18065
18744
  } catch (error) {
@@ -18068,7 +18747,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18068
18747
  }
18069
18748
  } else if (subcommand === "spawn") {
18070
18749
  try {
18071
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DH6_p4-f.cjs'); });
18750
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-od9V23cc.cjs'); });
18072
18751
  await handleSpawnCommand(args.slice(1));
18073
18752
  } catch (error) {
18074
18753
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -18080,7 +18759,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18080
18759
  return;
18081
18760
  } else if (subcommand === "sessions") {
18082
18761
  try {
18083
- const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-B3vP18kz.cjs'); });
18762
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-qULzUzlI.cjs'); });
18084
18763
  await handleSessionsCommand(args.slice(1));
18085
18764
  } catch (error) {
18086
18765
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -18092,7 +18771,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18092
18771
  return;
18093
18772
  } else if (subcommand === "send") {
18094
18773
  try {
18095
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-aUkjhey7.cjs'); });
18774
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-9075kHZP.cjs'); });
18096
18775
  await handleSendCommand(args.slice(1));
18097
18776
  } catch (error) {
18098
18777
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -18198,9 +18877,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18198
18877
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
18199
18878
  const projectId = args[3];
18200
18879
  try {
18201
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-Cadw1sAk.cjs'); });
18202
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DwcY3kyi.cjs'); }).then(function (n) { return n.persistence; });
18203
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CPubg05Z.cjs'); }).then(function (n) { return n.api; });
18880
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-D23kF0_u.cjs'); });
18881
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DY_sFA3J.cjs'); }).then(function (n) { return n.persistence; });
18882
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-CUCU7Vw8.cjs'); }).then(function (n) { return n.api; });
18204
18883
  let userEmail = void 0;
18205
18884
  try {
18206
18885
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -18231,7 +18910,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18231
18910
  }
18232
18911
  if (geminiSubcommand === "project" && args[2] === "get") {
18233
18912
  try {
18234
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-Cadw1sAk.cjs'); });
18913
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-D23kF0_u.cjs'); });
18235
18914
  const config = readGeminiLocalConfig();
18236
18915
  if (config.googleCloudProject) {
18237
18916
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -18271,7 +18950,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18271
18950
  process.exit(0);
18272
18951
  }
18273
18952
  try {
18274
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-DuwdVV1O.cjs'); });
18953
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-CtOEFvhf.cjs'); });
18275
18954
  let startedBy = void 0;
18276
18955
  for (let i = 1; i < args.length; i++) {
18277
18956
  if (args[i] === "--started-by") {
@@ -18294,7 +18973,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18294
18973
  } else if (subcommand === "pi") {
18295
18974
  if (args[1] === "--terminal") {
18296
18975
  try {
18297
- const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-kDSd_r9X.cjs'); });
18976
+ const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-qdJ9KZVL.cjs'); });
18298
18977
  process.exitCode = await runPiTerminal(args.slice(2));
18299
18978
  } catch (error) {
18300
18979
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -18311,7 +18990,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18311
18990
  }
18312
18991
  };
18313
18992
  try {
18314
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BKGspSWk.cjs'); });
18993
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DEOVn46R.cjs'); });
18315
18994
  const parsed = parsePiRunnerArgs(args.slice(1));
18316
18995
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
18317
18996
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -18339,7 +19018,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18339
19018
  return;
18340
19019
  } else if (subcommand === "acp") {
18341
19020
  try {
18342
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BKGspSWk.cjs'); });
19021
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DEOVn46R.cjs'); });
18343
19022
  let startedBy = void 0;
18344
19023
  let verbose = false;
18345
19024
  const acpArgs = [];
@@ -18379,7 +19058,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18379
19058
  return;
18380
19059
  } else if (subcommand === "openclaw") {
18381
19060
  try {
18382
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-BsvtCE_H.cjs'); });
19061
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-DoqZXBvL.cjs'); });
18383
19062
  let startedBy = void 0;
18384
19063
  let verbose = false;
18385
19064
  let gatewayUrl;
@@ -18430,7 +19109,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18430
19109
  return;
18431
19110
  } else if (subcommand === "todo") {
18432
19111
  try {
18433
- const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-Bcc2XxGh.cjs'); });
19112
+ const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-BCT_ZPuV.cjs'); });
18434
19113
  await handleTodoCommand(args.slice(1));
18435
19114
  } catch (error) {
18436
19115
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -18439,16 +19118,28 @@ Conversation history is preserved on the server, but in-flight tool calls are in
18439
19118
  return;
18440
19119
  } else if (subcommand === "teams") {
18441
19120
  try {
18442
- const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-BW8D1n3o.cjs'); });
19121
+ const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-CrCzU-AV.cjs'); });
18443
19122
  await handleTeamsCommand(args.slice(1));
18444
19123
  } catch (error) {
18445
19124
  console.error(error instanceof Error ? error.message : "Teams command failed");
18446
19125
  process.exitCode = 1;
18447
19126
  }
18448
19127
  return;
19128
+ } else if (subcommand === "auto") {
19129
+ try {
19130
+ const { handleAutoCommand } = await Promise.resolve().then(function () { return require('./auto-C-90ojIH.cjs'); });
19131
+ await handleAutoCommand(args.slice(1));
19132
+ } catch (error) {
19133
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Automation command failed");
19134
+ if (process.env.DEBUG) {
19135
+ console.error(error);
19136
+ }
19137
+ process.exitCode = 1;
19138
+ }
19139
+ return;
18449
19140
  } else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
18450
19141
  try {
18451
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-CMxEZ6-0.cjs'); });
19142
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-JpqI2POW.cjs'); });
18452
19143
  await handleMcpCommand(args[1] === "--terminal-tools");
18453
19144
  } catch (error) {
18454
19145
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -18682,6 +19373,8 @@ ${chalk.bold("Usage:")}
18682
19373
  conversations on this machine (agent-home --help)
18683
19374
  very-happy connect Connect AI vendor API keys
18684
19375
  very-happy todo Built-in Todos and official agent skill (todo --help)
19376
+ very-happy auto Scheduled / triggered automations: spawn, sticky
19377
+ sessions or scripts with tracked runs (auto --help)
18685
19378
  very-happy sandbox Configure and manage OS-level sandboxing
18686
19379
  very-happy notify Send push notification
18687
19380
  very-happy mcp Stdio MCP server exposing copy_to_clipboard for a
@@ -18839,12 +19532,15 @@ exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
18839
19532
  exports.archiveSession = archiveSession;
18840
19533
  exports.checkPreviewPath = checkPreviewPath;
18841
19534
  exports.createSessionMetadata = createSessionMetadata;
19535
+ exports.describeAction = describeAction;
19536
+ exports.describeTrigger = describeTrigger;
18842
19537
  exports.hashObject = hashObject;
18843
19538
  exports.initialMachineMetadata = initialMachineMetadata;
18844
19539
  exports.isValidSpawnOrigin = isValidSpawnOrigin;
18845
19540
  exports.listAccountSessions = listAccountSessions;
18846
19541
  exports.listSessions = listSessions;
18847
19542
  exports.normalizeAcpPermissionMode = normalizeAcpPermissionMode;
19543
+ exports.parseDuration = parseDuration;
18848
19544
  exports.query = query;
18849
19545
  exports.readSessionMetadata = readSessionMetadata;
18850
19546
  exports.readSessionTranscript = readSessionTranscript;
@@ -18852,7 +19548,9 @@ exports.registerAgentAttachmentDownloads = registerAgentAttachmentDownloads;
18852
19548
  exports.registerAssistantSessionTools = registerAssistantSessionTools;
18853
19549
  exports.registerKillSessionHandler = registerKillSessionHandler;
18854
19550
  exports.removeSessionModeFile = removeSessionModeFile;
19551
+ exports.resolveFirstMessageMeta = resolveFirstMessageMeta;
18855
19552
  exports.resolvePermissionRequest = resolvePermissionRequest;
19553
+ exports.resolveSpawnPermissionMode = resolveSpawnPermissionMode;
18856
19554
  exports.sanitizeSpawnPermissionMode = sanitizeSpawnPermissionMode;
18857
19555
  exports.sendUserMessage = sendUserMessage;
18858
19556
  exports.sessionWebUrl = sessionWebUrl;
@@ -18860,7 +19558,9 @@ exports.setupOfflineReconnection = setupOfflineReconnection;
18860
19558
  exports.stageClaudeAttachments = stageClaudeAttachments;
18861
19559
  exports.startHappyServer = startHappyServer;
18862
19560
  exports.stopSession = stopSession;
19561
+ exports.suggestTitleForPrompt = suggestTitleForPrompt;
18863
19562
  exports.tmuxSupportsSessionEnv = tmuxSupportsSessionEnv;
19563
+ exports.triggerFromFlags = triggerFromFlags;
18864
19564
  exports.waitForSessionKey = waitForSessionKey;
18865
19565
  exports.waitForTurnEnd = waitForTurnEnd;
18866
19566
  exports.writeSessionModeFile = writeSessionModeFile;