@namzu/cli 24.0.0 → 25.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.
- package/CHANGELOG.md +776 -0
- package/README.md +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/drain.d.ts +0 -23
- package/dist/commands/drain.d.ts.map +1 -1
- package/dist/commands/drain.js +23 -9
- package/dist/commands/drain.js.map +1 -1
- package/dist/commands/resident-flags.d.ts +6 -1
- package/dist/commands/resident-flags.d.ts.map +1 -1
- package/dist/commands/resident-flags.js +39 -1
- package/dist/commands/resident-flags.js.map +1 -1
- package/dist/commands/resident-learning.d.ts +6 -0
- package/dist/commands/resident-learning.d.ts.map +1 -0
- package/dist/commands/resident-learning.js +293 -0
- package/dist/commands/resident-learning.js.map +1 -0
- package/dist/commands/resident.d.ts.map +1 -1
- package/dist/commands/resident.js +42 -4
- package/dist/commands/resident.js.map +1 -1
- package/dist/commands/run-flags.d.ts +2 -2
- package/dist/commands/run-flags.d.ts.map +1 -1
- package/dist/commands/run-flags.js +6 -6
- package/dist/commands/run-flags.js.map +1 -1
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +9 -10
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +3 -2
- package/dist/commands/run.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +27 -4
- package/dist/config/load.js.map +1 -1
- package/dist/config/run-limits.d.ts +11 -0
- package/dist/config/run-limits.d.ts.map +1 -0
- package/dist/config/run-limits.js +64 -0
- package/dist/config/run-limits.js.map +1 -0
- package/dist/config/schema.d.ts +12 -4
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/context/runtime-message.d.ts.map +1 -1
- package/dist/context/runtime-message.js +2 -0
- package/dist/context/runtime-message.js.map +1 -1
- package/dist/integrations/plugins/runtime.d.ts +19 -1
- package/dist/integrations/plugins/runtime.d.ts.map +1 -1
- package/dist/integrations/plugins/runtime.js +89 -1
- package/dist/integrations/plugins/runtime.js.map +1 -1
- package/dist/integrations/plugins/settings.d.ts +15 -0
- package/dist/integrations/plugins/settings.d.ts.map +1 -0
- package/dist/integrations/plugins/settings.js +106 -0
- package/dist/integrations/plugins/settings.js.map +1 -0
- package/dist/integrations/resident/inspection.d.ts +10 -0
- package/dist/integrations/resident/inspection.d.ts.map +1 -0
- package/dist/integrations/resident/inspection.js +263 -0
- package/dist/integrations/resident/inspection.js.map +1 -0
- package/dist/integrations/resident/learning-sources.d.ts +4 -0
- package/dist/integrations/resident/learning-sources.d.ts.map +1 -0
- package/dist/integrations/resident/learning-sources.js +79 -0
- package/dist/integrations/resident/learning-sources.js.map +1 -0
- package/dist/integrations/resident/learning-storage.d.ts +5 -0
- package/dist/integrations/resident/learning-storage.d.ts.map +1 -0
- package/dist/integrations/resident/learning-storage.js +51 -0
- package/dist/integrations/resident/learning-storage.js.map +1 -0
- package/dist/integrations/resident/runner-launch.d.ts +5 -0
- package/dist/integrations/resident/runner-launch.d.ts.map +1 -1
- package/dist/integrations/resident/runner-launch.js +2 -0
- package/dist/integrations/resident/runner-launch.js.map +1 -1
- package/dist/integrations/resident/runner-store.d.ts.map +1 -1
- package/dist/integrations/resident/runner-store.js +2 -4
- package/dist/integrations/resident/runner-store.js.map +1 -1
- package/dist/integrations/resident/runner-worker.js +3 -0
- package/dist/integrations/resident/runner-worker.js.map +1 -1
- package/dist/integrations/resident/session-step.d.ts +6 -1
- package/dist/integrations/resident/session-step.d.ts.map +1 -1
- package/dist/integrations/resident/session-step.js +110 -14
- package/dist/integrations/resident/session-step.js.map +1 -1
- package/dist/integrations/resident/storage.d.ts.map +1 -1
- package/dist/integrations/resident/storage.js +7 -6
- package/dist/integrations/resident/storage.js.map +1 -1
- package/dist/integrations/resident/tool-evidence.d.ts +6 -0
- package/dist/integrations/resident/tool-evidence.d.ts.map +1 -0
- package/dist/integrations/resident/tool-evidence.js +117 -0
- package/dist/integrations/resident/tool-evidence.js.map +1 -0
- package/dist/integrations/resident/verification.d.ts +11 -0
- package/dist/integrations/resident/verification.d.ts.map +1 -0
- package/dist/integrations/resident/verification.js +109 -0
- package/dist/integrations/resident/verification.js.map +1 -0
- package/dist/integrations/sessions/compaction-evidence.d.ts +5 -0
- package/dist/integrations/sessions/compaction-evidence.d.ts.map +1 -0
- package/dist/integrations/sessions/compaction-evidence.js +59 -0
- package/dist/integrations/sessions/compaction-evidence.js.map +1 -0
- package/dist/integrations/sessions/context-inventory.js +1 -1
- package/dist/integrations/sessions/context-inventory.js.map +1 -1
- package/dist/integrations/sessions/conversation-search.d.ts +56 -8
- package/dist/integrations/sessions/conversation-search.d.ts.map +1 -1
- package/dist/integrations/sessions/conversation-search.js +600 -63
- package/dist/integrations/sessions/conversation-search.js.map +1 -1
- package/dist/integrations/sessions/database.d.ts +5 -0
- package/dist/integrations/sessions/database.d.ts.map +1 -0
- package/dist/integrations/sessions/database.js +34 -0
- package/dist/integrations/sessions/database.js.map +1 -0
- package/dist/integrations/sessions/evidence-page-validation.d.ts +8 -0
- package/dist/integrations/sessions/evidence-page-validation.d.ts.map +1 -0
- package/dist/integrations/sessions/evidence-page-validation.js +74 -0
- package/dist/integrations/sessions/evidence-page-validation.js.map +1 -0
- package/dist/integrations/sessions/evidence-recall.d.ts +5 -0
- package/dist/integrations/sessions/evidence-recall.d.ts.map +1 -0
- package/dist/integrations/sessions/evidence-recall.js +173 -0
- package/dist/integrations/sessions/evidence-recall.js.map +1 -0
- package/dist/integrations/sessions/paths.d.ts +7 -0
- package/dist/integrations/sessions/paths.d.ts.map +1 -0
- package/dist/integrations/sessions/paths.js +14 -0
- package/dist/integrations/sessions/paths.js.map +1 -0
- package/dist/integrations/sessions/run-discovery.d.ts +24 -0
- package/dist/integrations/sessions/run-discovery.d.ts.map +1 -0
- package/dist/integrations/sessions/run-discovery.js +142 -0
- package/dist/integrations/sessions/run-discovery.js.map +1 -0
- package/dist/integrations/sessions/store.d.ts +8 -18
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/store.js +14 -39
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/sessions/transcript-export.d.ts +0 -1
- package/dist/integrations/sessions/transcript-export.d.ts.map +1 -1
- package/dist/integrations/sessions/transcript-export.js +3 -2
- package/dist/integrations/sessions/transcript-export.js.map +1 -1
- package/dist/integrations/sessions/turn-evidence.d.ts +0 -9
- package/dist/integrations/sessions/turn-evidence.d.ts.map +1 -1
- package/dist/integrations/sessions/turn-evidence.js +5 -2
- package/dist/integrations/sessions/turn-evidence.js.map +1 -1
- package/dist/integrations/state/report.d.ts.map +1 -1
- package/dist/integrations/state/report.js +78 -21
- package/dist/integrations/state/report.js.map +1 -1
- package/dist/integrations/subagents/parent.d.ts +3 -2
- package/dist/integrations/subagents/parent.d.ts.map +1 -1
- package/dist/integrations/subagents/parent.js +11 -6
- package/dist/integrations/subagents/parent.js.map +1 -1
- package/dist/integrations/subagents/policy.d.ts +2 -5
- package/dist/integrations/subagents/policy.d.ts.map +1 -1
- package/dist/integrations/subagents/policy.js +2 -5
- package/dist/integrations/subagents/policy.js.map +1 -1
- package/dist/integrations/subagents/runtime.d.ts +7 -0
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +9 -7
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/permissions/rules.d.ts +2 -8
- package/dist/permissions/rules.d.ts.map +1 -1
- package/dist/permissions/rules.js +19 -10
- package/dist/permissions/rules.js.map +1 -1
- package/dist/tui/App.d.ts +0 -12
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +235 -15
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/ChoicePicker.d.ts +2 -1
- package/dist/tui/ChoicePicker.d.ts.map +1 -1
- package/dist/tui/ChoicePicker.js +10 -8
- package/dist/tui/ChoicePicker.js.map +1 -1
- package/dist/tui/ToolOutputViewer.js +1 -1
- package/dist/tui/ToolOutputViewer.js.map +1 -1
- package/dist/tui/agent.d.ts +20 -3
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +159 -31
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/conversation-evidence-view.d.ts +6 -0
- package/dist/tui/conversation-evidence-view.d.ts.map +1 -0
- package/dist/tui/conversation-evidence-view.js +96 -0
- package/dist/tui/conversation-evidence-view.js.map +1 -0
- package/dist/tui/conversation-history.d.ts +3 -1
- package/dist/tui/conversation-history.d.ts.map +1 -1
- package/dist/tui/conversation-history.js +16 -1
- package/dist/tui/conversation-history.js.map +1 -1
- package/dist/tui/markdownParser.d.ts.map +1 -1
- package/dist/tui/markdownParser.js +2 -1
- package/dist/tui/markdownParser.js.map +1 -1
- package/dist/tui/plugin-view.d.ts +9 -0
- package/dist/tui/plugin-view.d.ts.map +1 -0
- package/dist/tui/plugin-view.js +59 -0
- package/dist/tui/plugin-view.js.map +1 -0
- package/dist/tui/run-interruption.d.ts +2 -2
- package/dist/tui/run-interruption.d.ts.map +1 -1
- package/dist/tui/run-interruption.js +5 -1
- package/dist/tui/run-interruption.js.map +1 -1
- package/dist/tui/run-limits-settings.d.ts +39 -0
- package/dist/tui/run-limits-settings.d.ts.map +1 -0
- package/dist/tui/run-limits-settings.js +88 -0
- package/dist/tui/run-limits-settings.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +6 -1
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +40 -9
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/types.d.ts +4 -2
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/tui/App.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { resolveRunGuards } from '../config/run-limits.js';
|
|
3
|
+
import { RUN_LIMIT_FIELDS, runLimitCommands } from './run-limits-settings.js';
|
|
4
|
+
import { CliPathBuilder } from '../integrations/sessions/paths.js';
|
|
2
5
|
/**
|
|
3
6
|
* TUI root. Composes the banner, transcript, composer, status bar, and
|
|
4
7
|
* the first-run provider picker overlay.
|
|
@@ -15,7 +18,7 @@ import { discoverProviders } from '../integrations/providers/discover.js';
|
|
|
15
18
|
import { ProviderSetup } from './ProviderSetup.js';
|
|
16
19
|
import { ToolOutputViewer } from './ToolOutputViewer.js';
|
|
17
20
|
import { join, relative } from 'node:path';
|
|
18
|
-
import { DEFAULT_MAX_GOAL_ROUNDS,
|
|
21
|
+
import { DEFAULT_MAX_GOAL_ROUNDS, DiskMessageFeedbackStore, GoalRoundLimitError, HostCommandRegistry, RunCancelled, SessionGoalActivation, StaleGoalError, asSessionId, createAssistantMessage, createUserMessage, generateRunId, generateSessionId, isCompactionMessage, kernelHostCommands, } from '@namzu/sdk';
|
|
19
22
|
import { Box, Text, useApp, useInput, useStdout, useWindowSize } from 'ink';
|
|
20
23
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
21
24
|
import { pinTrustedProjectPath, resolveTrustedProjectContext, } from '../config/trusted-project-context.js';
|
|
@@ -36,6 +39,7 @@ import { discoverUserCommands } from '../user-commands/store.js';
|
|
|
36
39
|
import { AgentCockpit, AgentTaskPanel, AgentTranscript, activeSubagentCohorts, agentCockpitIsWide, agentPhasePageSize, agentPhases, agentPickerPageSize, agentTranscriptPageSize, agentWorkflowPageSize, agentWorkflows, maxAgentTranscriptTailOffset, } from './AgentExplorer.js';
|
|
37
40
|
import { BrandHeader } from './BrandHeader.js';
|
|
38
41
|
import { ChoicePicker } from './ChoicePicker.js';
|
|
42
|
+
import { emptyPluginReport, pluginDetails, pluginOption, pluginStartupState } from './plugin-view.js';
|
|
39
43
|
import { Composer, suggestionWindowSize, } from './Composer.js';
|
|
40
44
|
import { ComposerFrame } from './ComposerFrame.js';
|
|
41
45
|
import { CopyPicker } from './CopyPicker.js';
|
|
@@ -48,13 +52,14 @@ import { StatusBar } from './StatusBar.js';
|
|
|
48
52
|
import { TaskList } from './TaskList.js';
|
|
49
53
|
import { TextPrompt } from './TextPrompt.js';
|
|
50
54
|
import { modelCatalogueView } from './model-catalogue-view.js';
|
|
55
|
+
import { conversationEvidenceView } from './conversation-evidence-view.js';
|
|
51
56
|
import { Transcript, willCollapse } from './Transcript.js';
|
|
52
57
|
import { TrustPrompt } from './TrustPrompt.js';
|
|
53
58
|
import { createAgentSession, describeProviderModels, probeAgentSession, } from './agent.js';
|
|
54
59
|
import { choicePickerWindowSize, filterChoiceOptions, moveChoiceSelection, } from './choice-selection.js';
|
|
55
60
|
import { keepRecentRows } from './compact-transcript.js';
|
|
56
61
|
import { approvalIsDeliberate } from './consent-timing.js';
|
|
57
|
-
import { planTurnPublication } from './conversation-history.js';
|
|
62
|
+
import { assistantTranscriptTexts, planTurnPublication } from './conversation-history.js';
|
|
58
63
|
import { copyTargetsForResponse } from './copy-targets.js';
|
|
59
64
|
import { editablePrompts } from './edit-prompts.js';
|
|
60
65
|
import { editDraftInExternalEditor } from './external-editor.js';
|
|
@@ -171,13 +176,11 @@ export function projectConversation(messages, nextId, readableUserTexts = []) {
|
|
|
171
176
|
];
|
|
172
177
|
}
|
|
173
178
|
if (message.role === 'assistant') {
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
},
|
|
180
|
-
];
|
|
179
|
+
return assistantTranscriptTexts(message).map((content) => ({
|
|
180
|
+
id: nextId(),
|
|
181
|
+
role: 'assistant',
|
|
182
|
+
content,
|
|
183
|
+
}));
|
|
181
184
|
}
|
|
182
185
|
if (message.role === 'system' && isCompactionMessage(message.content)) {
|
|
183
186
|
return [
|
|
@@ -379,6 +382,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
379
382
|
const permissionModeSourceRef = useRef(ctx.skipPermissions === true ? 'launch-bypass' : 'default');
|
|
380
383
|
const [reasoningEffort, setReasoningEffortState] = useState();
|
|
381
384
|
const reasoningEffortRef = useRef(undefined);
|
|
385
|
+
const runLimitsOverrideRef = useRef({});
|
|
382
386
|
const setReasoningEffort = useCallback((next) => {
|
|
383
387
|
reasoningEffortRef.current = next;
|
|
384
388
|
setReasoningEffortState(next);
|
|
@@ -1106,7 +1110,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1106
1110
|
// so the feedback store correctly refused every TUI rating as unverifiable.
|
|
1107
1111
|
// Bind both trees to the exact conversation captured with the streamed
|
|
1108
1112
|
// message; `/resume` may change the active scope before a delayed click.
|
|
1109
|
-
const sessionDir = new
|
|
1113
|
+
const sessionDir = new CliPathBuilder(sessions.root).sessionDir(sessions.projectId, sessionId);
|
|
1110
1114
|
const store = new DiskMessageFeedbackStore({
|
|
1111
1115
|
rootDir: join(sessionDir, 'feedback'),
|
|
1112
1116
|
runsDir: join(sessionDir, 'runs'),
|
|
@@ -1321,6 +1325,70 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1321
1325
|
return;
|
|
1322
1326
|
if (picker.options[index]?.disabledReason)
|
|
1323
1327
|
return;
|
|
1328
|
+
if (picker.kind === 'plugin' || picker.kind === 'plugin-action') {
|
|
1329
|
+
if (picker.busy || picker.owner !== session)
|
|
1330
|
+
return;
|
|
1331
|
+
if (picker.kind === 'plugin') {
|
|
1332
|
+
const child = pluginActionPicker(picker.owner, value);
|
|
1333
|
+
setSelectedChoice(0);
|
|
1334
|
+
setChoicePicker(child ? { ...child, back: picker } : null);
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
const plugin = picker.owner.plugins?.list().find((item) => item.name === picker.pluginName);
|
|
1338
|
+
if (!plugin || !picker.owner.plugins)
|
|
1339
|
+
return;
|
|
1340
|
+
if (value === 'details') {
|
|
1341
|
+
setChoicePicker(null);
|
|
1342
|
+
pushMessage('system', pluginDetails(plugin));
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
if (state !== 'idle' || permission) {
|
|
1346
|
+
setChoicePicker({
|
|
1347
|
+
...picker,
|
|
1348
|
+
notice: 'Wait for the active turn and permission prompts to finish.',
|
|
1349
|
+
});
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
const enabled = value === 'enable';
|
|
1353
|
+
const remembering = value === 'remember';
|
|
1354
|
+
const pending = {
|
|
1355
|
+
...picker,
|
|
1356
|
+
busy: true,
|
|
1357
|
+
notice: remembering
|
|
1358
|
+
? 'Saving plugin setting…'
|
|
1359
|
+
: enabled
|
|
1360
|
+
? 'Enabling plugin…'
|
|
1361
|
+
: 'Disabling plugin…',
|
|
1362
|
+
};
|
|
1363
|
+
setChoicePicker(pending);
|
|
1364
|
+
const generation = conversationGenRef.current;
|
|
1365
|
+
const change = remembering
|
|
1366
|
+
? picker.owner.plugins.rememberState(plugin.name)
|
|
1367
|
+
: picker.owner.plugins.setEnabled(plugin.name, enabled);
|
|
1368
|
+
void change.then(() => {
|
|
1369
|
+
if (appLifetime.signal.aborted ||
|
|
1370
|
+
conversationGenRef.current !== generation ||
|
|
1371
|
+
choicePickerRef.current !== pending)
|
|
1372
|
+
return;
|
|
1373
|
+
const updated = pluginActionPicker(picker.owner, plugin.name);
|
|
1374
|
+
setSelectedChoice(0);
|
|
1375
|
+
setChoicePicker(updated ? { ...updated, back: loadedPluginPicker(picker.owner) } : null);
|
|
1376
|
+
}, (error) => {
|
|
1377
|
+
if (appLifetime.signal.aborted ||
|
|
1378
|
+
conversationGenRef.current !== generation ||
|
|
1379
|
+
choicePickerRef.current !== pending)
|
|
1380
|
+
return;
|
|
1381
|
+
const updated = pluginActionPicker(picker.owner, plugin.name);
|
|
1382
|
+
setChoicePicker(updated
|
|
1383
|
+
? {
|
|
1384
|
+
...updated,
|
|
1385
|
+
back: loadedPluginPicker(picker.owner),
|
|
1386
|
+
notice: `Could not change plugin: ${error instanceof Error ? error.message : String(error)}`,
|
|
1387
|
+
}
|
|
1388
|
+
: null);
|
|
1389
|
+
});
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1324
1392
|
if (picker.kind === 'review-preset') {
|
|
1325
1393
|
if (reviewChoiceInFlightRef.current)
|
|
1326
1394
|
return;
|
|
@@ -1580,6 +1648,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1580
1648
|
setSelectedChoice,
|
|
1581
1649
|
setTextPrompt,
|
|
1582
1650
|
session,
|
|
1651
|
+
state,
|
|
1652
|
+
permission,
|
|
1583
1653
|
]);
|
|
1584
1654
|
const sendCopyRequest = useCallback((index) => {
|
|
1585
1655
|
const picker = copyPickerRef.current;
|
|
@@ -1785,6 +1855,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1785
1855
|
...(activeCtx.hooks ? { hooks: activeCtx.hooks } : {}),
|
|
1786
1856
|
...(activeCtx.compaction ? { compaction: activeCtx.compaction } : {}),
|
|
1787
1857
|
...(activeCtx.memory ? { memory: activeCtx.memory } : {}),
|
|
1858
|
+
...(activeCtx.limits ? { limits: activeCtx.limits } : {}),
|
|
1788
1859
|
...(activeCtx.sandbox ? { sandbox: activeCtx.sandbox } : {}),
|
|
1789
1860
|
// Somebody is at this terminal, so the model may ask them one
|
|
1790
1861
|
// question when a decision is genuinely theirs.
|
|
@@ -2696,6 +2767,12 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2696
2767
|
pushMessage('system', 'The conversation changed while this editor was open. Reopen the command in the current conversation.');
|
|
2697
2768
|
return;
|
|
2698
2769
|
}
|
|
2770
|
+
if (prompt.kind === 'run-limit') {
|
|
2771
|
+
const field = RUN_LIMIT_FIELDS.find((field) => field.key === prompt.limitKey);
|
|
2772
|
+
if (field)
|
|
2773
|
+
commandPickerSubmitRef.current(`/config limits ${field.name} ${value.trim()}`);
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2699
2776
|
if (prompt.kind === 'goal-create' || prompt.kind === 'goal-edit') {
|
|
2700
2777
|
if (value.trim())
|
|
2701
2778
|
commandPickerSubmitRef.current(`/goal ${prompt.kind === 'goal-create' ? 'set' : 'edit'} ${value.trim()}`);
|
|
@@ -3025,10 +3102,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3025
3102
|
}
|
|
3026
3103
|
switch (event.kind) {
|
|
3027
3104
|
case 'delta': {
|
|
3028
|
-
if (event.messageId &&
|
|
3105
|
+
if (event.messageId &&
|
|
3106
|
+
(st.sourceMessageId !== event.messageId ||
|
|
3107
|
+
(event.textPart && st.sourceTextPartId !== event.textPart.id))) {
|
|
3029
3108
|
if (st.sourceMessageId !== undefined)
|
|
3030
3109
|
closeAssistant();
|
|
3031
3110
|
st.sourceMessageId = event.messageId;
|
|
3111
|
+
st.sourceTextPartId = event.textPart?.id;
|
|
3032
3112
|
}
|
|
3033
3113
|
setState('thinking');
|
|
3034
3114
|
if (event.messageId && event.runId && st.sessionId) {
|
|
@@ -3167,6 +3247,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3167
3247
|
setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
|
|
3168
3248
|
break;
|
|
3169
3249
|
}
|
|
3250
|
+
const evidence = !event.isError && event.output !== undefined
|
|
3251
|
+
? conversationEvidenceView(event.toolName, event.output) : undefined;
|
|
3252
|
+
if (evidence !== undefined) {
|
|
3253
|
+
pushMessage('tool', evidence.content, false, '✓', evidence.detail, theme.status.ok, 'ctrl+o details', 'evidence');
|
|
3254
|
+
setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
|
|
3255
|
+
break;
|
|
3256
|
+
}
|
|
3170
3257
|
pushMessage('tool', done?.label ?? formatToolCall(event.toolName, event.summary), false, event.isError ? '✗' : '✓', done?.detail, event.isError ? theme.status.error : theme.status.ok, event.durationMs !== undefined
|
|
3171
3258
|
? formatElapsed(event.durationMs)
|
|
3172
3259
|
: done
|
|
@@ -3275,6 +3362,10 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3275
3362
|
pushMessage('system', `History warning (${event.source}): ${event.text}`, false, '!');
|
|
3276
3363
|
break;
|
|
3277
3364
|
case 'done': {
|
|
3365
|
+
// Persistence and memory use the settled answer, not concatenated
|
|
3366
|
+
// commentary deltas. The transcript keeps each visible part above.
|
|
3367
|
+
if (event.text !== undefined)
|
|
3368
|
+
st.text = event.text;
|
|
3278
3369
|
// `run_completed` is not synonymous with success: budgets,
|
|
3279
3370
|
// cancellation and output guardrails arrive through this event too.
|
|
3280
3371
|
// Missing remains a normal end for older producers, matching the
|
|
@@ -3287,7 +3378,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3287
3378
|
outcome: st.completed ? 'completed' : 'stopped',
|
|
3288
3379
|
};
|
|
3289
3380
|
closeAssistant();
|
|
3290
|
-
const stopNotice = describeRunStop(event.stopReason);
|
|
3381
|
+
const stopNotice = describeRunStop(event.stopReason, event.budget);
|
|
3291
3382
|
if (stopNotice)
|
|
3292
3383
|
pushMessage('system', stopNotice, false, '■');
|
|
3293
3384
|
break;
|
|
@@ -3587,6 +3678,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3587
3678
|
activeTurnInboxRef.current = inbox;
|
|
3588
3679
|
const turnPermissionMode = permissionModeRef.current;
|
|
3589
3680
|
const turnReasoningEffort = reasoningEffortRef.current;
|
|
3681
|
+
const turnRunLimits = resolveRunGuards(ctxRef.current.limits, runLimitsOverrideRef.current);
|
|
3590
3682
|
// Always carry the guarded callback. `auto` and `strict` decide before
|
|
3591
3683
|
// calling it in makeResumeHandler; retaining it is what lets a session
|
|
3592
3684
|
// launched with --yolo later return to prompt mode truthfully.
|
|
@@ -3644,6 +3736,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3644
3736
|
signal: ac.signal,
|
|
3645
3737
|
runId,
|
|
3646
3738
|
permissionMode: turnPermissionMode,
|
|
3739
|
+
limits: turnRunLimits,
|
|
3647
3740
|
...(turnReasoningEffort !== undefined ? { effort: turnReasoningEffort } : {}),
|
|
3648
3741
|
...(goalRound ? { goalRound } : {}),
|
|
3649
3742
|
// The mode above decides whether this callback is consulted.
|
|
@@ -3679,6 +3772,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3679
3772
|
}
|
|
3680
3773
|
else if (event.kind === 'delta')
|
|
3681
3774
|
st.text += event.text;
|
|
3775
|
+
else if (event.kind === 'done' && event.text !== undefined)
|
|
3776
|
+
st.text = event.text;
|
|
3682
3777
|
}
|
|
3683
3778
|
}
|
|
3684
3779
|
else {
|
|
@@ -4135,6 +4230,48 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4135
4230
|
}
|
|
4136
4231
|
setProviderSetup(true);
|
|
4137
4232
|
return;
|
|
4233
|
+
case 'run-limits-set':
|
|
4234
|
+
case 'run-limit-editor':
|
|
4235
|
+
case 'run-limits-picker': {
|
|
4236
|
+
const currentLimits = resolveRunGuards(ctxRef.current.limits, runLimitsOverrideRef.current);
|
|
4237
|
+
if (slash.kind === 'run-limit-editor') {
|
|
4238
|
+
const scope = scopeRef.current;
|
|
4239
|
+
if (!scope) {
|
|
4240
|
+
pushMessage('system', 'Run limits are available after the session starts.');
|
|
4241
|
+
return;
|
|
4242
|
+
}
|
|
4243
|
+
const field = RUN_LIMIT_FIELDS.find((field) => field.key === slash.key);
|
|
4244
|
+
textPromptTokenRef.current += 1;
|
|
4245
|
+
setTextPrompt({
|
|
4246
|
+
token: textPromptTokenRef.current,
|
|
4247
|
+
kind: 'run-limit',
|
|
4248
|
+
limitKey: slash.key,
|
|
4249
|
+
title: `${field.label} (${slash.key === 'timeoutMs' ? 'ms; ' : ''}0 = unlimited)`,
|
|
4250
|
+
placeholder: field.hint,
|
|
4251
|
+
emptyNotice: 'Enter a value, or Esc to keep the current limit.',
|
|
4252
|
+
initialValue: String(currentLimits[slash.key]),
|
|
4253
|
+
sessionId: scope.sessionId,
|
|
4254
|
+
});
|
|
4255
|
+
return;
|
|
4256
|
+
}
|
|
4257
|
+
if (slash.kind === 'run-limits-set') {
|
|
4258
|
+
runLimitsOverrideRef.current = { ...runLimitsOverrideRef.current, ...slash.limits };
|
|
4259
|
+
}
|
|
4260
|
+
const commands = runLimitCommands(resolveRunGuards(currentLimits, runLimitsOverrideRef.current));
|
|
4261
|
+
setSelectedChoice(0);
|
|
4262
|
+
setChoicePicker({
|
|
4263
|
+
kind: 'command',
|
|
4264
|
+
title: 'Run limits',
|
|
4265
|
+
notice: `${slash.kind === 'run-limits-set' ? 'Updated. ' : ''}This session · new turns and their agents. Existing runs keep their limits.`,
|
|
4266
|
+
values: commands,
|
|
4267
|
+
options: commands.map((command) => ({
|
|
4268
|
+
label: command.label,
|
|
4269
|
+
description: command.description,
|
|
4270
|
+
})),
|
|
4271
|
+
windowSize: 5,
|
|
4272
|
+
});
|
|
4273
|
+
return;
|
|
4274
|
+
}
|
|
4138
4275
|
case 'settings-picker': {
|
|
4139
4276
|
const commands = [
|
|
4140
4277
|
{
|
|
@@ -4155,6 +4292,14 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4155
4292
|
label: 'Permissions',
|
|
4156
4293
|
description: `${permissionModeLabel(effectivePermissionMode(permissionModeRef.current, session?.approvalLatched() ?? false))} · this session.`,
|
|
4157
4294
|
},
|
|
4295
|
+
{
|
|
4296
|
+
name: 'config limits',
|
|
4297
|
+
label: 'Run limits',
|
|
4298
|
+
description: runLimitCommands(resolveRunGuards(ctxRef.current.limits, runLimitsOverrideRef.current))
|
|
4299
|
+
.slice(0, 3)
|
|
4300
|
+
.map((field) => `${field.label}: ${field.description}`)
|
|
4301
|
+
.join(' · '),
|
|
4302
|
+
},
|
|
4158
4303
|
{
|
|
4159
4304
|
name: 'status',
|
|
4160
4305
|
label: 'Web & session',
|
|
@@ -4171,7 +4316,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4171
4316
|
setChoicePicker({
|
|
4172
4317
|
kind: 'command',
|
|
4173
4318
|
title: 'Configuration',
|
|
4174
|
-
notice: 'Model, reasoning and
|
|
4319
|
+
notice: 'Model, reasoning, permission and run-limit changes apply to this session.',
|
|
4175
4320
|
values: commands,
|
|
4176
4321
|
options: commands.map((command) => ({
|
|
4177
4322
|
label: command.label,
|
|
@@ -4484,6 +4629,31 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4484
4629
|
pushMessage('system', `Skills (● active):\n ${lines.join('\n ')}`);
|
|
4485
4630
|
return;
|
|
4486
4631
|
}
|
|
4632
|
+
case 'plugins': {
|
|
4633
|
+
const plugins = session?.plugins?.list() ?? [];
|
|
4634
|
+
if (slash.name) {
|
|
4635
|
+
const plugin = plugins.find((item) => item.name === slash.name);
|
|
4636
|
+
pushMessage('system', plugin
|
|
4637
|
+
? pluginDetails(plugin)
|
|
4638
|
+
: `Plugin "${slash.name}" is not loaded in this session. Use /plugins to inspect availability.`);
|
|
4639
|
+
}
|
|
4640
|
+
else if (plugins.length === 0 || !session) {
|
|
4641
|
+
pushMessage('system', emptyPluginReport(ctx.plugins, ctx.cwd));
|
|
4642
|
+
}
|
|
4643
|
+
else if (slash.list) {
|
|
4644
|
+
pushMessage('system', `Plugins\n${plugins
|
|
4645
|
+
.map((plugin) => {
|
|
4646
|
+
const option = pluginOption(plugin);
|
|
4647
|
+
return `${option.label} · ${option.description}`;
|
|
4648
|
+
})
|
|
4649
|
+
.join('\n')}`);
|
|
4650
|
+
}
|
|
4651
|
+
else {
|
|
4652
|
+
setSelectedChoice(0);
|
|
4653
|
+
setChoicePicker(loadedPluginPicker(session));
|
|
4654
|
+
}
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4487
4657
|
case 'skill-picker': {
|
|
4488
4658
|
const skills = discoverSkills({ cwd: ctx.cwd });
|
|
4489
4659
|
if (skills.length === 0) {
|
|
@@ -5554,6 +5724,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5554
5724
|
// menu, so a provider/session change cannot silently reinterpret Enter.
|
|
5555
5725
|
if (choicePickerRef.current) {
|
|
5556
5726
|
const picker = choicePickerRef.current;
|
|
5727
|
+
if ((picker.kind === 'plugin' || picker.kind === 'plugin-action') && picker.busy)
|
|
5728
|
+
return;
|
|
5557
5729
|
// Composer publishes a chooser while handling Return. App receives the
|
|
5558
5730
|
// same input dispatch (and key repeat can arrive before the commit), so
|
|
5559
5731
|
// the synchronous ref is an ownership fence but not yet an actionable
|
|
@@ -5805,7 +5977,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5805
5977
|
setPickerSelectionKind('provider-and-model');
|
|
5806
5978
|
setPhase('picker');
|
|
5807
5979
|
void discoverProviders({ skipProbes: true }).then(setPickerDetected).catch((error) => setPickerNotice(String(error)));
|
|
5808
|
-
} })) : phase === 'trust' ? (_jsx(TrustPrompt, { cwd: ctx.cwd })) : phase === 'unhealthy' ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: theme.status.error, bold: true, children: "Startup stopped" }), _jsx(Text, { color: theme.text.secondary, children: "Resolve the error above, then restart Namzu." })] })) : phase === 'resume' ? (_jsx(ResumePicker, { conversations: resumeList, selected: selectedResume })) : phase === 'edit' ? (_jsx(EditPromptPicker, { prompts: editList, selected: selectedEdit })) : phase === 'picker' ? (_jsx(Picker, { detected: pickerDetected ?? detected, currentProvider: currentProvider, selectionKind: pickerSelectionKind, currentModel: session?.modelSummary ?? null, initialView: pickerInitialView, onSubmit: handlePickerSubmit, onCancel: handlePickerCancel, onSetup: () => setProviderSetup(true), onCredential: handleTypedCredential, onLogin: startLoginFromPicker, onLoginComplete: finishLoginFromPicker, keyEntryFor: keyEntryFor, notice: pickerNotice })) : (_jsxs(_Fragment, { children: [agentSurface === null && outputViewer === null ? (_jsx(LiveActivity, { compact: compactWork, activeTools: visibleActiveTools, working: state === 'thinking' || state === 'tool', interruptible: abortRef.current !== null, animate: stdout.isTTY === true && permission === null && textPrompt === null, thinking: thinking })) : null, agentSurface === null && outputViewer === null && permission === null ? (_jsx(TaskList, { tasks: tasks, compact: compactWork })) : null, permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls, review: permission.review, summary: permission.summary, detailsOpen: permissionDetailsOpen, reviewOffset: permissionReviewOffset, choice: permissionChoice, queuedCount: queuedPermissionCount, sourceLabel: permissionSourceLabel, columns: terminal.columns, rows: terminal.rows })) : null, textPrompt ? (_jsx(TextPrompt, { columns: Math.max(1, (terminal.columns ?? 80) - 2), title: textPrompt.title, placeholder: textPrompt.placeholder, initialValue: textPrompt.initialValue, emptyNotice: textPrompt.emptyNotice, hidden: permission !== null || (agentSurface !== null || outputViewer !== null), onSubmit: submitTextPrompt, onCancel: cancelTextPrompt }, textPrompt.token)) : permission === null && agentSurface === null && outputViewer === null && choicePicker ? (_jsx(ChoicePicker, { columns: Math.max(1, (terminal.columns ?? 80) - 2), title: choicePicker.title, notice: choicePicker.notice, options: filterChoiceOptions(choicePicker.options, choiceQuery), query: choicePickerSearchable(choicePicker) ? choiceQuery : undefined, selected: selectedChoice, windowSize: choicePicker.kind === 'command' ? choicePicker.windowSize : undefined })) : permission === null && agentSurface === null && outputViewer === null && copyPicker ? (_jsx(CopyPicker, { targets: copyPicker.targets, selected: selectedCopy })) : null, _jsxs(ComposerFrame, { working: state === 'thinking' || state === 'tool' || visibleActiveTools.length > 0, focus: phase === 'ready' &&
|
|
5980
|
+
} })) : phase === 'trust' ? (_jsx(TrustPrompt, { cwd: ctx.cwd })) : phase === 'unhealthy' ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: theme.status.error, bold: true, children: "Startup stopped" }), _jsx(Text, { color: theme.text.secondary, children: "Resolve the error above, then restart Namzu." })] })) : phase === 'resume' ? (_jsx(ResumePicker, { conversations: resumeList, selected: selectedResume })) : phase === 'edit' ? (_jsx(EditPromptPicker, { prompts: editList, selected: selectedEdit })) : phase === 'picker' ? (_jsx(Picker, { detected: pickerDetected ?? detected, currentProvider: currentProvider, selectionKind: pickerSelectionKind, currentModel: session?.modelSummary ?? null, initialView: pickerInitialView, onSubmit: handlePickerSubmit, onCancel: handlePickerCancel, onSetup: () => setProviderSetup(true), onCredential: handleTypedCredential, onLogin: startLoginFromPicker, onLoginComplete: finishLoginFromPicker, keyEntryFor: keyEntryFor, notice: pickerNotice })) : (_jsxs(_Fragment, { children: [agentSurface === null && outputViewer === null ? (_jsx(LiveActivity, { compact: compactWork, activeTools: visibleActiveTools, working: state === 'thinking' || state === 'tool', interruptible: abortRef.current !== null, animate: stdout.isTTY === true && permission === null && textPrompt === null, thinking: thinking })) : null, agentSurface === null && outputViewer === null && permission === null ? (_jsx(TaskList, { tasks: tasks, compact: compactWork })) : null, permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls, review: permission.review, summary: permission.summary, detailsOpen: permissionDetailsOpen, reviewOffset: permissionReviewOffset, choice: permissionChoice, queuedCount: queuedPermissionCount, sourceLabel: permissionSourceLabel, columns: terminal.columns, rows: terminal.rows })) : null, textPrompt ? (_jsx(TextPrompt, { columns: Math.max(1, (terminal.columns ?? 80) - 2), title: textPrompt.title, placeholder: textPrompt.placeholder, initialValue: textPrompt.initialValue, emptyNotice: textPrompt.emptyNotice, hidden: permission !== null || (agentSurface !== null || outputViewer !== null), onSubmit: submitTextPrompt, onCancel: cancelTextPrompt }, textPrompt.token)) : permission === null && agentSurface === null && outputViewer === null && choicePicker ? (_jsx(ChoicePicker, { busy: 'busy' in choicePicker && choicePicker.busy === true, columns: Math.max(1, (terminal.columns ?? 80) - 2), title: choicePicker.title, notice: choicePicker.notice, options: filterChoiceOptions(choicePicker.options, choiceQuery), query: choicePickerSearchable(choicePicker) ? choiceQuery : undefined, selected: selectedChoice, windowSize: choicePicker.kind === 'command' ? choicePicker.windowSize : undefined })) : permission === null && agentSurface === null && outputViewer === null && copyPicker ? (_jsx(CopyPicker, { targets: copyPicker.targets, selected: selectedCopy })) : null, _jsxs(ComposerFrame, { working: state === 'thinking' || state === 'tool' || visibleActiveTools.length > 0, focus: phase === 'ready' &&
|
|
5809
5981
|
state !== 'awaiting-permission' &&
|
|
5810
5982
|
!compacting &&
|
|
5811
5983
|
externalEditorRequest === null &&
|
|
@@ -5932,7 +6104,55 @@ function labelOfOption(question, optionId) {
|
|
|
5932
6104
|
return question.options.find((option) => option.id === optionId)?.label ?? optionId;
|
|
5933
6105
|
}
|
|
5934
6106
|
function choicePickerSearchable(picker) {
|
|
5935
|
-
return ['command', 'skill', 'review-branch', 'review-commit'].includes(picker.kind);
|
|
6107
|
+
return ['command', 'skill', 'plugin', 'review-branch', 'review-commit'].includes(picker.kind);
|
|
6108
|
+
}
|
|
6109
|
+
function loadedPluginPicker(owner) {
|
|
6110
|
+
const plugins = owner.plugins?.list() ?? [];
|
|
6111
|
+
return {
|
|
6112
|
+
kind: 'plugin',
|
|
6113
|
+
owner,
|
|
6114
|
+
title: 'Plugins',
|
|
6115
|
+
notice: 'Loaded in this session · select to inspect or change state',
|
|
6116
|
+
values: plugins.map((plugin) => plugin.name),
|
|
6117
|
+
options: plugins.map(pluginOption),
|
|
6118
|
+
};
|
|
6119
|
+
}
|
|
6120
|
+
function pluginActionPicker(owner, name) {
|
|
6121
|
+
const plugin = owner.plugins?.list().find((item) => item.name === name);
|
|
6122
|
+
if (!plugin)
|
|
6123
|
+
return null;
|
|
6124
|
+
const enabled = plugin.status === 'enabled';
|
|
6125
|
+
return {
|
|
6126
|
+
kind: 'plugin-action',
|
|
6127
|
+
owner,
|
|
6128
|
+
pluginName: name,
|
|
6129
|
+
title: `${plugin.name} · ${plugin.version}`,
|
|
6130
|
+
notice: `${plugin.status} · after restart: ${pluginStartupState(plugin)} · ${plugin.tools.length} tool${plugin.tools.length === 1 ? '' : 's'}`,
|
|
6131
|
+
values: ['details', enabled ? 'disable' : 'enable', 'remember'],
|
|
6132
|
+
options: [
|
|
6133
|
+
{
|
|
6134
|
+
label: 'Details',
|
|
6135
|
+
description: 'Show directory and contributions.',
|
|
6136
|
+
selectedDescription: plugin.description,
|
|
6137
|
+
},
|
|
6138
|
+
{
|
|
6139
|
+
label: enabled ? 'Disable for this session' : 'Enable for this session',
|
|
6140
|
+
description: enabled
|
|
6141
|
+
? 'Remove its tools, skills and hooks; disconnect its MCP servers.'
|
|
6142
|
+
: 'Load this trusted plugin’s tools, skills, hooks and MCP servers.',
|
|
6143
|
+
selectedDescription: 'Resets on restart or model switch. An idle session is required.',
|
|
6144
|
+
},
|
|
6145
|
+
{
|
|
6146
|
+
label: `Keep ${enabled ? 'enabled' : 'disabled'} after restart`,
|
|
6147
|
+
description: 'Remember this state for future sessions and model switches.',
|
|
6148
|
+
selectedDescription: plugin.startupError ??
|
|
6149
|
+
'Applies only to this plugin directory. Other running sessions keep their state.',
|
|
6150
|
+
...(plugin.startupEnabled === enabled
|
|
6151
|
+
? { disabledReason: 'Already the startup setting.' }
|
|
6152
|
+
: {}),
|
|
6153
|
+
},
|
|
6154
|
+
],
|
|
6155
|
+
};
|
|
5936
6156
|
}
|
|
5937
6157
|
function permissionPicker(mode, approvedAll, more = false) {
|
|
5938
6158
|
const current = effectivePermissionMode(mode, approvedAll);
|