very-happy-cli 0.2.125 → 0.2.127

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 (47) hide show
  1. package/dist/{AcpBackend-DOQGItcS.mjs → AcpBackend-B9_vB_mY.mjs} +2 -2
  2. package/dist/{AcpBackend-ssQpTLks.cjs → AcpBackend-Cbykm5Pg.cjs} +2 -2
  3. package/dist/{AcpSessionManager-DHLc-D50.cjs → AcpSessionManager-DGCsNKRd.cjs} +1 -1
  4. package/dist/{AcpSessionManager-D4WkNsRs.mjs → AcpSessionManager-HsxKXreC.mjs} +1 -1
  5. package/dist/{agentGuidance-Cg9ZG1pP.cjs → agentGuidance-B-CazXmT.cjs} +64 -33
  6. package/dist/{agentGuidance-QpYwykb9.mjs → agentGuidance-CTyzeLCQ.mjs} +62 -35
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-CZNIz3W0.mjs → config-DotNknpP.mjs} +4 -4
  10. package/dist/{config-BxHOGtEK.cjs → config-Dwt-15hE.cjs} +4 -4
  11. package/dist/{index-CrSifgJM.mjs → index-B1qzJIMw.mjs} +249 -28
  12. package/dist/{index-Jrk6ud3g.mjs → index-C2kblG8-.mjs} +13 -9
  13. package/dist/{index-D-BgvQi5.mjs → index-CWPgxeLD.mjs} +4 -4
  14. package/dist/{index-Do-kjc30.cjs → index-DgjzHewW.cjs} +254 -31
  15. package/dist/{index-D_Rm9dw9.cjs → index-DhCEmP3d.cjs} +4 -4
  16. package/dist/{index-CBj99SO6.cjs → index-DyvVjr8Y.cjs} +13 -9
  17. package/dist/index.cjs +4 -4
  18. package/dist/index.mjs +4 -4
  19. package/dist/{installTerminalHooks-CPsdeOM4.cjs → installTerminalHooks-DfBvxRdw.cjs} +4 -4
  20. package/dist/{installTerminalHooks-Z1Jb6yjb.mjs → installTerminalHooks-cpuDExTT.mjs} +4 -4
  21. package/dist/lib.cjs +2 -2
  22. package/dist/lib.d.cts +1 -0
  23. package/dist/lib.d.mts +1 -0
  24. package/dist/lib.mjs +2 -2
  25. package/dist/{limits-DViGHwSj.cjs → limits-BDG3QjIB.cjs} +7 -7
  26. package/dist/{limits-FjbPJPsF.mjs → limits-D2XM3dvN.mjs} +9 -9
  27. package/dist/{mcp-BbQ5FWcQ.cjs → mcp-BZ3iYnBc.cjs} +4 -4
  28. package/dist/{mcp-zqPJ94Bk.mjs → mcp-CpUrsnoC.mjs} +4 -4
  29. package/dist/{runGemini-BnHGYX-H.cjs → runGemini-BE_nghsj.cjs} +6 -6
  30. package/dist/{runGemini-Bu49s_bN.mjs → runGemini-BY7uA-Uk.mjs} +6 -6
  31. package/dist/{runOpenClaw-CznAYPCh.mjs → runOpenClaw-Be_U4DHZ.mjs} +5 -5
  32. package/dist/{runOpenClaw-3Ld1eoWV.cjs → runOpenClaw-Cqun9jar.cjs} +5 -5
  33. package/dist/{send-BevgS1mf.mjs → send-D2xX42zg.mjs} +4 -4
  34. package/dist/{send-DCzyBxLl.cjs → send-DbqRxLQW.cjs} +4 -4
  35. package/dist/{sessions-DSoaBHwA.cjs → sessions-CtrMbMBJ.cjs} +4 -4
  36. package/dist/{sessions-Ba2oRTmw.mjs → sessions-D06cLJ7f.mjs} +4 -4
  37. package/dist/{spawn-CqMLY6dE.cjs → spawn-Cu_kM761.cjs} +4 -4
  38. package/dist/{spawn-j9KBfj4e.mjs → spawn-DMx1JKXo.mjs} +4 -4
  39. package/dist/{teams-CmU3K9vB.cjs → teams-C2MfKWFe.cjs} +67 -7
  40. package/dist/{teams-C0ggJTJ-.mjs → teams-D7ubdfTY.mjs} +64 -4
  41. package/dist/todo-hKSIY23F.mjs +128 -0
  42. package/dist/todo-yvodzg_y.cjs +132 -0
  43. package/dist/{types-BGHnQXl5.cjs → types-BuxqyzYf.cjs} +329 -12
  44. package/dist/{types-DLDkfrTC.mjs → types-Df_vXvzD.mjs} +320 -6
  45. package/package.json +7 -7
  46. package/dist/resources-CVl7NdOB.cjs +0 -83
  47. package/dist/resources-D-mn7ptO.mjs +0 -80
@@ -1,13 +1,13 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
2
2
  import os, { homedir, userInfo, tmpdir } from 'node:os';
