@namzu/cli 28.1.0 → 29.0.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 (90) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/dist/integrations/subagents/runtime.d.ts +25 -0
  3. package/dist/integrations/subagents/runtime.d.ts.map +1 -1
  4. package/dist/integrations/subagents/runtime.js +40 -4
  5. package/dist/integrations/subagents/runtime.js.map +1 -1
  6. package/dist/tui/AgentExplorer.d.ts +28 -4
  7. package/dist/tui/AgentExplorer.d.ts.map +1 -1
  8. package/dist/tui/AgentExplorer.js +177 -35
  9. package/dist/tui/AgentExplorer.js.map +1 -1
  10. package/dist/tui/App.d.ts.map +1 -1
  11. package/dist/tui/App.js +235 -21
  12. package/dist/tui/App.js.map +1 -1
  13. package/dist/tui/Composer.d.ts.map +1 -1
  14. package/dist/tui/Composer.js +10 -4
  15. package/dist/tui/Composer.js.map +1 -1
  16. package/dist/tui/ComposerFrame.d.ts +10 -1
  17. package/dist/tui/ComposerFrame.d.ts.map +1 -1
  18. package/dist/tui/ComposerFrame.js +27 -4
  19. package/dist/tui/ComposerFrame.js.map +1 -1
  20. package/dist/tui/EffortSlider.d.ts +54 -0
  21. package/dist/tui/EffortSlider.d.ts.map +1 -0
  22. package/dist/tui/EffortSlider.js +131 -0
  23. package/dist/tui/EffortSlider.js.map +1 -0
  24. package/dist/tui/LiveActivity.d.ts +23 -3
  25. package/dist/tui/LiveActivity.d.ts.map +1 -1
  26. package/dist/tui/LiveActivity.js +23 -17
  27. package/dist/tui/LiveActivity.js.map +1 -1
  28. package/dist/tui/Markdown.d.ts +29 -2
  29. package/dist/tui/Markdown.d.ts.map +1 -1
  30. package/dist/tui/Markdown.js +74 -36
  31. package/dist/tui/Markdown.js.map +1 -1
  32. package/dist/tui/StatusBar.js +1 -1
  33. package/dist/tui/StatusBar.js.map +1 -1
  34. package/dist/tui/Transcript.d.ts +24 -1
  35. package/dist/tui/Transcript.d.ts.map +1 -1
  36. package/dist/tui/Transcript.js +35 -8
  37. package/dist/tui/Transcript.js.map +1 -1
  38. package/dist/tui/ViewportBound.d.ts +48 -0
  39. package/dist/tui/ViewportBound.d.ts.map +1 -0
  40. package/dist/tui/ViewportBound.js +55 -0
  41. package/dist/tui/ViewportBound.js.map +1 -0
  42. package/dist/tui/agent-transcript-rows.d.ts +59 -0
  43. package/dist/tui/agent-transcript-rows.d.ts.map +1 -0
  44. package/dist/tui/agent-transcript-rows.js +115 -0
  45. package/dist/tui/agent-transcript-rows.js.map +1 -0
  46. package/dist/tui/agent.d.ts +39 -0
  47. package/dist/tui/agent.d.ts.map +1 -1
  48. package/dist/tui/agent.js +86 -5
  49. package/dist/tui/agent.js.map +1 -1
  50. package/dist/tui/live-window.d.ts +16 -0
  51. package/dist/tui/live-window.d.ts.map +1 -1
  52. package/dist/tui/live-window.js +50 -3
  53. package/dist/tui/live-window.js.map +1 -1
  54. package/dist/tui/markdown-block-cache.d.ts +9 -7
  55. package/dist/tui/markdown-block-cache.d.ts.map +1 -1
  56. package/dist/tui/markdown-block-cache.js +9 -7
  57. package/dist/tui/markdown-block-cache.js.map +1 -1
  58. package/dist/tui/markdown-table.d.ts +55 -0
  59. package/dist/tui/markdown-table.d.ts.map +1 -0
  60. package/dist/tui/markdown-table.js +175 -0
  61. package/dist/tui/markdown-table.js.map +1 -0
  62. package/dist/tui/markdown-wrap.d.ts +53 -0
  63. package/dist/tui/markdown-wrap.d.ts.map +1 -0
  64. package/dist/tui/markdown-wrap.js +189 -0
  65. package/dist/tui/markdown-wrap.js.map +1 -0
  66. package/dist/tui/markdownParser.d.ts +6 -0
  67. package/dist/tui/markdownParser.d.ts.map +1 -1
  68. package/dist/tui/markdownParser.js +49 -10
  69. package/dist/tui/markdownParser.js.map +1 -1
  70. package/dist/tui/slashCommands.d.ts.map +1 -1
  71. package/dist/tui/slashCommands.js +3 -0
  72. package/dist/tui/slashCommands.js.map +1 -1
  73. package/dist/tui/stream-blocks.d.ts.map +1 -1
  74. package/dist/tui/stream-blocks.js +32 -3
  75. package/dist/tui/stream-blocks.js.map +1 -1
  76. package/dist/tui/theme.d.ts +13 -0
  77. package/dist/tui/theme.d.ts.map +1 -1
  78. package/dist/tui/theme.js +16 -0
  79. package/dist/tui/theme.js.map +1 -1
  80. package/dist/tui/types.d.ts +7 -2
  81. package/dist/tui/types.d.ts.map +1 -1
  82. package/dist/tui/units.d.ts +6 -0
  83. package/dist/tui/units.d.ts.map +1 -0
  84. package/dist/tui/units.js +20 -0
  85. package/dist/tui/units.js.map +1 -0
  86. package/dist/tui/web-activity.d.ts +58 -0
  87. package/dist/tui/web-activity.d.ts.map +1 -0
  88. package/dist/tui/web-activity.js +105 -0
  89. package/dist/tui/web-activity.js.map +1 -0
  90. package/package.json +8 -8
