very-happy-cli 0.2.126 → 0.2.128

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-C9eu6CDu.cjs → AcpBackend-BYU6J2n0.cjs} +2 -2
  2. package/dist/{AcpBackend-BzGpBAZ0.mjs → AcpBackend-D7987ody.mjs} +2 -2
  3. package/dist/{AcpSessionManager-B223yT78.mjs → AcpSessionManager-BwrjVZyn.mjs} +1 -1
  4. package/dist/{AcpSessionManager-DQ1e_0gc.cjs → AcpSessionManager-artXJgqb.cjs} +1 -1
  5. package/dist/{agentGuidance-Tm8Z5HYp.cjs → agentGuidance-B1xYgHx6.cjs} +63 -33
  6. package/dist/{agentGuidance-BR0OFumQ.mjs → agentGuidance-BPGPaNlo.mjs} +62 -35
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-Bjd1nG0P.mjs → config-DSBE0Az0.mjs} +4 -4
  10. package/dist/{config-C0mofvCH.cjs → config-Dmkzs3o4.cjs} +4 -4
  11. package/dist/{index-BP56asuS.mjs → index-35FahgEU.mjs} +4 -4
  12. package/dist/{index-HW-KdG6W.cjs → index-CJmXLSQc.cjs} +4 -4
  13. package/dist/{index-D4eCRu9x.mjs → index-CfM6ewd8.mjs} +249 -27
  14. package/dist/{index-Cw9Zj1Zr.cjs → index-D_YVlpsW.cjs} +254 -30
  15. package/dist/{index-CV6sAa4a.mjs → index-m0Dnccc_.mjs} +13 -9
  16. package/dist/{index-CMbKpntk.cjs → index-vyTJsT_R.cjs} +13 -9
  17. package/dist/index.cjs +4 -4
  18. package/dist/index.mjs +4 -4
  19. package/dist/{installTerminalHooks-CNp1LExn.cjs → installTerminalHooks-C1zwiJJ2.cjs} +4 -4
  20. package/dist/{installTerminalHooks-D-CNSalI.mjs → installTerminalHooks-ClrFe2fA.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-DlbYYtOw.mjs → limits-BNTLh2lw.mjs} +9 -9
  26. package/dist/{limits-DG1OQnCb.cjs → limits-CrwtFbi8.cjs} +7 -7
  27. package/dist/{mcp-BtZoIIIM.cjs → mcp-Bc_mmJt8.cjs} +4 -4
  28. package/dist/{mcp-D3oV_dH-.mjs → mcp-BsohER5A.mjs} +4 -4
  29. package/dist/{runGemini-BfCSOPL1.cjs → runGemini-B2GesRWm.cjs} +6 -6
  30. package/dist/{runGemini-BJ8bU1pP.mjs → runGemini-B9S85Vms.mjs} +6 -6
  31. package/dist/{runOpenClaw-DTgOpJnO.mjs → runOpenClaw-Byhq-B1y.mjs} +5 -5
  32. package/dist/{runOpenClaw-CBlHXEAT.cjs → runOpenClaw-DCK-R8qe.cjs} +5 -5
  33. package/dist/{send-CDK1bZFj.cjs → send-CPdT1nOg.cjs} +4 -4
  34. package/dist/{send-9duZlpGX.mjs → send-DQS1zNWp.mjs} +4 -4
  35. package/dist/{sessions-CNkLCorF.cjs → sessions-DC0IKaD9.cjs} +4 -4
  36. package/dist/{sessions-jAFOkKvW.mjs → sessions-DIOdWnPe.mjs} +4 -4
  37. package/dist/{spawn-BG5oHi_f.mjs → spawn-DV4pIAHZ.mjs} +4 -4
  38. package/dist/{spawn-J-A5WGZ7.cjs → spawn-DrnCW2QK.cjs} +4 -4
  39. package/dist/{teams-MgWeyutG.cjs → teams-CwXEJs9I.cjs} +41 -6
  40. package/dist/{teams-Cuqsk7Ty.mjs → teams-DlTHOiCk.mjs} +38 -3
  41. package/dist/todo-BwXURAbQ.mjs +128 -0
  42. package/dist/todo-DrME15xq.cjs +132 -0
  43. package/dist/{types-n_VWu9qe.mjs → types-CFs8FmiY.mjs} +318 -6
  44. package/dist/{types-P5deH8Ls.cjs → types-DfatCoQL.cjs} +327 -12
  45. package/package.json +7 -7
  46. package/dist/resources-CVl7NdOB.cjs +0 -83
  47. package/dist/resources-D-mn7ptO.mjs +0 -80
