newmark-agent 0.5.14 → 0.6.0

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 (68) hide show
  1. package/dist/conversation-utility-host.bundle.cjs +4565 -2998
  2. package/dist/conversation-utility-host.js +1 -1
  3. package/dist/core/agent.d.ts +108 -16
  4. package/dist/core/agent.js +698 -195
  5. package/dist/core/agentKernel/agent.d.ts +1 -0
  6. package/dist/core/agentKernel/agent.js +9 -0
  7. package/dist/core/agentKernelDiagnostics.d.ts +4 -6
  8. package/dist/core/agentKernelDiagnostics.js +12 -9
  9. package/dist/core/agentKernelRunner.d.ts +5 -0
  10. package/dist/core/agentKernelRunner.js +250 -107
  11. package/dist/core/autoRouter.js +17 -27
  12. package/dist/core/config.d.ts +2 -0
  13. package/dist/core/config.js +7 -1
  14. package/dist/core/conversationCommandState.d.ts +17 -0
  15. package/dist/core/conversationCommandState.js +140 -0
  16. package/dist/core/conversationKernel.d.ts +25 -4
  17. package/dist/core/conversationKernel.js +360 -89
  18. package/dist/core/conversationListEvent.d.ts +6 -0
  19. package/dist/core/conversationListEvent.js +14 -0
  20. package/dist/core/electronUtilityAgentClient.d.ts +4 -1
  21. package/dist/core/electronUtilityAgentClient.js +4 -4
  22. package/dist/core/electronUtilityRuntimePool.d.ts +8 -2
  23. package/dist/core/electronUtilityRuntimePool.js +11 -5
  24. package/dist/core/installUpdate.js +41 -29
  25. package/dist/core/modelResponseHealth.d.ts +17 -0
  26. package/dist/core/modelResponseHealth.js +193 -0
  27. package/dist/core/providerUsageAccounting.d.ts +47 -0
  28. package/dist/core/providerUsageAccounting.js +71 -0
  29. package/dist/core/requestContextEstimate.d.ts +18 -0
  30. package/dist/core/requestContextEstimate.js +54 -0
  31. package/dist/core/subagent.d.ts +89 -5
  32. package/dist/core/subagent.js +264 -41
  33. package/dist/core/subagentCommunication.d.ts +43 -0
  34. package/dist/core/subagentCommunication.js +167 -0
  35. package/dist/core/types.d.ts +34 -1
  36. package/dist/core/utilityAgentProtocol.d.ts +4 -0
  37. package/dist/core/workEventCoalescer.js +4 -1
  38. package/dist/core/wslAgentClient.d.ts +18 -5
  39. package/dist/core/wslAgentClient.js +79 -27
  40. package/dist/core/wslAgentProtocol.d.ts +7 -0
  41. package/dist/core/wslAgentRuntimePool.d.ts +8 -2
  42. package/dist/core/wslAgentRuntimePool.js +19 -6
  43. package/dist/core/wslRuntimeProcessTree.d.ts +10 -0
  44. package/dist/core/wslRuntimeProcessTree.js +104 -0
  45. package/dist/llm/provider.d.ts +5 -12
  46. package/dist/llm/provider.js +150 -269
  47. package/dist/main.js +582 -356
  48. package/dist/preload.js +3 -3
  49. package/dist/providers/chat-completions.adapter.d.ts +2 -0
  50. package/dist/providers/chat-completions.adapter.js +84 -36
  51. package/dist/providers/provider-adapter.d.ts +2 -0
  52. package/dist/providers/provider-events.d.ts +23 -14
  53. package/dist/providers/provider-events.js +148 -43
  54. package/dist/providers/provider-headers.d.ts +7 -3
  55. package/dist/providers/provider-headers.js +109 -39
  56. package/dist/providers/provider-request-compat.d.ts +6 -0
  57. package/dist/providers/provider-request-compat.js +37 -0
  58. package/dist/providers/responses.adapter.d.ts +2 -0
  59. package/dist/providers/responses.adapter.js +192 -126
  60. package/dist/server.d.ts +9 -2
  61. package/dist/server.js +100 -24
  62. package/dist/tools/index.js +17 -1
  63. package/dist/ui/index.html +2515 -693
  64. package/dist/ui/lucide-sprite.svg +7 -0
  65. package/dist/ui/startup.html +6 -6
  66. package/dist/wsl-agent-host.bundle.cjs +4575 -3006
  67. package/dist/wsl-agent-host.js +9 -4
  68. package/package.json +23 -4