package/dist/tui/agent.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createCurrentCredentialReader } from '../integrations/providers/current-credential.js';
2
2
  import { createWebSearchTool, resolveWebSearch, webSearchLabel, } from '../integrations/web/search.js';
3
+ import { countListedResults, webActivityFromInput, webActivityKind, webCallTitle, } from './web-activity.js';
3
4
  /**
4
5
  * TUI agent session — provider-direct, tool-enabled.
5
6
  *
@@ -1021,6 +1022,10 @@ export async function createAgentSession(prefs, detected, options = {}) {
1021
1022
  // Agent tool. Keying the review channel by the executing turn keeps two
1022
1023
  // concurrent sends from borrowing each other's prompt or approval latch.
1023
1024
  const delegatedResumeHandlers = new Map();
1025
+ // Beside each borrowed handler, the switch that sends it the batches it
1026
+ // would otherwise never see (`reviewAllowedCalls`): without it a child's
1027
+ // rule-allowed or already-approved call ran past a handler in plan mode.
1028
+ const delegatedReviewAllowedCalls = new Map();
1024
1029
  const goalToolNames = new Set(SESSION_GOAL_TOOL_NAMES);
1025
1030
  if (options.sessionGoals) {
1026
1031
  registry.register(buildSessionGoalTools(options.sessionGoals, (turnId) => goalAuthorities.get(turnId)));
@@ -1211,6 +1216,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1211
1216
  },
1212
1217
  sandboxWorkspace,
1213
1218
  resolveResumeHandler: (turnId) => delegatedResumeHandlers.get(turnId),
1219
+ resolveReviewAllowedCalls: (turnId) => delegatedReviewAllowedCalls.get(turnId),
1214
1220
  resolveWaitForInbound: (turnId) => delegatedInputWaiters.get(turnId),
1215
1221
  ...(sandbox.provider ? { sandboxProvider: sandbox.provider } : {}),
1216
1222
  ...(options.sandbox?.teardownTimeoutMs !== undefined
@@ -1647,7 +1653,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1647
1653
  * checkpoint cannot carry. `resumeDurable` and `resumePaused` differ only
1648
1654
  * in where the log and its lease come from.
1649
1655
  */