@@ -3,13 +3,13 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var api = require('./types-P5deH8Ls.cjs');
7
- var persistence = require('./limits-DG1OQnCb.cjs');
6
+ var api = require('./types-DfatCoQL.cjs');
7
+ var persistence = require('./limits-CrwtFbi8.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 agentGuidance = require('./agentGuidance-Tm8Z5HYp.cjs');
12
+ var agentGuidance = require('./agentGuidance-B1xYgHx6.cjs');
13
13
  var promises = require('node:fs/promises');
14
14
  var fs = require('fs/promises');
15
15
  var ink = require('ink');
@@ -669,7 +669,7 @@ function shouldIncludeCoAuthoredBy() {
669
669
 
670
670
  const BASE_SYSTEM_PROMPT = (() => agentGuidance.trimIdent(`
671
671
  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.
672
- `))() + "\n\n" + agentGuidance.AGENT_GUIDANCE;
672
+ `))() + "\n\n" + agentGuidance.AGENT_GUIDANCE + "\n\n" + api.BUILTIN_TODO_DISCOVERY;
673
673
  const CO_AUTHORED_CREDITS = (() => agentGuidance.trimIdent(`
674
674
  When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
675
675
 
@@ -4764,6 +4764,7 @@ function createSessionMetadata(opts) {
4764
4764
  version: persistence.packageJson.version,
4765
4765
  os: os.platform(),
4766
4766
  machineId: opts.machineId,
4767
+ ...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
4767
4768
  homeDir: os.homedir(),
4768
4769
  happyHomeDir: persistence.configuration.happyHomeDir,
4769
4770
  happyLibDir: api.projectPath(),
@@ -5853,7 +5854,8 @@ async function sendUserMessage(sessionId, persisted, text, client, options = {})
5853
5854
  text
5854
5855
  },
5855
5856
  meta: {
5856
- sentFrom: options.sentFrom ?? "cli"
5857
+ sentFrom: options.sentFrom ?? "cli",
5858
+ ...options.model ? { model: options.model } : {}
5857
5859
  }
5858
5860
  };
5859
5861
  const encrypted = persistence.encodeBase64(persistence.encrypt(
@@ -7675,7 +7677,7 @@ function parseJsonObject(text) {
7675
7677
  return null;
7676
7678
  }
7677
7679
  }
7678
- 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-Cw9Zj1Zr.cjs', document.baseURI).href))).resolve(s)) {
7680
+ 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-D_YVlpsW.cjs', document.baseURI).href))).resolve(s)) {
7679
7681
  const platformArch = `${platform}-${arch}`;
7680
7682
  const candidates = [
7681
7683
  `@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
@@ -8822,6 +8824,100 @@ async function claimSessionOrExit(happySessionId, options) {
8822
8824
  process.on("exit", () => releaseSessionLock(happySessionId));
8823
8825
  }
8824
8826
 
8827
+ const TEAM_SKILL = `---
8828
+ name: very-happy-teams
8829
+ 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.
8830
+ ---
8831
+
8832
+ # Very Happy Agent Teams
8833
+
8834
+ 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.
8835
+
8836
+ 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.
8837
+
8838
+ 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.
8839
+
8840
+ 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.
8841
+
8842
+ 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.
8843
+
8844
+ 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.
8845
+
8846
+ 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.
8847
+
8848
+ 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.
8849
+
8850
+ 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.
8851
+ `;
8852
+ const PI_TEAMS_EXTENSION = `// Managed by Very Happy Agent Teams.
8853
+ export default async function(pi) {
8854
+ const endpoint = process.env.HAPPY_MCP_URL;
8855
+ if (!endpoint) return;
8856
+ const url = new URL(endpoint);
8857
+ if (!['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.protocol !== 'http:') throw new Error('Very Happy MCP must be local');
8858
+ const {readFile} = await import('node:fs/promises');
8859
+ const {join} = await import('node:path');
8860
+ const {homedir} = await import('node:os');
8861
+ pi.on('tool_call', async (event, ctx) => {
8862
+ let mode = process.env.HAPPY_PERMISSION_MODE || 'default';
8863
+ const sid = process.env.HAPPY_SESSION_ID;
8864
+ if (sid && /^[a-zA-Z0-9_-]+$/.test(sid)) {
8865
+ try { mode = JSON.parse(await readFile(join(process.env.HAPPY_HOME_DIR || join(homedir(), '.happy'), 'session-modes', sid + '.json'), 'utf8')).permissionMode; }
8866
+ catch (error) { if (error.code !== 'ENOENT') mode = 'default'; }
8867
+ }
8868
+ if (mode === 'yolo') mode = 'bypassPermissions';
8869
+ if (!['default','plan','acceptEdits','bypassPermissions'].includes(mode)) mode = 'default';
8870
+ if (mode === 'bypassPermissions') return;
8871
+ if (['read','ls','find','grep','team_inspect','change_title','report_progress'].includes(event.toolName)) return;
8872
+ if (mode === 'plan') return {block:true,reason:'Plan mode allows read-only tools'};
8873
+ if (mode === 'acceptEdits' && ['write','edit'].includes(event.toolName)) return;
8874
+ try {
8875
+ const allowed = await ctx.ui.confirm('Allow ' + event.toolName + '?', JSON.stringify(event.input || {}).slice(0,4000));
8876
+ if (allowed === true) return;
8877
+ } catch {}
8878
+ return {block:true,reason:'Tool requires explicit approval'};
8879
+ });
8880
+ let nextId = 0;
8881
+ async function rpc(method, params) {
8882
+ const requestId = ++nextId;
8883
+ 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) });
8884
+ if (!response.ok) throw new Error('Very Happy tools unavailable (HTTP ' + response.status + ')');
8885
+ const body = await response.text();
8886
+ let data;
8887
+ if ((response.headers.get('content-type') || '').includes('text/event-stream')) {
8888
+ for (const event of body.split(/\\r?\\n\\r?\\n/)) {
8889
+ const payload = event.split(/\\r?\\n/).filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\\n');
8890
+ if (payload) { const candidate = JSON.parse(payload); if (candidate.id === requestId) data = candidate; }
8891
+ }
8892
+ } else data = JSON.parse(body);
8893
+ if (!data || data.error) throw new Error('Very Happy tool request failed');
8894
+ return data.result;
8895
+ }
8896
+ await rpc('initialize', {protocolVersion:'2024-11-05',capabilities:{},clientInfo:{name:'very-happy-pi-teams',version:'1'}});
8897
+ const available = await rpc('tools/list', {});
8898
+ for (const tool of available.tools || []) {
8899
+ pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
8900
+ async execute(_id, args) { const result = await rpc('tools/call', {name:tool.name,arguments:args}); return {content:result.content,details:{isError:!!result.isError}}; }
8901
+ });
8902
+ }
8903
+ }
8904
+ `;
8905
+
8906
+ function teamInitialPrompt(goal, root, directory) {
8907
+ 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.";
8908
+ return `${TEAM_SKILL}
8909
+
8910
+ ## Your role
8911
+ ${role}
8912
+
8913
+ ## Working directory
8914
+ ${directory ? JSON.stringify(directory) : "Use your current managed session working directory."}
8915
+ Delegate from this working directory unless a different repository was explicitly requested.
8916
+
8917
+ ## Assigned goal
8918
+ ${goal}`;
8919
+ }
8920
+
8825
8921
  const MAX_LINE_CHARS = 500;
8826
8922
  const MAX_ARGS_CHARS = 160;
8827
8923
  function truncateText(text, max = MAX_LINE_CHARS) {
@@ -9403,7 +9499,7 @@ function createTeamWorker(deps) {
9403
9499
  let knownSessionIds = /* @__PURE__ */ new Set();
9404
9500
  const action = async (teamId, requestId, value) => (await http.post(`/v1/teams/${encodeURIComponent(teamId)}/actions`, { requestId, action: value })).data;
9405
9501
  async function complete(op, receipt) {
9406
- await action(op.teamId, `complete-${op.id}`, { type: "complete-operation", operationId: op.id, machineId: deps.machineId, claimId: receipt.claimId, ...receipt.sessionId ? { sessionId: receipt.sessionId } : {} });
9502
+ 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 } : {} });
9407
9503
  writeReceipt(home, { ...receipt, phase: "completed" });
9408
9504
  }
9409
9505
  async function execute(op) {
@@ -9416,7 +9512,7 @@ function createTeamWorker(deps) {
9416
9512
  }
9417
9513
  return;
9418
9514
  }
9419
- const claimed = await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId });
9515
+ const claimed = await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId, ...op.teamLaunchVersion === 1 ? { teamLaunchVersion: 1 } : {} });
9420
9516
  if (!claimed.operation?.claimId) throw new Error("Operation claim did not return a receipt");
9421
9517
  op = claimed.operation;
9422
9518
  credential = claimed.credential;
@@ -9427,7 +9523,7 @@ function createTeamWorker(deps) {
9427
9523
  try {
9428
9524
  if (op.type === "spawn") {
9429
9525
  if (receipt.phase === "prepared") {
9430
- if (!credential) credential = (await action(op.teamId, `claim-${op.id}`, { type: "claim-operation", operationId: op.id, machineId: deps.machineId })).credential;
9526
+ 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;
9431
9527
  if (!credential) throw new Error("No scoped credential for worker");
9432
9528
  const current = (await http.get(`/v1/teams/${encodeURIComponent(op.teamId)}`)).data.team;
9433
9529
  const task = current.tasks.find((t) => t.id === op.taskId);
@@ -9467,7 +9563,7 @@ function createTeamWorker(deps) {
9467
9563
  return;
9468
9564
  }
9469
9565
  const key = await waitForSessionKey(receipt.sessionId, 1e4);
9470
- await sendUserMessage(receipt.sessionId, key, op.prompt, "teams", { localId: `teams-initial-${op.id}`, sentFrom: "team" });
9566
+ 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 } : {} });
9471
9567
  receipt = { ...receipt, phase: "delivered" };
9472
9568
  writeReceipt(home, receipt);
9473
9569
  }
@@ -9569,7 +9665,7 @@ Use the teams tools to inspect current task state before acting. This message is
9569
9665
  const status = axios.isAxiosError(error) ? error.response?.status : void 0;
9570
9666
  if (status !== 404) deps.log("Teams schedule advancement unavailable; regular reconciliation continues");
9571
9667
  }
9572
- const { data } = await http.get("/v1/teams/operations", { params: { machineId: deps.machineId, schedulesVersion: 1 } });
9668
+ const { data } = await http.get("/v1/teams/operations", { params: { machineId: deps.machineId, schedulesVersion: 1, teamLaunchVersion: 1 } });
9573
9669
  knownSessionIds = new Set((data.teams ?? []).flatMap((team) => team.bots.flatMap((bot) => bot.sessionId ? [bot.sessionId] : [])));
9574
9670
  for (const op of data.operations) {
9575
9671
  if (closed) break;
@@ -9613,6 +9709,7 @@ const hostSuffix = process.env.HAPPY_VARIANT === "dev" ? "-dev" : "";
9613
9709
  const startupCliAvailability = api.detectCLIAvailability();
9614
9710
  const initialMachineMetadata = {
9615
9711
  teamsVersion: 1,
9712
+ teamLaunchVersion: 1,
9616
9713
  host: os$1.hostname() + hostSuffix,
9617
9714
  platform: os$1.platform(),
9618
9715
  happyCliVersion: persistence.packageJson.version,
@@ -10883,7 +10980,8 @@ async function startDaemon() {
10883
10980
  });
10884
10981
  void apiMachine.updateMachineMetadata((current) => ({
10885
10982
  ...current ?? initialMachineMetadata,
10886
- teamsVersion: 1
10983
+ teamsVersion: 1,
10984
+ teamLaunchVersion: 1
10887
10985
  })).catch(() => persistence.logger.debug("[DAEMON RUN] Teams capability publication will retry on reconnect"));
10888
10986
  const cleanupAndShutdown = async (source, errorMessage) => {
10889
10987
  persistence.logger.debug(`[DAEMON RUN] Starting proper cleanup (source: ${source}, errorMessage: ${errorMessage})...`);
@@ -10922,6 +11020,113 @@ async function startDaemon() {
10922
11020
  }
10923
11021
  }
10924
11022
 
11023
+ const TEAMS_ADOPT_CAPABILITY = "teams-adopt-v1";
11024
+ const TeamAdoptSchema = z.z.object({
11025
+ requestId: z.z.string().min(1).max(128),
11026
+ name: z.z.string().trim().min(1).max(128),
11027
+ teamId: z.z.string().min(1).max(128).optional()
11028
+ }).strict();
11029
+ async function retainIntent(path$1, input) {
11030
+ await promises.mkdir(path.dirname(path$1), { recursive: true, mode: 448 });
11031
+ let file;
11032
+ try {
11033
+ file = await promises.open(path$1, node_fs.constants.O_WRONLY | node_fs.constants.O_CREAT | node_fs.constants.O_EXCL, 384);
11034
+ } catch (error) {
11035
+ if (error.code !== "EEXIST") throw new Error("Cannot retain team connection request");
11036
+ }
11037
+ if (file) {
11038
+ try {
11039
+ await file.writeFile(JSON.stringify(input));
11040
+ await file.sync();
11041
+ } finally {
11042
+ await file.close();
11043
+ }
11044
+ return { fresh: true };
11045
+ }
11046
+ const existing = await promises.open(path$1, node_fs.constants.O_RDONLY | (node_fs.constants.O_NOFOLLOW ?? 0));
11047
+ try {
11048
+ const stat = await existing.stat();
11049
+ 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");
11050
+ const stored = JSON.parse(await existing.readFile("utf8"));
11051
+ const { resolvedTeamId, guidanceDelivered, ...request } = stored;
11052
+ const previous = TeamAdoptSchema.parse(request);
11053
+ if (JSON.stringify(previous) !== JSON.stringify(input)) throw new Error("Another team connection request already exists; retry the original request");
11054
+ if (resolvedTeamId !== void 0 && (typeof resolvedTeamId !== "string" || !resolvedTeamId)) throw new Error("Invalid connection receipt");
11055
+ if (guidanceDelivered !== void 0 && typeof guidanceDelivered !== "boolean") throw new Error("Invalid guidance receipt");
11056
+ return { resolvedTeamId, guidanceDelivered };
11057
+ } finally {
11058
+ await existing.close();
11059
+ }
11060
+ }
11061
+ async function retainTeam(path, input, resolvedTeamId, guidanceDelivered = false) {
11062
+ const temporary = `${path}.${node_crypto.randomUUID()}.new`;
11063
+ const file = await promises.open(temporary, "wx", 384);
11064
+ try {
11065
+ await file.writeFile(JSON.stringify({ ...input, resolvedTeamId, guidanceDelivered }));
11066
+ await file.sync();
11067
+ } finally {
11068
+ await file.close();
11069
+ }
11070
+ try {
11071
+ await promises.rename(temporary, path);
11072
+ } finally {
11073
+ await promises.unlink(temporary).catch(() => {
11074
+ });
11075
+ }
11076
+ }
11077
+ function createTeamAdoption(sessionId) {
11078
+ const client = agentGuidance.createTeamsClient(sessionId);
11079
+ let current;
11080
+ return (raw) => {
11081
+ const parsed = TeamAdoptSchema.safeParse(raw);
11082
+ if (!parsed.success) return { error: "Invalid team connection request" };
11083
+ if (process.env.VH_TEAM_SCOPE_FILE) return { error: "Assigned team members cannot create or join another team" };
11084
+ let path;
11085
+ try {
11086
+ path = `${agentGuidance.teamScopePath(sessionId)}.adopt.json`;
11087
+ } catch {
11088
+ return { error: "A managed session identity is required" };
11089
+ }
11090
+ const input = parsed.data;
11091
+ const signature = JSON.stringify(input);
11092
+ if (current) {
11093
+ if (current.signature !== signature && current.safeToChange) current = void 0;
11094
+ else if (current.signature !== signature) return { error: "Another team connection request already exists; retry the original request" };
11095
+ }
11096
+ if (current) {
11097
+ const result = current.result;
11098
+ if (result && "error" in result) current = void 0;
11099
+ return result ?? { status: "pending", requestId: input.requestId };
11100
+ }
11101
+ const operation = { signature };
11102
+ current = operation;
11103
+ void (async () => {
11104
+ let intent;
11105
+ try {
11106
+ intent = await retainIntent(path, input);
11107
+ const connected = await client.initialize(input, { resumeAdoption: true, expectedTeamId: intent.resolvedTeamId, onTeamResolved: (teamId) => retainTeam(path, input, teamId, intent?.guidanceDelivered) });
11108
+ const instructions = `${TEAM_SKILL}
11109
+
11110
+ 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.`;
11111
+ if (!intent.guidanceDelivered) {
11112
+ const key = await waitForSessionKey(sessionId, 1e4);
11113
+ await sendUserMessage(sessionId, key, instructions, "teams", { localId: `teams-adopt-${input.requestId}`, sentFrom: "team" });
11114
+ await retainTeam(path, input, connected.team.id, true);
11115
+ }
11116
+ operation.result = { status: "connected", teamId: connected.team.id, botId: connected.botId, instructions };
11117
+ } catch (error) {
11118
+ if (error instanceof agentGuidance.TeamConnectionPreflightError && intent?.fresh && !intent.resolvedTeamId) {
11119
+ await promises.unlink(path).catch(() => {
11120
+ });
11121
+ operation.safeToChange = true;
11122
+ }
11123
+ operation.result = { error: error instanceof Error ? error.message : "Team connection failed; retry the same request", ...operation.safeToChange ? { safeToChange: true } : {} };
11124
+ }
11125
+ })();
11126
+ return { status: "pending", requestId: input.requestId };
11127
+ };
11128
+ }
11129
+
10925
11130
  const PERSONAL_MEMORY_SOFT_LIMIT_CHARS = 2e3;
10926
11131
  function headingTitle(line) {
10927
11132
  const m = /^##(?!#)\s*(.*?)\s*$/.exec(line);
@@ -11565,6 +11770,11 @@ async function startHappyServer(client, options) {
11565
11770
  resolve(new URL(`http://127.0.0.1:${addr.port}`));