3
3
  import { randomUUID, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
4
- import { g as getProjectPath, p as projectPath, h as initializeSandbox, w as wrapCommand, b as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, d as delay, j as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, k as commandOnPath, l as deriveLocalCliUpdateSummary, m as isClaudeSessionId, n as getLocalHappyAgentCredentialPath, r as readLocalHappyAgentCredentials, o as parseExactVersion, q as compareExactVersions, t as extractCompleteLines, u as decideBackfill, v as parseTerminalHookPayload, x as tmuxArgs, y as scrubTmuxClientEnv, z as decideMirrorBinding, B as MIRROR_BACKFILL_LINES_DEFAULT, C as mirrorLineKey, D as mirrorLocalId, E as detectCLIAvailability, F as detectResumeSupport, G as summarizeSpawnSessionForLog, H as shellescape, A as ApiClient, J as fetchCliUpdateState, K as resolveCliUpdateCheckInterval, L as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, i as isValidSessionId, N as listVhTerminals, O as isValidTerminalId, P as readVhTerminal, Q as sendToVhTerminal, T as TITLE_PROMPT_PREFIX, U as BOARD_ANALYZER_PROMPT_PREFIX, f as connectionState, V as startOfflineReconnection, c as createEnvelope, W as CodexAppServerClient } from './types-DLDkfrTC.mjs';
5
- import { z as readDaemonState, l as logger, c as configuration, A as clearDaemonState, B as ensurePrivateDirectory, D as hardenPrivateFile, p as packageJson, f as readSettings, E as readCredentials, s as credentialRelayProblem, g as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, m as decodeBase64, I as writeCredentialsLegacy, J as writeCredentialsDataKey, i as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, K as decryptWithDataKey, L as decryptLegacy, M as persistSession, k as decrypt, e as ensurePrivateDirectorySync, N as acquireDaemonLock, O as deletePersistedSessions, P as writeDaemonState, Q as writeSettings, R as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, d as CLIPBOARD_MAX_BYTES, S as clearCredentials, T as clearMachineId, U as SandboxConfigSchema, V as getLatestDaemonLog } from './limits-FjbPJPsF.mjs';
4
+ import { j as getProjectPath, h as BUILTIN_TODO_DISCOVERY, p as projectPath, k as initializeSandbox, w as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, f as delay, l as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, m as commandOnPath, n as deriveLocalCliUpdateSummary, o as isClaudeSessionId, q as getLocalHappyAgentCredentialPath, r as readLocalHappyAgentCredentials, t as parseExactVersion, u as compareExactVersions, v as extractCompleteLines, x as decideBackfill, y as parseTerminalHookPayload, z as tmuxArgs, C as scrubTmuxClientEnv, D as decideMirrorBinding, E as MIRROR_BACKFILL_LINES_DEFAULT, F as mirrorLineKey, G as mirrorLocalId, H as detectCLIAvailability, J as detectResumeSupport, K as summarizeSpawnSessionForLog, L as shellescape, A as ApiClient, N as fetchCliUpdateState, O as resolveCliUpdateCheckInterval, P as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, i as isValidSessionId, Q as listVhTerminals, T as isValidTerminalId, U as readVhTerminal, V as sendToVhTerminal, W as TITLE_PROMPT_PREFIX, X as BOARD_ANALYZER_PROMPT_PREFIX, g as connectionState, Y as startOfflineReconnection, c as createEnvelope, Z as CodexAppServerClient } from './types-Df_vXvzD.mjs';
5
+ import { z as readDaemonState, l as logger, c as configuration, A as clearDaemonState, B as ensurePrivateDirectory, D as hardenPrivateFile, p as packageJson, g as readSettings, E as readCredentials, s as credentialRelayProblem, i as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, m as decodeBase64, I as writeCredentialsLegacy, J as writeCredentialsDataKey, d as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, K as decryptWithDataKey, L as decryptLegacy, M as persistSession, k as decrypt, f as ensurePrivateDirectorySync, N as acquireDaemonLock, O as deletePersistedSessions, P as writeDaemonState, Q as writeSettings, R as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES, S as clearCredentials, T as clearMachineId, U as SandboxConfigSchema, V as getLatestDaemonLog } from './limits-D2XM3dvN.mjs';
6
6
  import spawn$1, { spawn } from 'cross-spawn';
7
7
  import path, { join, resolve, basename, isAbsolute, dirname, sep, delimiter, normalize } from 'node:path';
8
8
  import { createInterface } from 'node:readline';
9
9
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, unlinkSync, renameSync, writeFileSync, accessSync, constants, openSync, fsyncSync, closeSync, rmSync as rmSync$1, chmodSync as chmodSync$1 } from 'node:fs';
10
- import { t as trimIdent, A as AGENT_GUIDANCE, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, T as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, d as REPORT_PROGRESS_TOOL_TITLE, e as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-QpYwykb9.mjs';
10
+ import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-CTyzeLCQ.mjs';
11
11
  import { readFile, writeFile, rename, unlink, mkdir, symlink, rm, readlink, lstat, stat, open as open$1, appendFile } from 'node:fs/promises';
12
12
  import fs, { watch, access } from 'fs/promises';
13
13
  import { useStdout, useInput, Box, Text, render } from 'ink';
@@ -647,7 +647,7 @@ function shouldIncludeCoAuthoredBy() {
647
647
 
648
648
  const BASE_SYSTEM_PROMPT = (() => trimIdent(`
649
649
  ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
650
- `))() + "\n\n" + AGENT_GUIDANCE;
650
+ `))() + "\n\n" + AGENT_GUIDANCE + "\n\n" + BUILTIN_TODO_DISCOVERY;
651
651
  const CO_AUTHORED_CREDITS = (() => trimIdent(`
652
652
  When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
653
653
 
@@ -5831,7 +5831,8 @@ async function sendUserMessage(sessionId, persisted, text, client, options = {})
5831
5831
  text
5832
5832
  },
5833
5833
  meta: {
5834
- sentFrom: options.sentFrom ?? "cli"
5834
+ sentFrom: options.sentFrom ?? "cli",
5835
+ ...options.model ? { model: options.model } : {}
5835
5836
  }
5836
5837
  };
