@namzu/cli 24.0.0 → 25.0.1
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 +841 -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 +6 -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 +245 -19
- 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 +211 -39
- 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);
|
|
@@ -401,10 +405,16 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
401
405
|
summaries: 0,
|
|
402
406
|
reclaimedTokens: 0,
|
|
403
407
|
});
|
|
404
|
-
// Jobs that ended while no turn was running.
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
+
// Jobs that ended while no turn was running.
|
|
409
|
+
//
|
|
410
|
+
// Only those: an exit that lands mid-turn belongs to the kernel, which
|
|
411
|
+
// rides it out on the next tool result or, for a job the model awaited,
|
|
412
|
+
// delivers it as the message that releases its own suspend. Queueing one
|
|
413
|
+
// here as well would announce the same exit twice, so the `abortRef`
|
|
414
|
+
// check below is the boundary between the two owners rather than an
|
|
415
|
+
// optimisation. Between turns the kernel is not listening, so the exit is
|
|
416
|
+
// held here and handed to the next send as part of the system text; the
|
|
417
|
+
// transcript row is written at once.
|
|
408
418
|
const idleJobNoticesRef = useRef([]);
|
|
409
419
|
const drainIdleJobNotices = () => {
|
|
410
420
|
if (idleJobNoticesRef.current.length === 0)
|
|
@@ -1106,7 +1116,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1106
1116
|
// so the feedback store correctly refused every TUI rating as unverifiable.
|
|
1107
1117
|
// Bind both trees to the exact conversation captured with the streamed
|
|
1108
1118
|
// message; `/resume` may change the active scope before a delayed click.
|
|
1109
|
-
const sessionDir = new
|
|
1119
|
+
const sessionDir = new CliPathBuilder(sessions.root).sessionDir(sessions.projectId, sessionId);
|
|
1110
1120
|
const store = new DiskMessageFeedbackStore({
|
|
1111
1121
|
rootDir: join(sessionDir, 'feedback'),
|
|
1112
1122
|
runsDir: join(sessionDir, 'runs'),
|
|
@@ -1321,6 +1331,70 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1321
1331
|
return;
|
|
1322
1332
|
if (picker.options[index]?.disabledReason)
|
|
1323
1333
|
return;
|
|
1334
|
+
if (picker.kind === 'plugin' || picker.kind === 'plugin-action') {
|
|
1335
|
+
if (picker.busy || picker.owner !== session)
|
|
1336
|
+
return;
|
|
1337
|
+
if (picker.kind === 'plugin') {
|
|
1338
|
+
const child = pluginActionPicker(picker.owner, value);
|
|
1339
|
+
setSelectedChoice(0);
|
|
1340
|
+
setChoicePicker(child ? { ...child, back: picker } : null);
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
const plugin = picker.owner.plugins?.list().find((item) => item.name === picker.pluginName);
|
|
1344
|
+
if (!plugin || !picker.owner.plugins)
|
|
1345
|
+
return;
|
|
1346
|
+
if (value === 'details') {
|
|
1347
|
+
setChoicePicker(null);
|
|
1348
|
+
pushMessage('system', pluginDetails(plugin));
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (state !== 'idle' || permission) {
|
|
1352
|
+
setChoicePicker({
|
|
1353
|
+
...picker,
|
|
1354
|
+
notice: 'Wait for the active turn and permission prompts to finish.',
|
|
1355
|
+
});
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
const enabled = value === 'enable';
|
|
1359
|
+
const remembering = value === 'remember';
|
|
1360
|
+
const pending = {
|
|
1361
|
+
...picker,
|
|
1362
|
+
busy: true,
|
|
1363
|
+
notice: remembering
|
|
1364
|
+
? 'Saving plugin setting…'
|
|
1365
|
+
: enabled
|
|
1366
|
+
? 'Enabling plugin…'
|
|
1367
|
+
: 'Disabling plugin…',
|
|
1368
|
+
};
|
|
1369
|
+
setChoicePicker(pending);
|
|
1370
|
+
const generation = conversationGenRef.current;
|
|
1371
|
+
const change = remembering
|
|
1372
|
+
? picker.owner.plugins.rememberState(plugin.name)
|
|
1373
|
+
: picker.owner.plugins.setEnabled(plugin.name, enabled);
|
|
1374
|
+
void change.then(() => {
|
|
1375
|
+
if (appLifetime.signal.aborted ||
|
|
1376
|
+
conversationGenRef.current !== generation ||
|
|
1377
|
+
choicePickerRef.current !== pending)
|
|
1378
|
+
return;
|
|
1379
|
+
const updated = pluginActionPicker(picker.owner, plugin.name);
|
|
1380
|
+
setSelectedChoice(0);
|
|
1381
|
+
setChoicePicker(updated ? { ...updated, back: loadedPluginPicker(picker.owner) } : null);
|
|
1382
|
+
}, (error) => {
|
|
1383
|
+
if (appLifetime.signal.aborted ||
|
|
1384
|
+
conversationGenRef.current !== generation ||
|
|
1385
|
+
choicePickerRef.current !== pending)
|
|
1386
|
+
return;
|
|
1387
|
+
const updated = pluginActionPicker(picker.owner, plugin.name);
|
|
1388
|
+
setChoicePicker(updated
|
|
1389
|
+
? {
|
|
1390
|
+
...updated,
|
|
1391
|
+
back: loadedPluginPicker(picker.owner),
|
|
1392
|
+
notice: `Could not change plugin: ${error instanceof Error ? error.message : String(error)}`,
|
|
1393
|
+
}
|
|
1394
|
+
: null);
|
|
1395
|
+
});
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1324
1398
|
if (picker.kind === 'review-preset') {
|
|
1325
1399
|
if (reviewChoiceInFlightRef.current)
|
|
1326
1400
|
return;
|
|
@@ -1580,6 +1654,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1580
1654
|
setSelectedChoice,
|
|
1581
1655
|
setTextPrompt,
|
|
1582
1656
|
session,
|
|
1657
|
+
state,
|
|
1658
|
+
permission,
|
|
1583
1659
|
]);
|
|
1584
1660
|
const sendCopyRequest = useCallback((index) => {
|
|
1585
1661
|
const picker = copyPickerRef.current;
|
|
@@ -1785,6 +1861,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1785
1861
|
...(activeCtx.hooks ? { hooks: activeCtx.hooks } : {}),
|
|
1786
1862
|
...(activeCtx.compaction ? { compaction: activeCtx.compaction } : {}),
|
|
1787
1863
|
...(activeCtx.memory ? { memory: activeCtx.memory } : {}),
|
|
1864
|
+
...(activeCtx.limits ? { limits: activeCtx.limits } : {}),
|
|
1788
1865
|
...(activeCtx.sandbox ? { sandbox: activeCtx.sandbox } : {}),
|
|
1789
1866
|
// Somebody is at this terminal, so the model may ask them one
|
|
1790
1867
|
// question when a decision is genuinely theirs.
|
|
@@ -2696,6 +2773,12 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2696
2773
|
pushMessage('system', 'The conversation changed while this editor was open. Reopen the command in the current conversation.');
|
|
2697
2774
|
return;
|
|
2698
2775
|
}
|
|
2776
|
+
if (prompt.kind === 'run-limit') {
|
|
2777
|
+
const field = RUN_LIMIT_FIELDS.find((field) => field.key === prompt.limitKey);
|
|
2778
|
+
if (field)
|
|
2779
|
+
commandPickerSubmitRef.current(`/config limits ${field.name} ${value.trim()}`);
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2699
2782
|
if (prompt.kind === 'goal-create' || prompt.kind === 'goal-edit') {
|
|
2700
2783
|
if (value.trim())
|
|
2701
2784
|
commandPickerSubmitRef.current(`/goal ${prompt.kind === 'goal-create' ? 'set' : 'edit'} ${value.trim()}`);
|
|
@@ -3025,10 +3108,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3025
3108
|
}
|
|
3026
3109
|
switch (event.kind) {
|
|
3027
3110
|
case 'delta': {
|
|
3028
|
-
if (event.messageId &&
|
|
3111
|
+
if (event.messageId &&
|
|
3112
|
+
(st.sourceMessageId !== event.messageId ||
|
|
3113
|
+
(event.textPart && st.sourceTextPartId !== event.textPart.id))) {
|
|
3029
3114
|
if (st.sourceMessageId !== undefined)
|
|
3030
3115
|
closeAssistant();
|
|
3031
3116
|
st.sourceMessageId = event.messageId;
|
|
3117
|
+
st.sourceTextPartId = event.textPart?.id;
|
|
3032
3118
|
}
|
|
3033
3119
|
setState('thinking');
|
|
3034
3120
|
if (event.messageId && event.runId && st.sessionId) {
|
|
@@ -3167,6 +3253,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3167
3253
|
setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
|
|
3168
3254
|
break;
|
|
3169
3255
|
}
|
|
3256
|
+
const evidence = !event.isError && event.output !== undefined
|
|
3257
|
+
? conversationEvidenceView(event.toolName, event.output) : undefined;
|
|
3258
|
+
if (evidence !== undefined) {
|
|
3259
|
+
pushMessage('tool', evidence.content, false, '✓', evidence.detail, theme.status.ok, 'ctrl+o details', 'evidence');
|
|
3260
|
+
setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
|
|
3261
|
+
break;
|
|
3262
|
+
}
|
|
3170
3263
|
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
3264
|
? formatElapsed(event.durationMs)
|
|
3172
3265
|
: done
|
|
@@ -3275,6 +3368,10 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3275
3368
|
pushMessage('system', `History warning (${event.source}): ${event.text}`, false, '!');
|
|
3276
3369
|
break;
|
|
3277
3370
|
case 'done': {
|
|
3371
|
+
// Persistence and memory use the settled answer, not concatenated
|
|
3372
|
+
// commentary deltas. The transcript keeps each visible part above.
|
|
3373
|
+
if (event.text !== undefined)
|
|
3374
|
+
st.text = event.text;
|
|
3278
3375
|
// `run_completed` is not synonymous with success: budgets,
|
|
3279
3376
|
// cancellation and output guardrails arrive through this event too.
|
|
3280
3377
|
// Missing remains a normal end for older producers, matching the
|
|
@@ -3287,7 +3384,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3287
3384
|
outcome: st.completed ? 'completed' : 'stopped',
|
|
3288
3385
|
};
|
|
3289
3386
|
closeAssistant();
|
|
3290
|
-
const stopNotice = describeRunStop(event.stopReason);
|
|
3387
|
+
const stopNotice = describeRunStop(event.stopReason, event.budget);
|
|
3291
3388
|
if (stopNotice)
|
|
3292
3389
|
pushMessage('system', stopNotice, false, '■');
|
|
3293
3390
|
break;
|
|
@@ -3587,6 +3684,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3587
3684
|
activeTurnInboxRef.current = inbox;
|
|
3588
3685
|
const turnPermissionMode = permissionModeRef.current;
|
|
3589
3686
|
const turnReasoningEffort = reasoningEffortRef.current;
|
|
3687
|
+
const turnRunLimits = resolveRunGuards(ctxRef.current.limits, runLimitsOverrideRef.current);
|
|
3590
3688
|
// Always carry the guarded callback. `auto` and `strict` decide before
|
|
3591
3689
|
// calling it in makeResumeHandler; retaining it is what lets a session
|
|
3592
3690
|
// launched with --yolo later return to prompt mode truthfully.
|
|
@@ -3644,6 +3742,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3644
3742
|
signal: ac.signal,
|
|
3645
3743
|
runId,
|
|
3646
3744
|
permissionMode: turnPermissionMode,
|
|
3745
|
+
limits: turnRunLimits,
|
|
3647
3746
|
...(turnReasoningEffort !== undefined ? { effort: turnReasoningEffort } : {}),
|
|
3648
3747
|
...(goalRound ? { goalRound } : {}),
|
|
3649
3748
|
// The mode above decides whether this callback is consulted.
|
|
@@ -3679,6 +3778,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3679
3778
|
}
|
|
3680
3779
|
else if (event.kind === 'delta')
|
|
3681
3780
|
st.text += event.text;
|
|
3781
|
+
else if (event.kind === 'done' && event.text !== undefined)
|
|
3782
|
+
st.text = event.text;
|
|
3682
3783
|
}
|
|
3683
3784
|
}
|
|
3684
3785
|
else {
|
|
@@ -4135,6 +4236,48 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4135
4236
|
}
|
|
4136
4237
|
setProviderSetup(true);
|
|
4137
4238
|
return;
|
|
4239
|
+
case 'run-limits-set':
|
|
4240
|
+
case 'run-limit-editor':
|
|
4241
|
+
case 'run-limits-picker': {
|
|
4242
|
+
const currentLimits = resolveRunGuards(ctxRef.current.limits, runLimitsOverrideRef.current);
|
|
4243
|
+
if (slash.kind === 'run-limit-editor') {
|
|
4244
|
+
const scope = scopeRef.current;
|
|
4245
|
+
if (!scope) {
|
|
4246
|
+
pushMessage('system', 'Run limits are available after the session starts.');
|
|
4247
|
+
return;
|
|
4248
|
+
}
|
|
4249
|
+
const field = RUN_LIMIT_FIELDS.find((field) => field.key === slash.key);
|
|
4250
|
+
textPromptTokenRef.current += 1;
|
|
4251
|
+
setTextPrompt({
|
|
4252
|
+
token: textPromptTokenRef.current,
|
|
4253
|
+
kind: 'run-limit',
|
|
4254
|
+
limitKey: slash.key,
|
|
4255
|
+
title: `${field.label} (${slash.key === 'timeoutMs' ? 'ms; ' : ''}0 = unlimited)`,
|
|
4256
|
+
placeholder: field.hint,
|
|
4257
|
+
emptyNotice: 'Enter a value, or Esc to keep the current limit.',
|
|
4258
|
+
initialValue: String(currentLimits[slash.key]),
|
|
4259
|
+
sessionId: scope.sessionId,
|
|
4260
|
+
});
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
if (slash.kind === 'run-limits-set') {
|
|
4264
|
+
runLimitsOverrideRef.current = { ...runLimitsOverrideRef.current, ...slash.limits };
|
|
4265
|
+
}
|
|
4266
|
+
const commands = runLimitCommands(resolveRunGuards(currentLimits, runLimitsOverrideRef.current));
|
|
4267
|
+
setSelectedChoice(0);
|
|
4268
|
+
setChoicePicker({
|
|
4269
|
+
kind: 'command',
|
|
4270
|
+
title: 'Run limits',
|
|
4271
|
+
notice: `${slash.kind === 'run-limits-set' ? 'Updated. ' : ''}This session · new turns and their agents. Existing runs keep their limits.`,
|
|
4272
|
+
values: commands,
|
|
4273
|
+
options: commands.map((command) => ({
|
|
4274
|
+
label: command.label,
|
|
4275
|
+
description: command.description,
|
|
4276
|
+
})),
|
|
4277
|
+
windowSize: 5,
|
|
4278
|
+
});
|
|
4279
|
+
return;
|
|
4280
|
+
}
|
|
4138
4281
|
case 'settings-picker': {
|
|
4139
4282
|
const commands = [
|
|
4140
4283
|
{
|
|
@@ -4155,6 +4298,14 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4155
4298
|
label: 'Permissions',
|
|
4156
4299
|
description: `${permissionModeLabel(effectivePermissionMode(permissionModeRef.current, session?.approvalLatched() ?? false))} · this session.`,
|
|
4157
4300
|
},
|
|
4301
|
+
{
|
|
4302
|
+
name: 'config limits',
|
|
4303
|
+
label: 'Run limits',
|
|
4304
|
+
description: runLimitCommands(resolveRunGuards(ctxRef.current.limits, runLimitsOverrideRef.current))
|
|
4305
|
+
.slice(0, 3)
|
|
4306
|
+
.map((field) => `${field.label}: ${field.description}`)
|
|
4307
|
+
.join(' · '),
|
|
4308
|
+
},
|
|
4158
4309
|
{
|
|
4159
4310
|
name: 'status',
|
|
4160
4311
|
label: 'Web & session',
|
|
@@ -4171,7 +4322,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4171
4322
|
setChoicePicker({
|
|
4172
4323
|
kind: 'command',
|
|
4173
4324
|
title: 'Configuration',
|
|
4174
|
-
notice: 'Model, reasoning and
|
|
4325
|
+
notice: 'Model, reasoning, permission and run-limit changes apply to this session.',
|
|
4175
4326
|
values: commands,
|
|
4176
4327
|
options: commands.map((command) => ({
|
|
4177
4328
|
label: command.label,
|
|
@@ -4484,6 +4635,31 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4484
4635
|
pushMessage('system', `Skills (● active):\n ${lines.join('\n ')}`);
|
|
4485
4636
|
return;
|
|
4486
4637
|
}
|
|
4638
|
+
case 'plugins': {
|
|
4639
|
+
const plugins = session?.plugins?.list() ?? [];
|
|
4640
|
+
if (slash.name) {
|
|
4641
|
+
const plugin = plugins.find((item) => item.name === slash.name);
|
|
4642
|
+
pushMessage('system', plugin
|
|
4643
|
+
? pluginDetails(plugin)
|
|
4644
|
+
: `Plugin "${slash.name}" is not loaded in this session. Use /plugins to inspect availability.`);
|
|
4645
|
+
}
|
|
4646
|
+
else if (plugins.length === 0 || !session) {
|
|
4647
|
+
pushMessage('system', emptyPluginReport(ctx.plugins, ctx.cwd));
|
|
4648
|
+
}
|
|
4649
|
+
else if (slash.list) {
|
|
4650
|
+
pushMessage('system', `Plugins\n${plugins
|
|
4651
|
+
.map((plugin) => {
|
|
4652
|
+
const option = pluginOption(plugin);
|
|
4653
|
+
return `${option.label} · ${option.description}`;
|
|
4654
|
+
})
|
|
4655
|
+
.join('\n')}`);
|
|
4656
|
+
}
|
|
4657
|
+
else {
|
|
4658
|
+
setSelectedChoice(0);
|
|
4659
|
+
setChoicePicker(loadedPluginPicker(session));
|
|
4660
|
+
}
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4487
4663
|
case 'skill-picker': {
|
|
4488
4664
|
const skills = discoverSkills({ cwd: ctx.cwd });
|
|
4489
4665
|
if (skills.length === 0) {
|
|
@@ -5554,6 +5730,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5554
5730
|
// menu, so a provider/session change cannot silently reinterpret Enter.
|
|
5555
5731
|
if (choicePickerRef.current) {
|
|
5556
5732
|
const picker = choicePickerRef.current;
|
|
5733
|
+
if ((picker.kind === 'plugin' || picker.kind === 'plugin-action') && picker.busy)
|
|
5734
|
+
return;
|
|
5557
5735
|
// Composer publishes a chooser while handling Return. App receives the
|
|
5558
5736
|
// same input dispatch (and key repeat can arrive before the commit), so
|
|
5559
5737
|
// the synchronous ref is an ownership fence but not yet an actionable
|
|
@@ -5805,7 +5983,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5805
5983
|
setPickerSelectionKind('provider-and-model');
|
|
5806
5984
|
setPhase('picker');
|
|
5807
5985
|
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' &&
|
|
5986
|
+
} })) : 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
5987
|
state !== 'awaiting-permission' &&
|
|
5810
5988
|
!compacting &&
|
|
5811
5989
|
externalEditorRequest === null &&
|
|
@@ -5932,7 +6110,55 @@ function labelOfOption(question, optionId) {
|
|
|
5932
6110
|
return question.options.find((option) => option.id === optionId)?.label ?? optionId;
|
|
5933
6111
|
}
|
|
5934
6112
|
function choicePickerSearchable(picker) {
|
|
5935
|
-
return ['command', 'skill', 'review-branch', 'review-commit'].includes(picker.kind);
|
|
6113
|
+
return ['command', 'skill', 'plugin', 'review-branch', 'review-commit'].includes(picker.kind);
|
|
6114
|
+
}
|
|
6115
|
+
function loadedPluginPicker(owner) {
|
|
6116
|
+
const plugins = owner.plugins?.list() ?? [];
|
|
6117
|
+
return {
|
|
6118
|
+
kind: 'plugin',
|
|
6119
|
+
owner,
|
|
6120
|
+
title: 'Plugins',
|
|
6121
|
+
notice: 'Loaded in this session · select to inspect or change state',
|
|
6122
|
+
values: plugins.map((plugin) => plugin.name),
|
|
6123
|
+
options: plugins.map(pluginOption),
|
|
6124
|
+
};
|
|
6125
|
+
}
|
|
6126
|
+
function pluginActionPicker(owner, name) {
|
|
6127
|
+
const plugin = owner.plugins?.list().find((item) => item.name === name);
|
|
6128
|
+
if (!plugin)
|
|
6129
|
+
return null;
|
|
6130
|
+
const enabled = plugin.status === 'enabled';
|
|
6131
|
+
return {
|
|
6132
|
+
kind: 'plugin-action',
|
|
6133
|
+
owner,
|
|
6134
|
+
pluginName: name,
|
|
6135
|
+
title: `${plugin.name} · ${plugin.version}`,
|
|
6136
|
+
notice: `${plugin.status} · after restart: ${pluginStartupState(plugin)} · ${plugin.tools.length} tool${plugin.tools.length === 1 ? '' : 's'}`,
|
|
6137
|
+
values: ['details', enabled ? 'disable' : 'enable', 'remember'],
|
|
6138
|
+
options: [
|
|
6139
|
+
{
|
|
6140
|
+
label: 'Details',
|
|
6141
|
+
description: 'Show directory and contributions.',
|
|
6142
|
+
selectedDescription: plugin.description,
|
|
6143
|
+
},
|
|
6144
|
+
{
|
|
6145
|
+
label: enabled ? 'Disable for this session' : 'Enable for this session',
|
|
6146
|
+
description: enabled
|
|
6147
|
+
? 'Remove its tools, skills and hooks; disconnect its MCP servers.'
|
|
6148
|
+
: 'Load this trusted plugin’s tools, skills, hooks and MCP servers.',
|
|
6149
|
+
selectedDescription: 'Resets on restart or model switch. An idle session is required.',
|
|
6150
|
+
},
|
|
6151
|
+
{
|
|
6152
|
+
label: `Keep ${enabled ? 'enabled' : 'disabled'} after restart`,
|
|
6153
|
+
description: 'Remember this state for future sessions and model switches.',
|
|
6154
|
+
selectedDescription: plugin.startupError ??
|
|
6155
|
+
'Applies only to this plugin directory. Other running sessions keep their state.',
|
|
6156
|
+
...(plugin.startupEnabled === enabled
|
|
6157
|
+
? { disabledReason: 'Already the startup setting.' }
|
|
6158
|
+
: {}),
|
|
6159
|
+
},
|
|
6160
|
+
],
|
|
6161
|
+
};
|
|
5936
6162
|
}
|
|
5937
6163
|
function permissionPicker(mode, approvedAll, more = false) {
|
|
5938
6164
|
const current = effectivePermissionMode(mode, approvedAll);
|