1650
- const kernelResume = ({ entry, sessionLog, checkpointStore, lease, signal, checkpointId, listener, }) => operations.promise(signal, async (ownedSignal) => {
1656
+ const kernelResume = ({ entry, sessionLog, checkpointStore, lease, signal, checkpointId, listener, permissionMode, currentPermissionMode, }) => operations.promise(signal, async (ownedSignal) => {
1651
1657
  const selectTaskStore = beginTaskStoreReadout();
1652
1658
  // The turn's own limits, as its `turn_started` recorded them.
1653
1659
  const resumedLimits = (await readStoredTurnGuards(sessionLog, entry.turnId)) ?? resolveTurnGuards(options.limits);
@@ -1684,12 +1690,32 @@ export async function createAgentSession(prefs, detected, options = {}) {
1684
1690
  ]
1685
1691
  .filter((s) => Boolean(s))
1686
1692
  .join('\n\n') || undefined;
1687
- const resumeHandler = makeResumeHandler(approval, undefined, options.permissionMode, (name, input) => isPromptExempt(registry, name, input), { unattendedSandboxEscape });
1688
1693
  if (delegatedResumeHandlers.has(entry.turnId)) {
1689
1694
  throw new Error(`Turn ${entry.turnId} already owns a delegated review channel.`);
1690
1695
  }
1696
+ // The resumed turn is decided under the caller's mode, else the
1697
+ // session's, read at every decision exactly as a new turn's is: the
1698
+ // interactive terminal creates its session without a mode and hands
1699
+ // one to each turn, so the session's alone left a TUI `/resume` in
1700
+ // plan mode under `auto`. Plan is stricter than the rules, so while it
1701
+ // holds a batch a rule allows, or an approval earlier in the turn
1702
+ // covers, still reaches the handler that refuses it — in this turn and
1703
+ // in every turn it delegates. No prompt stands behind the handler:
1704
+ // nobody answers a drainer's terminal, and `/resume` never asked.
1705
+ const modeControl = createLiveModeControl({
1706
+ initial: permissionMode ?? options.permissionMode ?? 'auto',
1707
+ ...(currentPermissionMode ? { read: currentPermissionMode } : {}),
1708
+ ...(recordedModes.has(String(entry.sessionId))
1709
+ ? { recorded: recordedModes.get(String(entry.sessionId)) }
1710
+ : {}),
1711
+ handlerFor: (mode) => makeResumeHandler(approval, undefined, mode, reviewExemptionFor(mode, registry, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }),
1712
+ });
1713
+ const resumeHandler = modeControl.handler;
1714
+ const reviewAllowedCalls = modeControl.reviewAllowedCalls;
1715
+ liveModeControls.add(modeControl);
1691
1716
  const turnScope = { ...entry, topicId: scope.topicId };
1692
1717
  delegatedResumeHandlers.set(entry.turnId, resumeHandler);
1718
+ delegatedReviewAllowedCalls.set(entry.turnId, reviewAllowedCalls);
1693
1719
  delegationScopes.set(entry.turnId, turnScope);
1694
1720
  delegationLimits.set(entry.turnId, resumedLimits);
1695
1721
  const turnTaskStore = selectTaskStore(turnScope);
@@ -1762,6 +1788,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
1762
1788
  // prompt would block the pass forever on a turn nobody is watching.
1763
1789
  // The gate's deny rules still apply.
1764
1790
  resumeHandler,
1791
+ approvalPolicyName: modeControl.initialName,
1792
+ onApprovalPolicy: (box) => modeControl.attach(box),
1793
+ reviewAllowedCalls,
1765
1794
  signal: ownedSignal,
1766
1795
  // Attribution comes from the ENTRY, not from this session: the turn
1767
1796
  // belongs to whoever started it, and stamping the drainer's ids onto
@@ -1797,8 +1826,11 @@ export async function createAgentSession(prefs, detected, options = {}) {
1797
1826
  });
1798
1827
  }
1799
1828
  finally {
1829
+ liveModeControls.delete(modeControl);
1830
+ recordedModes.set(String(entry.sessionId), modeControl.current());
1800
1831
  if (delegatedResumeHandlers.get(entry.turnId) === resumeHandler) {
1801
1832
  delegatedResumeHandlers.delete(entry.turnId);
1833
+ delegatedReviewAllowedCalls.delete(entry.turnId);
1802
1834
  delegationScopes.delete(entry.turnId);
1803
1835
  delegationLimits.delete(entry.turnId);
1804
1836
  await subagentRuntime?.releaseTurn(entry.turnId);
@@ -1811,7 +1843,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1811
1843
  * headless caller renders a resumed turn exactly as it rendered the first
1812
1844
  * segment.
1813
1845
  */
1814
- const resumePausedStream = ({ turnId, checkpointId, signal, }) => {
1846
+ const resumePausedStream = ({ turnId, checkpointId, signal, permissionMode, currentPermissionMode, }) => {
1815
1847
  const queue = [];
1816
1848
  let wake;
1817
1849
  let settled = false;
@@ -1829,6 +1861,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
1829
1861
  sessionLog,
1830
1862
  ...(signal ? { signal } : {}),
1831
1863
  ...(checkpointId !== undefined ? { checkpointId: checkpointId } : {}),
1864
+ ...(permissionMode ? { permissionMode } : {}),
1865
+ ...(currentPermissionMode ? { currentPermissionMode } : {}),
1832
1866
  listener: (event) => {
1833
1867
  queue.push(event);
1834
1868
  wake?.();
@@ -2023,7 +2057,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
2023
2057
  ...(recordedModes.has(String(turnScope.sessionId))
2024
2058
  ? { recorded: recordedModes.get(String(turnScope.sessionId)) }
2025
2059
  : {}),
2026
- handlerFor: (mode) => makeResumeHandler(approval, opts?.onPermission, mode, (name, input) => isPromptExempt(runTools, name, input), { unattendedSandboxEscape }),
2060
+ handlerFor: (mode) => makeResumeHandler(approval, opts?.onPermission, mode, reviewExemptionFor(mode, runTools, (input) => subagentRuntime?.launchesReadOnlyAgent(input) === true), { unattendedSandboxEscape }),
2027
2061
  });
2028
2062
  const resumeHandler = modeControl.handler;
2029
2063
  liveModeControls.add(modeControl);
@@ -2043,6 +2077,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
2043
2077
  }
2044
2078
  claimed.add(turnId);
2045
2079
  delegatedResumeHandlers.set(turnId, resumeHandler);
2080
+ delegatedReviewAllowedCalls.set(turnId, modeControl.reviewAllowedCalls);
2046
2081
  if (opts?.onModelSwitch)
2047
2082
  modelSwitchHandlers.set(turnId, opts.onModelSwitch);
2048
2083
  if (opts?.waitForInbound)
@@ -2331,6 +2366,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
2331
2366
  if (delegatedResumeHandlers.get(turnId) !== resumeHandler)
2332
2367
  continue;
2333
2368
  delegatedResumeHandlers.delete(turnId);
2369
+ delegatedReviewAllowedCalls.delete(turnId);
2334
2370
  modelSwitchHandlers.delete(turnId);
2335
2371
  delegatedInputWaiters.delete(turnId);
2336
2372
  delegationScopes.delete(turnId);
@@ -2986,6 +3022,27 @@ export function makeResumeHandler(approval, onPermission, mode = onPermission ?
2986
3022
  * registry does not know.
2987
3023
  */
2988
3024
  export const isPromptExempt = isReviewExempt;
3025
+ /** The delegation tool whose read-only launches {@link reviewExemptionFor} lets through. */
3026
+ export const AGENT_LAUNCH_TOOL = 'Agent';
3027
+ /**
3028
+ * What skips review under `mode`: the kernel's exemption, and — in every mode
3029
+ * but `strict` — an `Agent` call that starts a read-only child on the
3030
+ * session's own provider and model (`launchesReadOnlyAgent`).
3031
+ *
3032
+ * Starting such a child changes nothing by itself: its roster holds no tool
3033
+ * that writes, and every call it makes is reviewed under this same mode as
3034
+ * before. What the operator stops being asked is "may I start a reader?".
3035
+ * A launch that is not read-only, or that picks another provider, model or
3036
+ * effort, is asked about as before. `strict` still refuses it, since no rule
3037
+ * allowed it; `plan` lets it start, since reading is what plan mode is for.
3038
+ * An operator who wants every launch asked about writes
3039
+ * `permissions: { Agent: "ask" }`: an `ask` rule is an explicit review, which
3040
+ * no exemption skips.
3041
+ */
3042
+ export function reviewExemptionFor(mode, registry, launchesReadOnlyAgent) {
3043
+ return (name, input) => isPromptExempt(registry, name, input) ||
3044
+ (mode !== 'strict' && name === AGENT_LAUNCH_TOOL && launchesReadOnlyAgent(input));
3045
+ }
2989
3046
  /** The exempt roster, sorted, for the surface that has to NAME it. */
2990
3047
  export function promptExemptToolNames(registry) {
2991
3048
  return registry
@@ -3009,12 +3066,23 @@ export function toAgentEvent(event, presenter) {
3009
3066
  toolUseId: event.tool.id,
3010
3067
  toolName: 'web_search',
3011
3068
  };
3069
+ // A page action names an address rather than a query, and reads as
3070
+ // a fetch; everything else is a search, named once the provider says.
3071
+ const web = event.tool.url && !event.tool.query
3072
+ ? { kind: 'fetch', target: event.tool.url, hosted: true }
3073
+ : {
3074
+ kind: 'search',
3075
+ hosted: true,
3076
+ ...(event.tool.query ? { target: event.tool.query } : {}),
3077
+ ...(event.tool.results !== undefined ? { results: event.tool.results } : {}),
3078
+ };
3012
3079
  return event.tool.status === 'running'
3013
3080
  ? {
3014
3081
  ...common,
3015
3082
  kind: 'tool-start',
3016
- summary: 'Web search',
3083
+ summary: webCallTitle(web),
3017
3084
  standalone: true,
3085
+ web,
3018
3086
  }
3019
3087
  : {
3020
3088
  ...common,
@@ -3022,6 +3090,7 @@ export function toAgentEvent(event, presenter) {
3022
3090
  summary: event.tool.status === 'completed' ? '' : 'Provider-hosted search failed',
3023
3091
  isError: event.tool.status !== 'completed',
3024
3092
  output: event.tool.status,
3093
+ web,
3025
3094
  };
3026
3095
  }
3027
3096
  case 'text_delta':
@@ -3061,6 +3130,10 @@ export function toAgentEvent(event, presenter) {
3061
3130
  : {}),
3062
3131
  };
3063
3132
  })(),
3133
+ ...(() => {
3134
+ const web = webActivityFromInput(event.toolName, event.input);
3135
+ return web ? { web } : {};
3136
+ })(),
3064
3137
  };
3065
3138
  case 'tool_progress':
3066
3139
  return {
@@ -3098,6 +3171,13 @@ export function toAgentEvent(event, presenter) {
3098
3171
  ...(withoutRepeatedSummary && withoutRepeatedSummary.length > 0
3099
3172
  ? { detail: withoutRepeatedSummary }
3100
3173
  : {}),
3174
+ ...(() => {
3175
+ const kind = webActivityKind(event.toolName);
3176
+ if (!kind)
3177
+ return {};
3178
+ const results = kind === 'search' && !event.isError ? countListedResults(event.result) : undefined;
3179
+ return { web: { kind, ...(results !== undefined ? { results } : {}) } };
3180
+ })(),
3101
3181
  };
3102
3182
  }
3103
3183
  case 'token_usage_updated':
@@ -3113,6 +3193,7 @@ export function toAgentEvent(event, presenter) {
3113
3193
  sessionId: event.sessionId,
3114
3194
  ...(event.turnId ? { turnId: event.turnId } : {}),
3115
3195
  totalTokens: event.usage.totalTokens,
3196
+ outputTokens: event.usage.completionTokens,
3116
3197
  ...(event.budget ? { budget: event.budget } : {}),
3117
3198
  cost: event.cost,
3118
3199
  ...(event.contextTokens !== undefined ? { contextTokens: event.contextTokens } : {}),