5837
5838
  const encrypted = encodeBase64(encrypt(
@@ -8800,6 +8801,100 @@ async function claimSessionOrExit(happySessionId, options) {
8800
8801
  process.on("exit", () => releaseSessionLock(happySessionId));
8801
8802
  }
8802
8803
 
8804
+ const TEAM_SKILL = `---
8805
+ name: very-happy-teams
8806
+ description: Coordinate coding agents through Very Happy teams: delegate tasks, exchange messages, review results, and recover interrupted work. Use when asked to organize or participate in an agent team.
8807
+ ---
8808
+
8809
+ # Very Happy Agent Teams
8810
+
8811
+ Use the team_* tools in a managed Very Happy session. If absent, explain that this host needs Very Happy Teams setup; reading this skill alone does not establish a connection.
8812
+
8813
+ Start with team_inspect if already assigned. Otherwise team_create or team_join connects the current session. A scoped worker cannot create another root identity or use account-wide tools to bypass its scope.
8814
+
8815
+ Work yourself when delegation would add needless overhead. Delegate independent work with a clear goal and acceptance criteria. When delegating within your assignment, pass its parentTaskId. Members may delegate further within their existing authority. After delegating child work, wait for those results instead of continuing parallel edits yourself. Waiting parents yield their execution slot to their children; the team limit counts progressing leaf assignments, not OS processes.
8816
+
8817
+ Name exactly one integrator for each team. Workers edit only their assigned worktrees; they must not merge main, publish releases, change global daemon or npm installations, or allocate shared backlog IDs. The integrator serializes these operations. Across teams, the owner coordinates shared resources: a worktree is not a global lock.
8818
+
8819
+ Use a unique requestId for each action and reuse it with identical arguments when the outcome is unknown. Inspect state before retrying; a timeout does not prove no action occurred. A delivered message is not proof its recipient processed it.
8820
+
8821
+ Inspect the current attemptId and goalVersion before team_submit. Include artifact locations and verification evidence. The owner reviews and accepts or returns the result. All children completing does not automatically satisfy the parent goal.
8822
+
8823
+ Acceptance and cleanup are separate. Never delete dirty worktrees, stop user-owned sessions, or bypass permissions to finish cleanup. Use team_cancel or team_handoff and inspect the resulting status. Keep summaries concise; do not repeatedly forward full descendant transcripts.
8824
+
8825
+ A root member may create a persistent schedule for itself with the schedule tools; team_inspect shows schedules. Timely delivery requires an online daemon. A scheduled delivery does not prove the model completed the work. Pause or cancel schedules explicitly when they are no longer needed.
8826
+
8827
+ On restart inspect authoritative state and messages before taking new actions. Do not invent completed work from a previous context. Scope credentials are runtime-owned; never read, print, or copy them into prompts.
8828
+ `;
8829
+ const PI_TEAMS_EXTENSION = `// Managed by Very Happy Agent Teams.
8830
+ export default async function(pi) {
8831
+ const endpoint = process.env.HAPPY_MCP_URL;
8832
+ if (!endpoint) return;
8833
+ const url = new URL(endpoint);
8834
+ if (!['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.protocol !== 'http:') throw new Error('Very Happy MCP must be local');
8835
+ const {readFile} = await import('node:fs/promises');
8836
+ const {join} = await import('node:path');
8837
+ const {homedir} = await import('node:os');
8838
+ pi.on('tool_call', async (event, ctx) => {
8839
+ let mode = process.env.HAPPY_PERMISSION_MODE || 'default';
8840
+ const sid = process.env.HAPPY_SESSION_ID;
8841
+ if (sid && /^[a-zA-Z0-9_-]+$/.test(sid)) {
8842
+ try { mode = JSON.parse(await readFile(join(process.env.HAPPY_HOME_DIR || join(homedir(), '.happy'), 'session-modes', sid + '.json'), 'utf8')).permissionMode; }
8843
+ catch (error) { if (error.code !== 'ENOENT') mode = 'default'; }
8844
+ }
8845
+ if (mode === 'yolo') mode = 'bypassPermissions';
8846
+ if (!['default','plan','acceptEdits','bypassPermissions'].includes(mode)) mode = 'default';
8847
+ if (mode === 'bypassPermissions') return;
8848
+ if (['read','ls','find','grep','team_inspect','change_title','report_progress'].includes(event.toolName)) return;
8849
+ if (mode === 'plan') return {block:true,reason:'Plan mode allows read-only tools'};
8850
+ if (mode === 'acceptEdits' && ['write','edit'].includes(event.toolName)) return;
8851
+ try {
8852
+ const allowed = await ctx.ui.confirm('Allow ' + event.toolName + '?', JSON.stringify(event.input || {}).slice(0,4000));
8853
+ if (allowed === true) return;
8854
+ } catch {}
8855
+ return {block:true,reason:'Tool requires explicit approval'};
8856
+ });
8857
+ let nextId = 0;
8858
+ async function rpc(method, params) {
8859
+ const requestId = ++nextId;
8860
+ const response = await fetch(endpoint, { method: 'POST', redirect: 'error', headers: {'content-type':'application/json', accept:'application/json, text/event-stream'}, body: JSON.stringify({jsonrpc:'2.0',id:requestId,method,params}), signal: AbortSignal.timeout(30000) });
8861
+ if (!response.ok) throw new Error('Very Happy tools unavailable (HTTP ' + response.status + ')');
8862
+ const body = await response.text();
8863
+ let data;
8864
+ if ((response.headers.get('content-type') || '').includes('text/event-stream')) {
8865
+ for (const event of body.split(/\\r?\\n\\r?\\n/)) {
8866
+ const payload = event.split(/\\r?\\n/).filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\\n');
8867
+ if (payload) { const candidate = JSON.parse(payload); if (candidate.id === requestId) data = candidate; }
8868
+ }
8869
+ } else data = JSON.parse(body);
8870
+ if (!data || data.error) throw new Error('Very Happy tool request failed');
8871
+ return data.result;
8872
+ }
8873
+ await rpc('initialize', {protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'very-happy-pi-teams',version:'1'}});
8874
+ const available = await rpc('tools/list', {});
8875
+ for (const tool of available.tools || []) {
8876
+ pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
8877
+ async execute(_id, args) { const result = await rpc('tools/call', {name:tool.name,arguments:args}); return {content:result.content,details:{isError:!!result.isError}}; }
8878
+ });
8879
+ }
8880
+ }
8881
+ `;
8882
+
8883
+ function teamInitialPrompt(goal, root, directory) {
8884
+ const role = root ? "You are the team lead. Inspect your assigned goal, arrange independent work when useful, review member results, integrate changes, and submit the final result for the user to accept. Delegate under your assigned goal using parentTaskId. You must not accept your own root goal." : "You are a team member. Inspect your assigned task and work in your assigned worktree. Delegate within your task when useful, then submit evidence for the lead to review.";
8885
+ return `${TEAM_SKILL}
8886
+
8887
+ ## Your role
8888
+ ${role}
8889
+
8890
+ ## Working directory
8891
+ ${directory ? JSON.stringify(directory) : "Use your current managed session working directory."}
8892
+ Delegate from this working directory unless a different repository was explicitly requested.
8893
+
8894
+ ## Assigned goal
8895
+ ${goal}`;
8896
+ }
8897
+
8803
8898
  const MAX_LINE_CHARS = 500;
8804
8899
  const MAX_ARGS_CHARS = 160;
8805
8900
  function truncateText(text, max = MAX_LINE_CHARS) {
@@ -9381,7 +9476,7 @@ function createTeamWorker(deps) {
9381
9476
  let knownSessionIds = /* @__PURE__ */ new Set();
9382
9477
  const action = async (teamId, requestId, value) => (await http.post(`/v1/teams/${encodeURIComponent(teamId)}/actions`, { requestId, action: value })).data;
9383
9478
  async function complete(op, receipt) {
9384
- await action(op.teamId, `complete-${op.id}`, { type: "complete-operation", operationId: op.id, machineId: deps.machineId, claimId: receipt.claimId, ...receipt.sessionId ? { sessionId: receipt.sessionId } : {} });
9479
+ 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 } : {} });
9385
9480
  writeReceipt(home, { ...receipt, phase: "completed" });
9386
9481
  }
9387
9482
  async function execute(op) {
@@ -9394,7 +9489,7 @@ function createTeamWorker(deps) {
9394
9489
  }
9395
9490
  return;
9396
9491
  }
9397
- const claimed = await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId });
9492
+ const claimed = await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId, ...op.teamLaunchVersion === 1 ? { teamLaunchVersion: 1 } : {} });
9398
9493
  if (!claimed.operation?.claimId) throw new Error("Operation claim did not return a receipt");