package/dist/server.js CHANGED
@@ -56,6 +56,7 @@ const nativeBash_1 = require("./core/nativeBash");
56
56
  const installUpdate_1 = require("./core/installUpdate");
57
57
  const mobilePairing_1 = require("./core/mobilePairing");
58
58
  const workEventCoalescer_1 = require("./core/workEventCoalescer");
59
+ const conversationListEvent_1 = require("./core/conversationListEvent");
59
60
  const searchMcpPool_1 = require("./core/searchMcpPool");
60
61
  const PORT = 47890;
61
62
  let agent = null;
@@ -124,6 +125,36 @@ function resolveMobileWorkspace(current, workspaceId) {
124
125
  return [...current.workspace.internal, ...current.workspace.external].find(workspace => workspace.id === clean)
125
126
  || (current.workspace.current?.id === clean ? current.workspace.current : null);
126
127
  }
128
+ function hostedCommandTarget(current, params) {
129
+ const workspaceId = String(params.workspaceId || current.workspace.current?.id || '');
130
+ const workspace = workspaceId ? resolveMobileWorkspace(current, workspaceId) : null;
131
+ if (workspaceId && !workspace)
132
+ throw new Error('Unknown workspace');
133
+ const explicitConversation = params.conversationId || params.conversation;
134
+ const conversationId = String(explicitConversation || (workspace ? current.activeConversationIdForWorkspace(workspace) : current.activeConversationId));
135
+ if (workspace && explicitConversation && !current.hasConversationInWorkspace(conversationId, workspace))
136
+ throw new Error('Conversation not found in workspace');
137
+ return { workspaceId, conversationId };
138
+ }
139
+ function promptCommandMessage(input) {
140
+ if (typeof input === 'string')
141
+ return input.trim() ? input : null;
142
+ if (!input || typeof input !== 'object' || Array.isArray(input))
143
+ return null;
144
+ const message = input;
145
+ return typeof message.text === 'string'
146
+ && (message.text.trim() || (Array.isArray(message.images) && message.images.length > 0)) ? message : null;
147
+ }
148
+ function hostedCommandOptions(params) {
149
+ return {
150
+ requestedMode: String(params.requestedMode || params.mode || ''),
151
+ goalObjective: String(params.goalObjective || ''),
152
+ inputMode: String(params.inputMode || ''),
153
+ clientMessageId: String(params.clientMessageId || ''),
154
+ flowName: String(params.flowName || params.defaultFlow || ''),
155
+ flowStart: typeof params.flowStart === 'number' && Number.isFinite(params.flowStart) ? params.flowStart : undefined,
156
+ };
157
+ }
127
158
  const MOBILE_EDITABLE_EXTENSIONS = new Set([
128
159
  '.txt', '.md', '.markdown', '.json', '.jsonc', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.conf',
129
160
  '.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.css', '.scss', '.html', '.htm', '.xml', '.svg',
@@ -629,17 +660,27 @@ async function handleApi(req, res, body) {
629
660
  messages: s.messages.slice(-20),
630
661
  })),
631
662
  archives: agent.listArchives(),
663
+ ...(hostedConversationUiState ? await hostedConversationUiState(hostedCommandTarget(agent, Object.fromEntries(url.searchParams))) : {}),
632
664
  });
633
665
  return;
634
666
  }
635
667
  case '/api/send':
636
668
  case '/api/send-prompt': {
637
669
  const params = JSON.parse(body || '{}');
638
- const message = params.message || '';
670
+ const message = promptCommandMessage(params.message);
639
671
  if (!message) {
640
672
  jsonResponse(res, { error: 'No message' }, 400);
641
673
  return;
642
674
  }
675
+ if (hostedConversationPrompt) {
676
+ const result = await hostedConversationPrompt(hostedCommandTarget(agent, params), message, hostedCommandOptions(params));
677
+ if (pathname === '/api/send-prompt' && Array.isArray(result.tokens)) {
678
+ jsonResponse(res, result.tokens.map((token) => token.text || '').join(''));
679
+ }
680
+ else
681
+ jsonResponse(res, result);
682
+ return;
683
+ }
643
684
  if (params.conversation)
644
685
  agent.setConversation(String(params.conversation));
645
686
  const tokens = await agent.process(message);
@@ -666,7 +707,12 @@ async function handleApi(req, res, body) {
666
707
  return;
667
708
  }
668
709
  case '/api/mode': {
669
- const m = JSON.parse(body || '{}').mode || 'build';
710
+ const params = JSON.parse(body || '{}');
711
+ const m = params.mode || 'build';
712
+ if (hostedConversationUiAction) {
713
+ jsonResponse(res, await hostedConversationUiAction(hostedCommandTarget(agent, params), 'mode', String(m), params));
714
+ return;
715
+ }
670
716
  agent.setMode(m);
671
717
  jsonResponse(res, { mode: agent.mode });
672
718
  return;
@@ -996,6 +1042,18 @@ async function handleApi(req, res, body) {
996
1042
  });
997
1043
  return;
998
1044
  }