11566
11771
  });
11567
11772
  });
11773
+ const adoptionEnabled = !process.env.VH_TEAM_SCOPE_FILE;
11774
+ if (adoptionEnabled) {
11775
+ client.rpcHandlerManager.registerHandler("teams-adopt", createTeamAdoption(client.sessionId));
11776
+ client.updateMetadata((metadata) => ({ ...metadata, capabilities: [.../* @__PURE__ */ new Set([...metadata.capabilities ?? [], TEAMS_ADOPT_CAPABILITY])] }));
11777
+ }
11568
11778
  persistence.logger.debug(`[happyMCP] server:ready sessionId=${client.sessionId} url=${baseUrl.toString()}`);
11569
11779
  return {
11570
11780
  url: baseUrl.toString(),
@@ -11578,6 +11788,7 @@ async function startHappyServer(client, options) {
11578
11788
  ],
11579
11789
  stop: () => {
11580
11790
  persistence.logger.debug(`[happyMCP] server:stop sessionId=${client.sessionId}`);
11791
+ if (adoptionEnabled) client.rpcHandlerManager.unregisterHandler("teams-adopt");
11581
11792
  server.close();
11582
11793
  }
11583
11794
  };
@@ -11834,7 +12045,7 @@ function registerSideQuestionHandler(rpc, deps) {
11834
12045
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
11835
12046
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
11836
12047
  const run = deps.run ?? (async (input) => {
11837
- const { query } = await Promise.resolve().then(function () { return require('./index-HW-KdG6W.cjs'); });
12048
+ const { query } = await Promise.resolve().then(function () { return require('./index-CJmXLSQc.cjs'); });
11838
12049
  return runSideQuestion(query, input);
11839
12050
  });
11840
12051
  const slots = /* @__PURE__ */ new Map();
@@ -11936,7 +12147,7 @@ const MAX_TITLE_CHARS = 60;
11936
12147
  const GENERATION_TIMEOUT_MS = 3e4;
11937
12148
  function resolveClaudeBinary() {
11938
12149
  try {
11939
- 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-Cw9Zj1Zr.cjs', document.baseURI).href)));
12150
+ 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-D_YVlpsW.cjs', document.baseURI).href)));
11940
12151
  const utilsPath = path.resolve(path.join(api.projectPath(), "scripts", "claude_version_utils.cjs"));
11941
12152
  const { getClaudeCliPath } = require$1(utilsPath);
11942
12153
  const path$1 = getClaudeCliPath();
@@ -14154,9 +14365,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
14154
14365
  };
14155
14366
  }
14156
14367
 
14157
- 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-Cw9Zj1Zr.cjs', document.baseURI).href)));
14368
+ 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-D_YVlpsW.cjs', document.baseURI).href)));
14158
14369
  const __dirname$1 = path.dirname(__filename$1);
14159
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-Cw9Zj1Zr.cjs', document.baseURI).href)));
14370
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-D_YVlpsW.cjs', document.baseURI).href)));
14160
14371
  const PRISMA_QUERY_ENGINE_FILES = {
14161
14372
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
14162
14373
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -15895,6 +16106,7 @@ function buildCodexTurnPrompt(opts) {
15895
16106
  parts.push(opts.message);
15896
16107
  if (opts.includeTitleInstruction) {
15897
16108
  parts.push(CHANGE_TITLE_INSTRUCTION);
16109
+ parts.push(api.BUILTIN_TODO_DISCOVERY);
15898
16110
  }
15899
16111
  return parts.join("\n\n");
15900
16112
  }
@@ -16787,7 +16999,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16787
16999
  process.exit(0);
16788
17000
  } else if (subcommand === "install-terminal-hooks") {
16789
17001
  try {
16790
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-CNp1LExn.cjs'); });
17002
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-C1zwiJJ2.cjs'); });
16791
17003
  const command = parseTerminalHooksArgs(args.slice(1));
16792
17004
  if (command.action === "help") {
16793
17005
  console.log(TERMINAL_HOOKS_HELP);
@@ -16804,7 +17016,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16804
17016
  }
16805
17017
  } else if (subcommand === "spawn") {
16806
17018
  try {
16807
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-J-A5WGZ7.cjs'); });
17019
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DrnCW2QK.cjs'); });
16808
17020
  await handleSpawnCommand(args.slice(1));
16809
17021
  } catch (error) {
16810
17022
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16816,7 +17028,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16816
17028
  return;
16817
17029
  } else if (subcommand === "sessions") {
16818
17030
  try {
16819
- const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-CNkLCorF.cjs'); });
17031
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-DC0IKaD9.cjs'); });
16820
17032
  await handleSessionsCommand(args.slice(1));
16821
17033
  } catch (error) {
16822
17034
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16828,7 +17040,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16828
17040
  return;
16829
17041
  } else if (subcommand === "send") {
16830
17042
  try {
16831
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-CDK1bZFj.cjs'); });
17043
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-CPdT1nOg.cjs'); });
16832
17044
  await handleSendCommand(args.slice(1));
16833
17045
  } catch (error) {
16834
17046
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -16934,9 +17146,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16934
17146
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
16935
17147
  const projectId = args[3];
16936
17148
  try {
16937
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-C0mofvCH.cjs'); });
16938
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DG1OQnCb.cjs'); }).then(function (n) { return n.persistence; });
16939
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-P5deH8Ls.cjs'); }).then(function (n) { return n.api; });
17149
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-Dmkzs3o4.cjs'); });
17150
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-CrwtFbi8.cjs'); }).then(function (n) { return n.persistence; });
17151
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-DfatCoQL.cjs'); }).then(function (n) { return n.api; });
16940
17152
  let userEmail = void 0;
16941
17153
  try {
16942
17154
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -16967,7 +17179,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
16967
17179
  }
16968
17180
  if (geminiSubcommand === "project" && args[2] === "get") {
16969
17181
  try {
16970
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-C0mofvCH.cjs'); });
17182
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-Dmkzs3o4.cjs'); });
16971
17183
  const config = readGeminiLocalConfig();
16972
17184
  if (config.googleCloudProject) {
16973
17185
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -17007,7 +17219,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17007
17219
  process.exit(0);
17008
17220
  }
17009
17221
  try {
17010
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-BfCSOPL1.cjs'); });
17222
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-B2GesRWm.cjs'); });
17011
17223
  let startedBy = void 0;
17012
17224
  for (let i = 1; i < args.length; i++) {
17013
17225
  if (args[i] === "--started-by") {
@@ -17037,7 +17249,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17037
17249
  }
17038
17250
  };
17039
17251
  try {
17040
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-CMbKpntk.cjs'); });
17252
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-vyTJsT_R.cjs'); });
17041
17253
  const parsed = parsePiRunnerArgs(args.slice(1));
17042
17254
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17043
17255
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17064,7 +17276,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17064
17276
  return;
17065
17277
  } else if (subcommand === "acp") {
17066
17278
  try {
17067
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-CMbKpntk.cjs'); });
17279
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-vyTJsT_R.cjs'); });
17068
17280
  let startedBy = void 0;
17069
17281
  let verbose = false;
17070
17282
  const acpArgs = [];
@@ -17104,7 +17316,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17104
17316
  return;
17105
17317
  } else if (subcommand === "openclaw") {
17106
17318
  try {
17107
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-CBlHXEAT.cjs'); });
17319
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-DCK-R8qe.cjs'); });
17108
17320
  let startedBy = void 0;
17109
17321
  let verbose = false;
17110
17322
  let gatewayUrl;
@@ -17153,9 +17365,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17153
17365
  process.exit(1);
17154
17366
  }
17155
17367
  return;
17368
+ } else if (subcommand === "todo") {
17369
+ try {
17370
+ const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-DrME15xq.cjs'); });
17371
+ await handleTodoCommand(args.slice(1));
17372
+ } catch (error) {
17373
+ console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
17374
+ process.exitCode = 1;
17375
+ }
17376
+ return;
17156
17377
  } else if (subcommand === "teams") {
17157
17378
  try {
17158
- const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-MgWeyutG.cjs'); });
17379
+ const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-CwXEJs9I.cjs'); });
17159
17380
  await handleTeamsCommand(args.slice(1));
17160
17381
  } catch (error) {
17161
17382
  console.error(error instanceof Error ? error.message : "Teams command failed");
@@ -17164,7 +17385,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17164
17385
  return;
17165
17386
  } else if (subcommand === "mcp" && args.length === 1) {
17166
17387
  try {
17167
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-BtZoIIIM.cjs'); });
17388
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-Bc_mmJt8.cjs'); });
17168
17389
  await handleMcpCommand();
17169
17390
  } catch (error) {
17170
17391
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
@@ -17391,6 +17612,7 @@ ${chalk.bold("Usage:")}
17391
17612
  Install optional Claude terminal mirror hooks
17392
17613
  (add --remove to uninstall them)
17393
17614
  very-happy connect Connect AI vendor API keys
17615
+ very-happy todo Built-in Todos and official agent skill (todo --help)
17394
17616
  very-happy sandbox Configure and manage OS-level sandboxing
17395
17617
  very-happy notify Send push notification
17396
17618
  very-happy mcp Stdio MCP server exposing copy_to_clipboard for a
@@ -17537,8 +17759,10 @@ exports.GOOGLE_API_KEY_ENV = GOOGLE_API_KEY_ENV;
17537
17759
  exports.MAX_READ_LIMIT = MAX_READ_LIMIT;
17538
17760
  exports.MessageBuffer = MessageBuffer;
17539
17761
  exports.MessageQueue2 = MessageQueue2;
17762
+ exports.PI_TEAMS_EXTENSION = PI_TEAMS_EXTENSION;
17540
17763
  exports.SPAWN_AGENTS = SPAWN_AGENTS;
17541
17764
  exports.StreamRelay = StreamRelay;
17765
+ exports.TEAM_SKILL = TEAM_SKILL;
17542
17766
  exports.TitleGenerator = TitleGenerator;
17543
17767
  exports.archiveSession = archiveSession;
17544
17768
  exports.checkIfDaemonRunningAndCleanupStaleState = checkIfDaemonRunningAndCleanupStaleState;
@@ -1,13 +1,12 @@
1
- import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-BzGpBAZ0.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-BzGpBAZ0.mjs';
3
- import { c as configuration, f as readSettings, g as encodeBase64, l as logger } from './limits-DlbYYtOw.mjs';
4
- import { A as AcpSessionManager } from './AcpSessionManager-B223yT78.mjs';
1
+ import { D as DefaultTransport, A as AcpBackend } from './AcpBackend-D7987ody.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-D7987ody.mjs';
3
+ import { c as configuration, g as readSettings, i as encodeBase64, l as logger } from './limits-BNTLh2lw.mjs';
4
+ import { A as AcpSessionManager } from './AcpSessionManager-BwrjVZyn.mjs';
5
+ import { g as connectionState, A as ApiClient, p as projectPath, h as BUILTIN_TODO_DISCOVERY } from './types-CFs8FmiY.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-n_VWu9qe.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-D4eCRu9x.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-CfM6ewd8.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-BR0OFumQ.mjs';
35
+ import './agentGuidance-BPGPaNlo.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,14 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var AcpBackend = require('./AcpBackend-C9eu6CDu.cjs');
4
- var persistence = require('./limits-DG1OQnCb.cjs');
5
- var AcpSessionManager = require('./AcpSessionManager-DQ1e_0gc.cjs');
3
+ var AcpBackend = require('./AcpBackend-BYU6J2n0.cjs');
4
+ var persistence = require('./limits-CrwtFbi8.cjs');
5
+ var AcpSessionManager = require('./AcpSessionManager-artXJgqb.cjs');
6
+ var api = require('./types-DfatCoQL.cjs');
6
7
  var promises = require('node:fs/promises');
7
8
  var node_crypto = require('node:crypto');
8
9
  var path = require('node:path');
9
- var resources = require('./resources-CVl7NdOB.cjs');
10
- var api = require('./types-P5deH8Ls.cjs');
11
- var index = require('./index-Cw9Zj1Zr.cjs');
10
+ var index = require('./index-D_YVlpsW.cjs');
12
11
  require('node:child_process');
13
12
  require('@agentclientprotocol/sdk');
14
13
  require('node:fs');
@@ -34,7 +33,7 @@ require('fs');
34
33
  require('node:readline');
35
34
  require('@anthropic-ai/sandbox-runtime');
36
35
  require('expo-server-sdk');
37
- require('./agentGuidance-Tm8Z5HYp.cjs');
36
+ require('./agentGuidance-B1xYgHx6.cjs');
38
37
  require('ink');
39
38
  require('react');
40
39
  require('@anthropic-ai/claude-agent-sdk');
@@ -75,7 +74,7 @@ async function preparePiTeamsRuntime() {
75
74
  const directory = path.join(persistence.configuration.happyHomeDir, "teams", "pi-runtime");
76
75
  await promises.mkdir(directory, { recursive: true, mode: 448 });
77
76
  const extension = path.join(directory, "very-happy-teams.mjs");
78
- await publish(extension, resources.PI_TEAMS_EXTENSION, 384);
77
+ await publish(extension, index.PI_TEAMS_EXTENSION, 384);
79
78
  const launcher = path.join(directory, "pi-teams.mjs");
80
79
  const script = `#!${process.execPath}
81
80
  import { spawn } from 'node:child_process';
@@ -1075,6 +1074,7 @@ async function runAcp(opts) {
1075
1074
  logAcp("muted", `Outgoing models from ${opts.agentName}: not reported yet`);
1076
1075
  }
1077
1076
  }
1077
+ let todoDiscoverySent = false;
1078
1078
  while (!shouldExit) {
1079
1079
  const waitSignal = abortController.signal;
1080
1080
  const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
@@ -1101,7 +1101,11 @@ async function runAcp(opts) {
1101
1101
  if (typeof batch.mode.model === "string" && batch.mode.model.length > 0) {
1102
1102
  await switchModelIfRequested(batch.mode.model);
1103
1103
  }
1104
- await backend.sendPrompt(acpSessionId, batch.message);
1104
+ const prompt = todoDiscoverySent ? batch.message : `${batch.message}
1105
+
1106
+ ${api.BUILTIN_TODO_DISCOVERY}`;
1107
+ await backend.sendPrompt(acpSessionId, prompt);
1108
+ todoDiscoverySent = true;
1105
1109
  await turnEnded;
1106
1110
  sendEnvelopes(sessionManager.endTurn("completed"));
1107
1111
  streamRelay.endTurn();