9399
9494
  op = claimed.operation;
9400
9495
  credential = claimed.credential;
@@ -9405,7 +9500,7 @@ function createTeamWorker(deps) {
9405
9500
  try {
9406
9501
  if (op.type === "spawn") {
9407
9502
  if (receipt.phase === "prepared") {
9408
- if (!credential) credential = (await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId })).credential;
9503
+ if (!credential) credential = (await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId, ...op.teamLaunchVersion === 1 ? { teamLaunchVersion: 1 } : {} })).credential;
9409
9504
  if (!credential) throw new Error("No scoped credential for worker");
9410
9505
  const current = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
9411
9506
  const task = current.tasks.find((t) => t.id === op.taskId);
@@ -9423,7 +9518,7 @@ function createTeamWorker(deps) {
9423
9518
  writePrivateFileSync(scopeFile, JSON.stringify({ serverUrl: server, scopeToken: credential.token, teamId: op.teamId, botId: op.botId, taskId: op.taskId, attemptId: op.attemptId }));
9424
9519
  receipt = { ...receipt, ...resource, scopeFile, phase: "spawning" };
9425
9520
  writeReceipt(home, receipt);
9426
- const spawned = await deps.spawn({ directory: resource.directory, agent: op.assistant === "pi-acp" ? "pi" : op.assistant, permissionMode: "default", spawnedBy: "teams", environmentVariables: { VH_TEAM_SCOPE_FILE: scopeFile, VH_TEAM_OPERATION_ID: op.id } });
9521
+ 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 } });
9427
9522
  if (spawned.type !== "success") throw new Error(spawned.type === "error" ? spawned.errorMessage : "Directory approval required");
9428
9523
  receipt = { ...receipt, sessionId: spawned.sessionId, phase: "spawned" };
9429
9524
  writeReceipt(home, receipt);
@@ -9445,7 +9540,7 @@ function createTeamWorker(deps) {
9445
9540
  return;
9446
9541
  }
9447
9542
  const key = await waitForSessionKey(receipt.sessionId, 1e4);
9448
- await sendUserMessage(receipt.sessionId, key, op.prompt, "teams", { localId: `teams-initial-${op.id}`, sentFrom: "team" });
9543
+ 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 } : {} });
9449
9544
  receipt = { ...receipt, phase: "delivered" };
9450
9545
  writeReceipt(home, receipt);
9451
9546
  }
@@ -9547,7 +9642,7 @@ Use the teams tools to inspect current task state before acting. This message is
9547
9642
  const status = axios.isAxiosError(error) ? error.response?.status : void 0;
9548
9643
  if (status !== 404) deps.log("Teams schedule advancement unavailable; regular reconciliation continues");
9549
9644
  }
9550
- const { data } = await http.get("/v1/teams/operations", { params: { machineId: deps.machineId, schedulesVersion: 1 } });
9645
+ const { data } = await http.get("/v1/teams/operations", { params: { machineId: deps.machineId, schedulesVersion: 1, teamLaunchVersion: 1 } });
9551
9646
  knownSessionIds = new Set((data.teams ?? []).flatMap((team) => team.bots.flatMap((bot) => bot.sessionId ? [bot.sessionId] : [])));