1045
+ case '/api/input-mode': {
1046
+ const params = JSON.parse(body || '{}');
1047
+ const inputMode = params.inputMode === 'next' ? 'next' : 'guide';
1048
+ if (hostedConversationUiAction) {
1049
+ jsonResponse(res, await hostedConversationUiAction(hostedCommandTarget(agent, params), 'input_mode', inputMode, params));
1050
+ }
1051
+ else {
1052
+ agent.setInputMode(inputMode);
1053
+ jsonResponse(res, { inputMode: agent.inputMode });
1054
+ }
1055
+ return;
1056
+ }
999
1057
  case '/api/mobile/web-search': {
1000
1058
  const input = JSON.parse(body || '{}');
1001
1059
  const query = String(input.query || '').trim();
@@ -1082,10 +1140,14 @@ async function handleApi(req, res, body) {
1082
1140
  mobileJson(res, { error: 'Conversation not found in workspace' }, 404);
1083
1141
  return;
1084
1142
  }
1085
- // GUI conversations normally execute inside isolated runtime pools.
1086
- // Their durable target state can be newer than the hosted main Agent's
1087
- // foreground memory, so workspace-bound mobile reads must hydrate a
1088
- // fresh read-only target Agent from storage.
1143
+ // The GUI owner may contain live deltas not yet persisted. Forward the
1144
+ // requested history window to that same owner, without another slice.
1145
+ if (hostedConversationUiState) {
1146
+ const targetWorkspaceId = ws?.id || current.workspace.current?.id || '';
1147
+ mobileJson(res, await hostedConversationUiState({ workspaceId: targetWorkspaceId, conversationId: String(conversationId) }, { window: windowSize, before }));
1148
+ return;
1149
+ }
1150
+ // Standalone server mode has no GUI runtime owner to delegate to.
1089
1151
  const snapshotAgent = ws ? mobileScopedAgent(ws, String(conversationId)) : current;
1090
1152
  const snapshot = snapshotAgent.getConversationSnapshot(String(conversationId), {
1091
1153
  window: windowSize,
@@ -1278,6 +1340,7 @@ async function handleApi(req, res, body) {
1278
1340
  return;
1279
1341
  }
1280
1342
  const created = agent.createConversationInWorkspace(ws, String(params.title || ''));
1343
+ publishServerWorkEvent((0, conversationListEvent_1.conversationListEvent)(agent, ws));
1281
1344
  mobileJson(res, {
1282
1345
  ok: true,
1283
1346
  workspace: { id: ws.id, name: ws.name, path: ws.path, isInternal: ws.isInternal },
@@ -1312,7 +1375,7 @@ async function handleApi(req, res, body) {
1312
1375
  const workspaceId = String(params.workspaceId || '');
1313
1376
  const conversationId = String(params.conversationId || '');
1314
1377
  const action = String(params.action || '');
1315
- const allowed = new Set(['goal_update', 'goal_guide', 'conversation_guide', 'goal_toggle_pause', 'goal_clear', 'flow_pause', 'flow_resume', 'flow_guide', 'conversation_stop', 'input_mode', 'queue_enqueue', 'queue_update', 'queue_delete', 'queue_reorder', 'queue_toggle_pause', 'queue_guide']);
1378
+ const allowed = new Set(['goal_update', 'goal_guide', 'conversation_guide', 'goal_toggle_pause', 'goal_clear', 'flow_pause', 'flow_resume', 'flow_guide', 'conversation_stop', 'mode', 'input_mode', 'queue_enqueue', 'queue_update', 'queue_delete', 'queue_reorder', 'queue_toggle_pause', 'queue_set_pause', 'queue_guide']);
1316
1379
  if (!workspaceId || !conversationId || !allowed.has(action)) {
1317
1380
  mobileJson(res, { error: 'workspaceId, conversationId, and a valid action are required' }, 400);
1318
1381
  return;
@@ -1342,7 +1405,7 @@ async function handleApi(req, res, body) {
1342
1405
  const workspaceId = String(params.workspaceId || '');
1343
1406
  const conversationId = String(params.conversationId || '');
1344
1407
  const action = String(params.action || '');
1345
- const allowed = new Set(['queue_enqueue', 'queue_update', 'queue_delete', 'queue_reorder', 'queue_toggle_pause', 'queue_guide']);
1408
+ const allowed = new Set(['queue_enqueue', 'queue_update', 'queue_delete', 'queue_reorder', 'queue_toggle_pause', 'queue_set_pause', 'queue_guide']);
1346
1409
  if (!workspaceId || !conversationId || !allowed.has(action)) {
1347
1410
  jsonResponse(res, { error: 'workspaceId, conversationId, and a valid queue action are required' }, 400);
1348
1411
  return;
@@ -1373,6 +1436,7 @@ async function handleApi(req, res, body) {
1373
1436
  mobileJson(res, { ok: false, error: 'Conversation rename failed' }, 404);
1374
1437
  return;
1375
1438
  }
1439
+ publishServerWorkEvent((0, conversationListEvent_1.conversationListEvent)(agent, ws));
1376
1440
  mobileJson(res, { ok: true, conversations: mobileWorkspaceConversationRows(agent, ws) });
1377
1441
  return;
1378
1442
  }
@@ -1395,6 +1459,7 @@ async function handleApi(req, res, body) {
1395
1459
  mobileJson(res, { ok: false, error: 'Conversation pin update failed' }, 404);
1396
1460
  return;
1397
1461
  }
1462
+ publishServerWorkEvent((0, conversationListEvent_1.conversationListEvent)(agent, ws));
1398
1463
  mobileJson(res, { ok: true, pinned, conversations: mobileWorkspaceConversationRows(agent, ws) });
1399
1464
  return;
1400
1465
  }
@@ -1432,19 +1497,18 @@ async function handleApi(req, res, body) {
1432
1497
  mobileJson(res, { ok: false, error: 'Conversation reorder failed' }, 409);
1433
1498
  return;
1434
1499
  }
1500
+ publishServerWorkEvent((0, conversationListEvent_1.conversationListEvent)(current, ws));
1435
1501
  mobileJson(res, { ok: true, conversations: mobileWorkspaceConversationRows(current, ws) });
1436
1502
  return;
1437
1503
  }
1438
1504
  case '/api/mobile/send': {
1439
1505
  const params = JSON.parse(body || '{}');
1440
- const message = String(params.message || '');
1506
+ const message = promptCommandMessage(params.message);
1441
1507
  if (!message) {
1442
1508
  mobileJson(res, { error: 'No message' }, 400);
1443
1509
  return;
1444
1510
  }
1445
- const workspaceId = String(params.workspaceId || '');
1446
- let requestAgent = agent;
1447
- let requestWorkspace = agent.workspace.current;
1511
+ const workspaceId = String(params.workspaceId || agent.workspace.current?.id || '');
1448
1512
  let resolvedWorkspace = null;
1449
1513
  if (workspaceId) {
1450
1514
  const ws = resolveMobileWorkspace(agent, workspaceId);
@@ -1453,7 +1517,6 @@ async function handleApi(req, res, body) {
1453
1517
  return;
1454
1518
  }
1455
1519
  resolvedWorkspace = ws;
1456
- requestWorkspace = ws;
1457
1520
  }
1458
1521
  const conversationId = String(params.conversationId
1459
1522
  || (resolvedWorkspace ? agent.activeConversationIdForWorkspace(resolvedWorkspace) : agent.activeConversationId));
@@ -1463,20 +1526,22 @@ async function handleApi(req, res, body) {
1463
1526
  mobileJson(res, { error: 'Conversation not found in workspace' }, 404);
1464
1527
  return;
1465
1528
  }
1466
- const currentWs = agent.workspace.current;
1467
- if (!currentWs || path.resolve(currentWs.path) !== path.resolve(ws.path)) {
1468
- requestAgent = mobileScopedAgent(ws, conversationId);
1469
- }
1470
1529
  }
1471
- if (hostedConversationPrompt && resolvedWorkspace) {
1472
- const result = await hostedConversationPrompt({ workspaceId, conversationId }, message, {
1473
- requestedMode: String(params.requestedMode || ''),
1474
- goalObjective: String(params.goalObjective || ''),
1475
- inputMode: String(params.inputMode || ''),
1476
- });
1530
+ // The hosted command owns execution and queueing for both IPC and HTTP.
1531
+ // Resolve/authenticate the target without creating a second Agent here.
1532
+ if (hostedConversationPrompt) {
1533
+ const result = await hostedConversationPrompt({ workspaceId, conversationId }, message, hostedCommandOptions(params));
1477
1534
  mobileJson(res, { ok: true, conversationId, ...result });
1478
1535
  return;
1479
1536
  }
1537
+ let requestAgent = agent;
1538
+ const requestWorkspace = resolvedWorkspace || agent.workspace.current;
1539
+ if (resolvedWorkspace) {
1540
+ const currentWs = agent.workspace.current;
1541
+ if (!currentWs || path.resolve(currentWs.path) !== path.resolve(resolvedWorkspace.path)) {
1542
+ requestAgent = mobileScopedAgent(resolvedWorkspace, conversationId);
1543
+ }
1544
+ }
1480
1545
  if (requestAgent === agent && params.conversationId)
1481
1546
  requestAgent.setConversation(conversationId);
1482
1547
  let scopedRuntimeKey = '';
@@ -1546,6 +1611,12 @@ async function handleApi(req, res, body) {
1546
1611
  mobileJson(res, { error: 'Conversation not found in workspace' }, 404);
1547
1612
  return;
1548
1613
  }
1614
+ if (hostedConversationUiAction) {
1615
+ const action = pathname === '/api/mobile/conversation-branch-inspect' ? 'conversation_branch_inspect'
1616
+ : pathname === '/api/mobile/conversation-branch-activate' ? 'conversation_branch_activate' : 'conversation_branch_create';
1617
+ mobileJson(res, await hostedConversationUiAction({ workspaceId, conversationId }, action, '', params));
1618
+ return;
1619
+ }
1549
1620
  const scoped = mobileScopedAgent(ws, conversationId);
1550
1621
  if (pathname === '/api/mobile/conversation-branch-inspect') {
1551
1622
  const branchId = String(params.branchId || '');
@@ -1605,7 +1676,11 @@ async function handleApi(req, res, body) {
1605
1676
  mobileJson(res, { ok: false, error: 'Conversation not found in workspace' }, 404);
1606
1677
  return;
1607
1678
  }
1608
- // 运行中拒绝:与移动端 running 判定一致(activeConversationId + agent.status 非 idle)
1679
+ if (hostedConversationUiAction) {
1680
+ mobileJson(res, await hostedConversationUiAction({ workspaceId, conversationId }, 'conversation_archive', '', params));
1681
+ return;
1682
+ }
1683
+ // Standalone server fallback; GUI-hosted archive uses its shared owner above.
1609
1684
  if (mobileConversationRuntimeBusy(agent, ws, conversationId)) {
1610
1685
  mobileJson(res, { ok: false, error: 'Conversation is running' }, 423);
1611
1686
  return;
@@ -1615,6 +1690,7 @@ async function handleApi(req, res, body) {
1615
1690
  mobileJson(res, { ok: false, error: 'Conversation archive could not be written.' }, 500);
1616
1691
  return;
1617
1692
  }
1693
+ publishServerWorkEvent((0, conversationListEvent_1.conversationListEvent)(agent, ws));
1618
1694
  mobileJson(res, {
1619
1695
  ok: true,
1620
1696
  fileName: filename,
@@ -490,7 +490,23 @@ class ToolExecutor {
490
490
  t('SubAgent', 'Create one real same-conversation peer SubAgent and return immediately. This tool is only for delegation; never use it to create or update the conversation task checklist (use task_create for that). Creation is bound to the currently running Build Block: non-Ultra intelligence has a hard ceiling of 4 active peers for that Build, Ultra has 16, and excess calls terminate without creating or queueing a record. The peer has a stable human-readable name plus a separate exact id. Pass model to select an exact configured model deployment (deployment:providerId:modelId or an unambiguous provider/model name). When model is omitted, the peer inherits the parent Agent\'s currently resolved model deployment. Plan mode peers are forced to Plan.', { name: { type: 'string', description: 'Human-readable SubAgent name shown in the monitoring sidebar. Omit only when a named preset supplies it.' }, nature: { type: 'string', description: 'Legacy alias for name.' }, prompt: { type: 'string', description: 'Work delegated to this SubAgent; this is not a checklist item.' }, preset: { type: 'string' }, agent: { type: 'string' }, model: { type: 'string', description: 'Optional exact model deployment. Omit to inherit the parent Agent resolved model.' }, mode: { type: 'string' }, input_mode: { type: 'string' }, flow: { type: 'string' } }, ['prompt']),
491
491
  t('subagent_list', 'List flat same-conversation peer agents, optionally filtered by status. Each entry exposes both the stable name and the exact id; use the id for any subsequent targeting.', { status: { type: 'string', enum: ['idle', 'queued', 'working', 'completed', 'error', 'closed'] } }, []),
492
492
  t('subagent_read', 'Read one same-conversation peer status, queue/mailbox summary, latest bounded feedback, and result. Available for running, queued, completed, error, and closed peers. Pass the exact id returned by subagent_list, or a name for convenience lookup.', { id: { type: 'string', description: 'Exact peer id from subagent_list.' }, name: { type: 'string', description: 'Convenience peer name; ambiguous names resolve to the first match.' }, max_chars: { type: 'number', description: 'Bounded result size from 2000 to 32000 characters.' } }, []),
493
- t('subagent_send', 'Persist a mailbox message to a same-conversation peer agent. Target by exact id (preferred) or name.', { id: { type: 'string', description: 'Exact peer id from subagent_list.' }, name: { type: 'string', description: 'Convenience peer name.' }, message: { type: 'string' }, prompt: { type: 'string', description: 'Legacy alias for message.' }, kind: { type: 'string', enum: ['directive', 'question', 'result', 'handoff'] }, reply_to: { type: 'string' }, correlation_id: { type: 'string' } }, []),
493
+ t('subagent_send', 'Send selected content from your own history to a same-conversation peer, or id="root" for the main agent. wakeup defaults to false: an inactive receiver only stores the message; true requests activation. An active receiver handles either value normally. With no content/message, send the last visible text-history entry. Choose each explicit content kind and its range; summaries are written by you, not generated by the tool. Existing receiver history and static request metadata are unchanged until receipt.', {
494
+ id: { type: 'string', description: 'Exact peer id, or root for the main agent.' },
495
+ name: { type: 'string', description: 'Convenience peer name.' },
496
+ wakeup: { type: 'boolean', default: false, description: 'Activate an inactive receiver when true; false only stores mail. Both deliver normally to an active receiver.' },
497
+ content: { type: 'array', minItems: 1, maxItems: 16, description: 'Ordered content selections. Each selection requires a range. Do not combine with message/prompt.', items: {
498
+ type: 'object', required: ['kind', 'range'], additionalProperties: false, properties: {
499
+ kind: { type: 'string', enum: ['summary', 'text_history', 'tool_history'] },
500
+ range: { type: 'object', additionalProperties: false, description: 'Use last:N, or the 1-based inclusive start/end range in the selected kind. Text entries exclude hidden input; tool entries are complete call/result pairs; summary uses the combined visible timeline.', properties: {
501
+ last: { type: 'integer', minimum: 1 }, start: { type: 'integer', minimum: 1 }, end: { type: 'integer', minimum: 1 },
502
+ } },
503
+ text: { type: 'string', description: 'Your concise summary of the selected source range, required only for kind=summary.' },
504
+ },
505
+ } },
506
+ message: { type: 'string', description: 'Explicit free text, for a new directive or correction. Cannot be combined with content.' },
507
+ prompt: { type: 'string', description: 'Legacy alias for message.' },
508
+ kind: { type: 'string', enum: ['directive', 'question', 'result', 'handoff'] }, reply_to: { type: 'string' }, correlation_id: { type: 'string' },
509
+ }, []),
494
510
  t('subagent_result', 'Return the persisted transcript, mailbox summary, status, and latest result for a peer agent. Target by exact id (preferred) or name.', { id: { type: 'string', description: 'Exact peer id from subagent_list.' }, name: { type: 'string', description: 'Convenience peer name.' } }, []),
495
511
  t('subagent_close', 'Close a same-conversation peer. Root can close any peer; a peer can close only itself. Target by exact id (preferred) or name.', { id: { type: 'string', description: 'Exact peer id from subagent_list.' }, name: { type: 'string', description: 'Convenience peer name.' } }, []),
496
512
  t('linked_plan', 'Read or incrementally update the current conversation linked Markdown plan. Update requires expected_revision. Prefer append or old_text/new_text for local changes; markdown remains the legacy full replacement path.', { action: { type: 'string', enum: ['get', 'update'] }, markdown: { type: 'string' }, append: { type: 'string' }, old_text: { type: 'string' }, new_text: { type: 'string' }, replace_all: { type: 'boolean' }, expected_revision: { type: 'number' } }, ['action']),