9552
9647
  for (const op of data.operations) {
9553
9648
  if (closed) break;
@@ -9591,6 +9686,7 @@ const hostSuffix = process.env.HAPPY_VARIANT === "dev" ? "-dev" : "";
9591
9686
  const startupCliAvailability = detectCLIAvailability();
9592
9687
  const initialMachineMetadata = {
9593
9688
  teamsVersion: 1,
9689
+ teamLaunchVersion: 1,
9594
9690
  host: os$1.hostname() + hostSuffix,
9595
9691
  platform: os$1.platform(),
9596
9692
  happyCliVersion: packageJson.version,
@@ -10861,7 +10957,8 @@ async function startDaemon() {
10861
10957
  });
10862
10958
  void apiMachine.updateMachineMetadata((current) => ({
10863
10959
  ...current ?? initialMachineMetadata,
10864
- teamsVersion: 1
10960
+ teamsVersion: 1,
10961
+ teamLaunchVersion: 1
10865
10962
  })).catch(() => logger.debug("[DAEMON RUN] Teams capability publication will retry on reconnect"));
10866
10963
  const cleanupAndShutdown = async (source, errorMessage) => {
10867
10964
  logger.debug(`[DAEMON RUN] Starting proper cleanup (source: ${source}, errorMessage: ${errorMessage})...`);
@@ -10900,6 +10997,113 @@ async function startDaemon() {
10900
10997
  }
10901
10998
  }
10902
10999
 
11000
+ const TEAMS_ADOPT_CAPABILITY = "teams-adopt-v1";
11001
+ const TeamAdoptSchema = z.object({
11002
+ requestId: z.string().min(1).max(128),
11003
+ name: z.string().trim().min(1).max(128),
11004
+ teamId: z.string().min(1).max(128).optional()
11005
+ }).strict();
11006
+ async function retainIntent(path, input) {
11007
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
11008
+ let file;
11009
+ try {
11010
+ file = await open$1(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384);
11011
+ } catch (error) {
11012
+ if (error.code !== "EEXIST") throw new Error("Cannot retain team connection request");
11013
+ }
11014
+ if (file) {
11015
+ try {
11016
+ await file.writeFile(JSON.stringify(input));
11017
+ await file.sync();
11018
+ } finally {
11019
+ await file.close();
11020
+ }
11021
+ return { fresh: true };
11022
+ }
11023
+ const existing = await open$1(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
11024
+ try {
11025
+ const stat = await existing.stat();
11026
+ if (!stat.isFile() || process.platform !== "win32" && (stat.mode & 63) !== 0 || typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new Error("Unsafe team connection receipt");
11027
+ const stored = JSON.parse(await existing.readFile("utf8"));
11028
+ const { resolvedTeamId, guidanceDelivered, ...request } = stored;
11029
+ const previous = TeamAdoptSchema.parse(request);
11030
+ if (JSON.stringify(previous) !== JSON.stringify(input)) throw new Error("Another team connection request already exists; retry the original request");
11031
+ if (resolvedTeamId !== void 0 && (typeof resolvedTeamId !== "string" || !resolvedTeamId)) throw new Error("Invalid connection receipt");
11032
+ if (guidanceDelivered !== void 0 && typeof guidanceDelivered !== "boolean") throw new Error("Invalid guidance receipt");
11033
+ return { resolvedTeamId, guidanceDelivered };
11034
+ } finally {
11035
+ await existing.close();
11036
+ }
11037
+ }
11038
+ async function retainTeam(path, input, resolvedTeamId, guidanceDelivered = false) {
11039
+ const temporary = `${path}.${randomUUID()}.new`;
11040
+ const file = await open$1(temporary, "wx", 384);
11041
+ try {
11042
+ await file.writeFile(JSON.stringify({ ...input, resolvedTeamId, guidanceDelivered }));
11043
+ await file.sync();
11044
+ } finally {
11045
+ await file.close();
11046
+ }
11047
+ try {
11048
+ await rename(temporary, path);
11049
+ } finally {
11050
+ await unlink(temporary).catch(() => {
11051
+ });
11052
+ }
11053
+ }
11054
+ function createTeamAdoption(sessionId) {
11055
+ const client = createTeamsClient(sessionId);
11056
+ let current;
11057
+ return (raw) => {
11058
+ const parsed = TeamAdoptSchema.safeParse(raw);
11059
+ if (!parsed.success) return { error: "Invalid team connection request" };
11060
+ if (process.env.VH_TEAM_SCOPE_FILE) return { error: "Assigned team members cannot create or join another team" };
11061
+ let path;
11062
+ try {
11063
+ path = `${teamScopePath(sessionId)}.adopt.json`;
11064
+ } catch {
11065
+ return { error: "A managed session identity is required" };
11066
+ }
11067
+ const input = parsed.data;
11068
+ const signature = JSON.stringify(input);
11069
+ if (current) {
11070
+ if (current.signature !== signature && current.safeToChange) current = void 0;
11071
+ else if (current.signature !== signature) return { error: "Another team connection request already exists; retry the original request" };
11072
+ }
11073
+ if (current) {
11074
+ const result = current.result;
11075
+ if (result && "error" in result) current = void 0;
11076
+ return result ?? { status: "pending", requestId: input.requestId };
11077
+ }
11078
+ const operation = { signature };
11079
+ current = operation;
11080
+ void (async () => {
11081
+ let intent;
11082
+ try {
11083
+ intent = await retainIntent(path, input);
11084
+ const connected = await client.initialize(input, { resumeAdoption: true, expectedTeamId: intent.resolvedTeamId, onTeamResolved: (teamId) => retainTeam(path, input, teamId, intent?.guidanceDelivered) });
11085
+ const instructions = `${TEAM_SKILL}
11086
+
11087
+ This session is now connected as the team lead. Call team_inspect to read the authoritative state before continuing the user's goal. Do not call team_create or team_join again.`;
11088
+ if (!intent.guidanceDelivered) {
11089
+ const key = await waitForSessionKey(sessionId, 1e4);
11090
+ await sendUserMessage(sessionId, key, instructions, "teams", { localId: `teams-adopt-${input.requestId}`, sentFrom: "team" });
11091
+ await retainTeam(path, input, connected.team.id, true);
11092
+ }
11093
+ operation.result = { status: "connected", teamId: connected.team.id, botId: connected.botId, instructions };
11094
+ } catch (error) {
11095
+ if (error instanceof TeamConnectionPreflightError && intent?.fresh && !intent.resolvedTeamId) {
11096
+ await unlink(path).catch(() => {
11097
+ });
11098
+ operation.safeToChange = true;
11099
+ }
11100
+ operation.result = { error: error instanceof Error ? error.message : "Team connection failed; retry the same request", ...operation.safeToChange ? { safeToChange: true } : {} };
11101
+ }
11102
+ })();
11103
+ return { status: "pending", requestId: input.requestId };
11104
+ };
11105
+ }
11106
+
10903
11107
  const PERSONAL_MEMORY_SOFT_LIMIT_CHARS = 2e3;
10904
11108
  function headingTitle(line) {
10905
11109
  const m = /^##(?!#)\s*(.*?)\s*$/.exec(line);
@@ -11543,6 +11747,11 @@ async function startHappyServer(client, options) {
11543
11747
  resolve(new URL(`http://127.0.0.1:${addr.port}`));
11544
11748
  });
11545
11749
  });
11750
+ const adoptionEnabled = !process.env.VH_TEAM_SCOPE_FILE;
11751
+ if (adoptionEnabled) {
11752
+ client.rpcHandlerManager.registerHandler("teams-adopt", createTeamAdoption(client.sessionId));
11753
+ client.updateMetadata((metadata) => ({ ...metadata, capabilities: [.../* @__PURE__ */ new Set([...metadata.capabilities ?? [], TEAMS_ADOPT_CAPABILITY])] }));
11754
+ }
11546
11755
  logger.debug(`[happyMCP] server:ready sessionId=${client.sessionId} url=${baseUrl.toString()}`);
11547
11756
  return {
11548
11757
  url: baseUrl.toString(),
@@ -11556,6 +11765,7 @@ async function startHappyServer(client, options) {
11556
11765
  ],
11557
11766
  stop: () => {
11558
11767
  logger.debug(`[happyMCP] server:stop sessionId=${client.sessionId}`);
11768
+ if (adoptionEnabled) client.rpcHandlerManager.unregisterHandler("teams-adopt");
11559
11769
  server.close();
11560
11770
  }
11561
11771
  };
@@ -11812,7 +12022,7 @@ function registerSideQuestionHandler(rpc, deps) {
11812
12022
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
11813
12023
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
11814
12024
  const run = deps.run ?? (async (input) => {
11815
- const { query } = await import('./index-D-BgvQi5.mjs');
12025
+ const { query } = await import('./index-CWPgxeLD.mjs');
11816
12026
  return runSideQuestion(query, input);
11817
12027
  });
11818
12028
  const slots = /* @__PURE__ */ new Map();
@@ -15873,6 +16083,7 @@ function buildCodexTurnPrompt(opts) {
15873
16083
  parts.push(opts.message);
15874
16084
  if (opts.includeTitleInstruction) {
15875
16085
  parts.push(CHANGE_TITLE_INSTRUCTION);
16086
+ parts.push(BUILTIN_TODO_DISCOVERY);
15876
16087
  }
15877
16088
  return parts.join("\n\n");
15878
16089
  }
@@ -16765,7 +16976,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16765
16976
  process.exit(0);
16766
16977
  } else if (subcommand === "install-terminal-hooks") {
16767
16978
  try {
16768
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-Z1Jb6yjb.mjs');
16979
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-cpuDExTT.mjs');
16769
16980
  const command = parseTerminalHooksArgs(args.slice(1));
16770
16981
  if (command.action === "help") {
16771
16982
  console.log(TERMINAL_HOOKS_HELP);
@@ -16782,7 +16993,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16782
16993
  }
16783
16994
  } else if (subcommand === "spawn") {
16784
16995
  try {
16785
- const { handleSpawnCommand } = await import('./spawn-j9KBfj4e.mjs');
16996
+ const { handleSpawnCommand } = await import('./spawn-DMx1JKXo.mjs');
16786
16997
  await handleSpawnCommand(args.slice(1));
16787
16998
  } catch (error) {
16788
16999
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16794,7 +17005,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16794
17005
  return;
16795
17006
  } else if (subcommand === "sessions") {
16796
17007
  try {
16797
- const { handleSessionsCommand } = await import('./sessions-Ba2oRTmw.mjs');
17008
+ const { handleSessionsCommand } = await import('./sessions-D06cLJ7f.mjs');
16798
17009
  await handleSessionsCommand(args.slice(1));
16799
17010
  } catch (error) {
16800
17011
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16806,7 +17017,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16806
17017
  return;
16807
17018
  } else if (subcommand === "send") {
16808
17019
  try {
16809
- const { handleSendCommand } = await import('./send-BevgS1mf.mjs');
17020
+ const { handleSendCommand } = await import('./send-D2xX42zg.mjs');
16810
17021
  await handleSendCommand(args.slice(1));
16811
17022
  } catch (error) {
16812
17023
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16912,9 +17123,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16912
17123
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
16913
17124
  const projectId = args[3];
16914
17125
  try {
16915
- const { saveGoogleCloudProjectToConfig } = await import('./config-CZNIz3W0.mjs');
16916
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-FjbPJPsF.mjs').then(function (n) { return n.W; });
16917
- const { ApiClient: ApiClient2 } = await import('./types-DLDkfrTC.mjs').then(function (n) { return n.X; });
17126
+ const { saveGoogleCloudProjectToConfig } = await import('./config-DotNknpP.mjs');
17127
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-D2XM3dvN.mjs').then(function (n) { return n.W; });
17128
+ const { ApiClient: ApiClient2 } = await import('./types-Df_vXvzD.mjs').then(function (n) { return n._; });
16918
17129
  let userEmail = void 0;
16919
17130
  try {
16920
17131
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -16945,7 +17156,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16945
17156
  }
16946
17157
  if (geminiSubcommand === "project" && args[2] === "get") {
16947
17158
  try {
16948
- const { readGeminiLocalConfig } = await import('./config-CZNIz3W0.mjs');
17159
+ const { readGeminiLocalConfig } = await import('./config-DotNknpP.mjs');
16949
17160
  const config = readGeminiLocalConfig();
16950
17161
  if (config.googleCloudProject) {
16951
17162
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -16985,7 +17196,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16985
17196
  process.exit(0);
16986
17197
  }
16987
17198
  try {
16988
- const { runGemini } = await import('./runGemini-Bu49s_bN.mjs');
17199
+ const { runGemini } = await import('./runGemini-BY7uA-Uk.mjs');
16989
17200
  let startedBy = void 0;
16990
17201
  for (let i = 1; i < args.length; i++) {
16991
17202
  if (args[i] === "--started-by") {
@@ -17015,7 +17226,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17015
17226
  }
17016
17227
  };
17017
17228
  try {
17018
- const { runAcp, resolveAcpAgentConfig } = await import('./index-Jrk6ud3g.mjs');
17229
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-C2kblG8-.mjs');
17019
17230
  const parsed = parsePiRunnerArgs(args.slice(1));
17020
17231
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17021
17232
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17042,7 +17253,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17042
17253
  return;
17043
17254
  } else if (subcommand === "acp") {
17044
17255
  try {
17045
- const { runAcp, resolveAcpAgentConfig } = await import('./index-Jrk6ud3g.mjs');
17256
+ const { runAcp, resolveAcpAgentConfig } = await import('./index-C2kblG8-.mjs');
17046
17257
  let startedBy = void 0;
17047
17258
  let verbose = false;
17048
17259
  const acpArgs = [];
@@ -17082,7 +17293,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17082
17293
  return;
17083
17294
  } else if (subcommand === "openclaw") {
17084
17295
  try {
17085
- const { runOpenClaw } = await import('./runOpenClaw-CznAYPCh.mjs');
17296
+ const { runOpenClaw } = await import('./runOpenClaw-Be_U4DHZ.mjs');
17086
17297
  let startedBy = void 0;
17087
17298
  let verbose = false;
17088
17299
  let gatewayUrl;
@@ -17131,9 +17342,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17131
17342
  process.exit(1);
17132
17343
  }
17133
17344
  return;
17345
+ } else if (subcommand === "todo") {
17346
+ try {
17347
+ const { handleTodoCommand } = await import('./todo-hKSIY23F.mjs');
17348
+ await handleTodoCommand(args.slice(1));
17349
+ } catch (error) {
17350
+ console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
17351
+ process.exitCode = 1;
17352
+ }
17353
+ return;
17134
17354
  } else if (subcommand === "teams") {
17135
17355
  try {
17136
- const { handleTeamsCommand } = await import('./teams-C0ggJTJ-.mjs');
17356
+ const { handleTeamsCommand } = await import('./teams-D7ubdfTY.mjs');
17137
17357
  await handleTeamsCommand(args.slice(1));
17138
17358
  } catch (error) {
17139
17359
  console.error(error instanceof Error ? error.message : "Teams command failed");
@@ -17142,7 +17362,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17142
17362
  return;
17143
17363
  } else if (subcommand === "mcp" && args.length === 1) {
17144
17364
  try {
17145
- const { handleMcpCommand } = await import('./mcp-zqPJ94Bk.mjs');
17365
+ const { handleMcpCommand } = await import('./mcp-CpUrsnoC.mjs');
17146
17366
  await handleMcpCommand();
17147
17367
  } catch (error) {
17148
17368
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -17369,6 +17589,7 @@ ${chalk.bold("Usage:")}
17369
17589
  Install optional Claude terminal mirror hooks
17370
17590
  (add --remove to uninstall them)
17371
17591
  very-happy connect Connect AI vendor API keys
17592
+ very-happy todo Built-in Todos and official agent skill (todo --help)
17372
17593
  very-happy sandbox Configure and manage OS-level sandboxing
17373
17594
  very-happy notify Send push notification
17374
17595
  very-happy mcp Stdio MCP server exposing copy_to_clipboard for a
@@ -17503,4 +17724,4 @@ ${chalk.bold("Examples:")}
17503
17724
  }
17504
17725
  }
17505
17726
 
17506
- export { ALLOWED_SPAWN_PERMISSION_MODES as A, hashObject as B, startHappyServer as C, DEFAULT_RECENT_LIMIT as D, BasePermissionHandler as E, writeSessionModeFile as F, GEMINI_MODEL_ENV as G, normalizeAcpPermissionMode as H, removeSessionModeFile as I, GEMINI_API_KEY_ENV as J, GOOGLE_API_KEY_ENV as K, BaseReasoningProcessor as L, MAX_READ_LIMIT as M, MessageBuffer as N, CHANGE_TITLE_INSTRUCTION as O, SPAWN_AGENTS as S, TitleGenerator as T, sessionWebUrl as a, sanitizeSpawnPermissionMode as b, checkIfDaemonRunningAndCleanupStaleState as c, sendUserMessage as d, listSessions as e, stopSession as f, resolvePermissionRequest as g, archiveSession as h, isValidSpawnOrigin as i, DEFAULT_GEMINI_MODEL as j, setTerminalTitleViaDaemon as k, listAccountSessions as l, registerAssistantSessionTools as m, initialMachineMetadata as n, createSessionMetadata as o, pushClipboardViaDaemon as p, query as q, readSessionTranscript as r, spawnDaemonSession as s, tmuxSupportsSessionEnv as t, setupOfflineReconnection as u, notifyDaemonSessionStarted as v, waitForSessionKey as w, MessageQueue2 as x, registerKillSessionHandler as y, StreamRelay as z };
17727
+ export { ALLOWED_SPAWN_PERMISSION_MODES as A, hashObject as B, startHappyServer as C, DEFAULT_RECENT_LIMIT as D, TitleGenerator as E, BasePermissionHandler as F, GEMINI_MODEL_ENV as G, writeSessionModeFile as H, normalizeAcpPermissionMode as I, removeSessionModeFile as J, GEMINI_API_KEY_ENV as K, GOOGLE_API_KEY_ENV as L, MAX_READ_LIMIT as M, BaseReasoningProcessor as N, MessageBuffer as O, PI_TEAMS_EXTENSION as P, CHANGE_TITLE_INSTRUCTION as Q, SPAWN_AGENTS as S, TEAM_SKILL as T, sessionWebUrl as a, sanitizeSpawnPermissionMode as b, checkIfDaemonRunningAndCleanupStaleState as c, sendUserMessage as d, listSessions as e, stopSession as f, resolvePermissionRequest as g, archiveSession as h, isValidSpawnOrigin as i, DEFAULT_GEMINI_MODEL as j, setTerminalTitleViaDaemon as k, listAccountSessions as l, registerAssistantSessionTools as m, initialMachineMetadata as n, createSessionMetadata as o, pushClipboardViaDaemon as p, query as q, readSessionTranscript as r, spawnDaemonSession as s, tmuxSupportsSessionEnv as t, setupOfflineReconnection as u, notifyDaemonSessionStarted as v, waitForSessionKey as w, MessageQueue2 as x, registerKillSessionHandler as y, StreamRelay as z };
@@ -1,13 +1,12 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-DOQGItcS.mjs';
2
- export { a as DEFAULT_IDLE_TIMEOUT_MS, b as DEFAULT_TOOL_CALL_TIMEOUT_MS, e as extractErrorDetail, f as formatDuration, c as formatDurationMinutes, h as handleAgentMessageChunk, d as handleAgentThoughtChunk, g as handleLegacyMessageChunk, i as handlePlanUpdate, j as handleThinkingUpdate, k as handleToolCall, l as handleToolCallUpdate, p as parseArgsFromContent } from './AcpBackend-DOQGItcS.mjs';
3
- import { c as configuration, f as readSettings, g as encodeBase64, l as logger } from './limits-FjbPJPsF.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-D4WkNsRs.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-B9_vB_mY.mjs';
2
+ export { a as DEFAULT_IDLE_TIMEOUT_MS, b as DEFAULT_TOOL_CALL_TIMEOUT_MS, e as extractErrorDetail, f as formatDuration, c as formatDurationMinutes, h as handleAgentMessageChunk, d as handleAgentThoughtChunk, g as handleLegacyMessageChunk, i as handlePlanUpdate, j as handleThinkingUpdate, k as handleToolCall, l as handleToolCallUpdate, p as parseArgsFromContent } from './AcpBackend-B9_vB_mY.mjs';
3
+ import { c as configuration, g as readSettings, i as encodeBase64, l as logger } from './limits-D2XM3dvN.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-HsxKXreC.mjs';
5
+ import { g as connectionState, A as ApiClient, p as projectPath, h as BUILTIN_TODO_DISCOVERY } from './types-Df_vXvzD.mjs';
5
6
  import { mkdir, chmod, writeFile, rename } from 'node:fs/promises';
6
7
  import { randomUUID } from 'node:crypto';
7
8
  import { join } from 'node:path';
8
- import { P as PI_TEAMS_EXTENSION } from './resources-D-mn7ptO.mjs';
9
- import { f as connectionState, A as ApiClient, p as projectPath } from './types-DLDkfrTC.mjs';
10
- import { n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, z as StreamRelay, x as MessageQueue2, B as hashObject, C as startHappyServer, T as TitleGenerator, y as registerKillSessionHandler, E as BasePermissionHandler, F as writeSessionModeFile, H as normalizeAcpPermissionMode, I as removeSessionModeFile } from './index-CrSifgJM.mjs';
9
+ import { P as PI_TEAMS_EXTENSION, n as initialMachineMetadata, o as createSessionMetadata, u as setupOfflineReconnection, v as notifyDaemonSessionStarted, z as StreamRelay, x as MessageQueue2, B as hashObject, C as startHappyServer, E as TitleGenerator, y as registerKillSessionHandler, F as BasePermissionHandler, H as writeSessionModeFile, I as normalizeAcpPermissionMode, J as removeSessionModeFile } from './index-B1qzJIMw.mjs';
11
10
  import 'node:child_process';
12
11
  import '@agentclientprotocol/sdk';
13
12
  import 'node:fs';
@@ -33,7 +32,7 @@ import 'fs';
33
32
  import 'node:readline';
34
33
  import '@anthropic-ai/sandbox-runtime';
35
34
  import 'expo-server-sdk';
36
- import './agentGuidance-QpYwykb9.mjs';
35
+ import './agentGuidance-CTyzeLCQ.mjs';
37
36
  import 'ink';
38
37
  import 'react';
39
38
  import '@anthropic-ai/claude-agent-sdk';
@@ -1074,6 +1073,7 @@ async function runAcp(opts) {
1074
1073
  logAcp("muted", `Outgoing models from ${opts.agentName}: not reported yet`);
1075
1074
  }
1076
1075
  }
1076
+ let todoDiscoverySent = false;
1077
1077
  while (!shouldExit) {
1078
1078
  const waitSignal = abortController.signal;
1079
1079
  const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
@@ -1100,7 +1100,11 @@ async function runAcp(opts) {
1100
1100
  if (typeof batch.mode.model === "string" && batch.mode.model.length > 0) {
1101
1101
  await switchModelIfRequested(batch.mode.model);
1102
1102
  }
1103
- await backend.sendPrompt(acpSessionId, batch.message);
1103
+ const prompt = todoDiscoverySent ? batch.message : `${batch.message}
1104
+
1105
+ ${BUILTIN_TODO_DISCOVERY}`;
1106
+ await backend.sendPrompt(acpSessionId, prompt);
1107
+ todoDiscoverySent = true;
1104
1108
  await turnEnded;
1105
1109
  sendEnvelopes(sessionManager.endTurn("completed"));
1106
1110
  streamRelay.endTurn();
@@ -1,11 +1,11 @@
1
- import { q as query } from './index-CrSifgJM.mjs';
1
+ import { q as query } from './index-B1qzJIMw.mjs';
2
2
  export { AbortError } from '@anthropic-ai/claude-agent-sdk';
3
3
  import 'chalk';
4
4
  import 'node:os';
5
5
  import 'node:crypto';
6
- import './types-DLDkfrTC.mjs';
6
+ import './types-Df_vXvzD.mjs';
7
7
  import 'axios';
8
- import './limits-FjbPJPsF.mjs';
8
+ import './limits-D2XM3dvN.mjs';
9
9
  import 'node:fs/promises';
10
10
  import 'node:fs';
11
11
  import 'node:path';
@@ -29,7 +29,7 @@ import 'fs';
29
29
  import 'node:readline';
30
30
  import '@anthropic-ai/sandbox-runtime';
31
31
  import 'expo-server-sdk';
32
- import './agentGuidance-QpYwykb9.mjs';
32
+ import './agentGuidance-CTyzeLCQ.mjs';
33
33
  import 'ink';
34
34
  import 'react';
35
35
  import 'ps-list';