moderado 0.4.18 → 0.4.28
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/README.md +3 -4
- package/dist/args.js +1 -1
- package/dist/commands/chat.d.ts +1 -3
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +117 -45
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/skills.js +2 -2
- package/dist/commands/skills.js.map +1 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +16 -8
- package/dist/skills.js.map +1 -1
- package/dist/ui/help_content.d.ts +17 -0
- package/dist/ui/help_content.d.ts.map +1 -0
- package/dist/ui/help_content.js +13 -0
- package/dist/ui/help_content.js.map +1 -0
- package/dist/ui/help_modal.d.ts.map +1 -1
- package/dist/ui/help_modal.js +5 -5
- package/dist/ui/help_modal.js.map +1 -1
- package/dist/ui/provider_connect.js +2 -2
- package/dist/ui/provider_connect.js.map +1 -1
- package/dist/ui/renderer.d.ts +6 -0
- package/dist/ui/renderer.d.ts.map +1 -1
- package/dist/ui/renderer.js +23 -4
- package/dist/ui/renderer.js.map +1 -1
- package/dist/ui/welcome.d.ts +32 -7
- package/dist/ui/welcome.d.ts.map +1 -1
- package/dist/ui/welcome.js +176 -74
- package/dist/ui/welcome.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,8 @@ npm install -g moderado
|
|
|
88
88
|
moderado --help
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
The
|
|
91
|
+
The 0.4.28 release candidate adds organized `/help` command groups and resumes
|
|
92
|
+
interrupted chat sessions. Its `/connect` flow handles the
|
|
92
93
|
Gateway and direct providers; `/login` is no longer offered. The previous
|
|
93
94
|
0.4.8 release uses `/login` for the Gateway. The earlier 0.3.10 release is
|
|
94
95
|
self-contained and works without the Gateway. All three use the `moderado`
|
|
@@ -101,9 +102,7 @@ npm and Scoop. See `docs/DISTRIBUTION.md` for package-manager details.
|
|
|
101
102
|
|
|
102
103
|
### Update an existing installation
|
|
103
104
|
|
|
104
|
-
These commands are available starting with Moderado v0.4.18.
|
|
105
|
-
published v0.4.10 release does not include them; install v0.4.18 or later with
|
|
106
|
-
the original installer before using these commands.
|
|
105
|
+
These commands are available starting with Moderado v0.4.18.
|
|
107
106
|
|
|
108
107
|
```bash
|
|
109
108
|
moderado update # latest release within the installed major version
|
package/dist/args.js
CHANGED
|
@@ -103,7 +103,7 @@ USAGE:
|
|
|
103
103
|
COMMANDS:
|
|
104
104
|
models Discover live models, capability & access tiers
|
|
105
105
|
run "<task>" Execute a bounded coding task in the workspace\n skills List built-in and user skills
|
|
106
|
-
skills on|off NAME
|
|
106
|
+
skills on|off NAME|PATTERN Enable or disable user skills (* matches all)
|
|
107
107
|
doctor Check local Moderado setup
|
|
108
108
|
update Update within the current major version
|
|
109
109
|
upgrade Upgrade across major versions
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare function resolveTurnAssistantAnswer(result: {
|
|
|
37
37
|
answer: string;
|
|
38
38
|
isError: boolean;
|
|
39
39
|
};
|
|
40
|
-
export declare function saveFailedTurnPrompt(history: ChatMessage[], prompt: string): ChatMessage[];
|
|
40
|
+
export declare function saveFailedTurnPrompt(history: ChatMessage[], prompt: string, partialResponse?: string): ChatMessage[];
|
|
41
41
|
export declare function isBareExitCommand(input: string): boolean;
|
|
42
42
|
/** Resolve the required, explicit local destination for `/session share`. */
|
|
43
43
|
export declare function resolveSessionSharePath(workspaceRoot: string, outputPath: string): string;
|
|
@@ -70,7 +70,6 @@ export declare function handleGenerationKeypress(str: string, key: {
|
|
|
70
70
|
onBtw?: (command: string) => void;
|
|
71
71
|
}): void;
|
|
72
72
|
export declare function isNetworkCommand(request: Pick<ApprovalRequest, 'toolName' | 'exactPayload'>): boolean;
|
|
73
|
-
export declare function isNetworkConsentReply(input: string, previousAnswer: string): boolean;
|
|
74
73
|
export declare function isLocalModelQuery(input: string): boolean;
|
|
75
74
|
export declare function isLocalProviderQuery(input: string): boolean;
|
|
76
75
|
export declare function isLocalIdentityQuery(input: string): boolean;
|
|
@@ -101,7 +100,6 @@ export declare function buildSearchAnswerTask(question: string, result: ToolResu
|
|
|
101
100
|
export declare function replaceEvidenceTurn(messages: ChatMessage[], evidenceTask: string, question: string): ChatMessage[];
|
|
102
101
|
export interface ApprovalPolicyOptions {
|
|
103
102
|
autoApprove: boolean;
|
|
104
|
-
networkAccessApproved: boolean;
|
|
105
103
|
requestInteractiveApproval: (request: ApprovalRequest, signal?: AbortSignal) => Promise<ApprovalDecision>;
|
|
106
104
|
}
|
|
107
105
|
export declare function decideApproval(request: ApprovalRequest, options: ApprovalPolicyOptions, signal?: AbortSignal): Promise<ApprovalDecision>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAkG,kBAAkB,EAA0D,MAAM,iBAAiB,CAAC;AAC7M,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAoB,aAAa,EAAE,eAAe,EAAyB,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AAC3L,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAuD,cAAc,EAAE,kBAAkB,EAA2K,MAAM,cAAc,CAAC;AAUhS,OAAO,EAAyH,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACtK,OAAO,EAAkC,kBAAkB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/chat.js
CHANGED
|
@@ -14,7 +14,7 @@ import { connectProviderInteractive, isAuthenticationFailure, isModeradoCloudOAu
|
|
|
14
14
|
import { initWorkspace } from './init.js';
|
|
15
15
|
import { expandMentions, createWorkspaceFileSource } from '../ui/file_mentions.js';
|
|
16
16
|
import { listFiles } from '../vendor/tools/index.js';
|
|
17
|
-
import { exitCleanly, promptInteractiveTurn, renderChatAnswerDelta, renderChatComposerCursor, renderChatThoughtTimeUpdate, renderChatUsageUpdate, renderGatewayFallbackUpdate, renderFullWelcomeScreen, renderWelcomePopupLayer, terminalCleanExitDone, wrapText } from '../ui/welcome.js';
|
|
17
|
+
import { exitCleanly, promptInteractiveTurn, renderChatAnswerDelta, renderChatComposerCursor, renderChatThoughtTimeUpdate, renderChatUsageUpdate, renderGatewayFallbackUpdate, renderFullWelcomeScreen, renderWelcomePopupLayer, terminalCleanExitDone, wrapText, getChatAnswerArea } from '../ui/welcome.js';
|
|
18
18
|
import { accumulateSessionUsage, compactSessionMessages, createSession, exportSessionMarkdown, formatSessionCost, SessionStore } from '../sessions.js';
|
|
19
19
|
import { layerPromptBox, renderBoxLines, selectConfirmPopup, selectListPopup } from '../ui/popup.js';
|
|
20
20
|
import { askModalChoice } from '../ui/prompt.js';
|
|
@@ -23,6 +23,7 @@ import { inspectGitWorkspace, readGitDiff } from '../vendor/tools/index.js';
|
|
|
23
23
|
import { buildWorkspaceMap } from '../repo_map.js';
|
|
24
24
|
import { evaluateTokenBudget, parseBudgetCommand } from '../budget.js';
|
|
25
25
|
import { discoverSkills, formatSkillContext, formatSkillsList, getEnabledSkillNames, selectEnabledSkills, setSkillEnabled } from '../skills.js';
|
|
26
|
+
import { getHelpCommandLines } from '../ui/help_content.js';
|
|
26
27
|
export function createConnectionProvider(connection) {
|
|
27
28
|
return new NvidiaAdapter({ apiKey: connection.apiKey, baseUrl: connection.baseUrl, providerId: connection.id, providerName: connection.displayName });
|
|
28
29
|
}
|
|
@@ -192,10 +193,46 @@ export function resolveTurnAssistantAnswer(result, previousHistoryLength, lastEr
|
|
|
192
193
|
isError: false,
|
|
193
194
|
};
|
|
194
195
|
}
|
|
195
|
-
export function saveFailedTurnPrompt(history, prompt) {
|
|
196
|
+
export function saveFailedTurnPrompt(history, prompt, partialResponse = '') {
|
|
196
197
|
if (!prompt.trim())
|
|
197
198
|
return history;
|
|
198
|
-
|
|
199
|
+
const interruptionNote = '[This response was interrupted before completion. Continue the task if asked.]';
|
|
200
|
+
const interruptedResponse = partialResponse.trim()
|
|
201
|
+
? `${partialResponse.trim()}\n\n${interruptionNote}`
|
|
202
|
+
: '[This task was interrupted before a response was produced. Continue the task if asked.]';
|
|
203
|
+
return [...history, { role: 'user', content: prompt }, { role: 'assistant', content: interruptedResponse }];
|
|
204
|
+
}
|
|
205
|
+
const CONTINUE_INTERRUPTED_TASK_PROMPT = 'Continue the interrupted task from the saved conversation. Start from the unfinished work and avoid repeating completed steps.';
|
|
206
|
+
function hasInterruptedTurn(messages) {
|
|
207
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
208
|
+
const message = messages[index];
|
|
209
|
+
if (message.role !== 'assistant' || !message.content?.trim())
|
|
210
|
+
continue;
|
|
211
|
+
return message.content.includes('[This response was interrupted before completion.')
|
|
212
|
+
|| message.content.includes('[This task was interrupted before a response was produced.');
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
function restoreLastSessionTurn(messages) {
|
|
217
|
+
let assistantIndex = -1;
|
|
218
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
219
|
+
if (messages[index].role === 'assistant' && messages[index].content?.trim()) {
|
|
220
|
+
assistantIndex = index;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (assistantIndex < 0) {
|
|
225
|
+
const question = [...messages].reverse().find((message) => message.role === 'user' && message.content?.trim())?.content ?? '';
|
|
226
|
+
return { question, answer: question ? 'This task was interrupted. Type “continue” to resume with the current model.' : '' };
|
|
227
|
+
}
|
|
228
|
+
let question = '';
|
|
229
|
+
for (let index = assistantIndex - 1; index >= 0; index -= 1) {
|
|
230
|
+
if (messages[index].role === 'user' && messages[index].content?.trim()) {
|
|
231
|
+
question = messages[index].content ?? '';
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return { question, answer: messages[assistantIndex].content ?? '' };
|
|
199
236
|
}
|
|
200
237
|
export function isBareExitCommand(input) {
|
|
201
238
|
return input.trim().toLowerCase() === 'exit';
|
|
@@ -305,9 +342,6 @@ export function isNetworkCommand(request) {
|
|
|
305
342
|
const text = Array.isArray(command) ? command.join(' ').toLowerCase() : '';
|
|
306
343
|
return request.toolName === 'run_command' && /\b(curl|wget|invoke-webrequest|iwr|irm)\b|https?:\/\//.test(text);
|
|
307
344
|
}
|
|
308
|
-
export function isNetworkConsentReply(input, previousAnswer) {
|
|
309
|
-
return /^(y|yes)$/i.test(input.trim()) && /\b(weather|internet|online|look up|web|curl)\b/i.test(previousAnswer);
|
|
310
|
-
}
|
|
311
345
|
export function isLocalModelQuery(input) {
|
|
312
346
|
const text = input.trim().toLowerCase().replace(/[?!.]+$/, '').trim();
|
|
313
347
|
if (!text)
|
|
@@ -455,9 +489,8 @@ export function replaceEvidenceTurn(messages, evidenceTask, question) {
|
|
|
455
489
|
return messages.map((message) => (message.role === 'user' && message.content === evidenceTask ? { role: 'user', content: question } : message));
|
|
456
490
|
}
|
|
457
491
|
export async function decideApproval(request, options, signal) {
|
|
458
|
-
if (isNetworkCommand(request)
|
|
459
|
-
return
|
|
460
|
-
}
|
|
492
|
+
if (isNetworkCommand(request))
|
|
493
|
+
return options.requestInteractiveApproval(request, signal);
|
|
461
494
|
if (options.autoApprove && !request.toolName.startsWith('mcp.')) {
|
|
462
495
|
return { requestId: request.requestId, status: 'approved' };
|
|
463
496
|
}
|
|
@@ -743,6 +776,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
743
776
|
modelId: currentModel,
|
|
744
777
|
mode: activeMode,
|
|
745
778
|
});
|
|
779
|
+
currentModel = args.model ?? (activeSession.providerId === activeConnection?.id ? activeSession.modelId : undefined) ?? currentModel;
|
|
746
780
|
if (activeSession.usage.available &&
|
|
747
781
|
!activeSession.usage.costKnown &&
|
|
748
782
|
activeSession.providerId === activeConnection?.id &&
|
|
@@ -767,11 +801,9 @@ export async function handleChatSession(args, version, signal) {
|
|
|
767
801
|
await reloadMcpTools();
|
|
768
802
|
const terminalApproval = new TerminalApprovalHandler();
|
|
769
803
|
const checkpoints = new WorkspaceCheckpointStore();
|
|
770
|
-
let networkAccessApproved = false;
|
|
771
804
|
let suspendGenerationInput;
|
|
772
805
|
const approvalHandler = { requestApproval: async (req, sig) => decideApproval(req, {
|
|
773
806
|
autoApprove: activeAutoApprove,
|
|
774
|
-
networkAccessApproved,
|
|
775
807
|
requestInteractiveApproval: async (request, approvalSignal) => {
|
|
776
808
|
const resumeGenerationInput = suspendGenerationInput?.();
|
|
777
809
|
try {
|
|
@@ -785,8 +817,10 @@ export async function handleChatSession(args, version, signal) {
|
|
|
785
817
|
const router = new Router();
|
|
786
818
|
const loop = new AgentLoop();
|
|
787
819
|
let conversationHistory = [...activeSession.messages];
|
|
788
|
-
|
|
789
|
-
let
|
|
820
|
+
const restoredTurn = restoreLastSessionTurn(conversationHistory);
|
|
821
|
+
let lastQuestion = restoredTurn.question;
|
|
822
|
+
let lastAnswer = restoredTurn.answer;
|
|
823
|
+
let lastTurnDone = conversationHistory.length > 0 && !hasInterruptedTurn(conversationHistory);
|
|
790
824
|
let lastThoughtTime = 0;
|
|
791
825
|
let lastOutputTokenRate;
|
|
792
826
|
let lastTokenUsage;
|
|
@@ -802,10 +836,17 @@ export async function handleChatSession(args, version, signal) {
|
|
|
802
836
|
isFirst = false;
|
|
803
837
|
}
|
|
804
838
|
else {
|
|
839
|
+
let autoContinueRequested = false;
|
|
805
840
|
const turn = await promptInteractiveTurn({
|
|
806
841
|
model: currentModel ?? 'No model connected — use /connect', tokens: Math.round(sessionTokens), cost: costLabel(), workspace: canonicalWorkspace, version,
|
|
807
|
-
usageAvailable: activeSession.usage.available, usageEstimated: activeSession.usage.estimated, initialMode: activeMode, initialAutoApprove: activeAutoApprove, isFirstTurn: isFirst, signal, chatQuestion: lastQuestion || undefined, chatAnswer: lastAnswer || undefined, chatThoughtTime: lastThoughtTime, outputTokenRate: lastOutputTokenRate, tokenUsage: lastTokenUsage,
|
|
842
|
+
usageAvailable: activeSession.usage.available, usageEstimated: activeSession.usage.estimated, initialMode: activeMode, initialAutoApprove: activeAutoApprove, isFirstTurn: isFirst, signal, chatQuestion: lastQuestion || undefined, chatAnswer: lastAnswer || undefined, chatThoughtTime: lastThoughtTime, chatTaskDone: lastTurnDone, outputTokenRate: lastOutputTokenRate, tokenUsage: lastTokenUsage,
|
|
808
843
|
queuedCommands: commandQueue.items,
|
|
844
|
+
takeAutoContinuePrompt: () => {
|
|
845
|
+
if (!autoContinueRequested)
|
|
846
|
+
return undefined;
|
|
847
|
+
autoContinueRequested = false;
|
|
848
|
+
return CONTINUE_INTERRUPTED_TASK_PROMPT;
|
|
849
|
+
},
|
|
809
850
|
onExit: exitMessage,
|
|
810
851
|
questionHistory: conversationHistory.flatMap((message) => message.role === 'user' && message.content?.trim() ? [message.content] : []),
|
|
811
852
|
onModelSelect: async (drawFrame) => {
|
|
@@ -828,15 +869,25 @@ export async function handleChatSession(args, version, signal) {
|
|
|
828
869
|
activeConnection = { ...activeConnection, defaultModel: modelId };
|
|
829
870
|
saveConnection(activeConnection);
|
|
830
871
|
config = loadConfig();
|
|
872
|
+
activeSession.providerId = activeConnection.id;
|
|
873
|
+
activeSession.providerName = activeConnection.displayName;
|
|
874
|
+
activeSession.modelId = modelId;
|
|
875
|
+
sessionStore.save(activeSession);
|
|
876
|
+
autoContinueRequested = hasInterruptedTurn(activeSession.messages);
|
|
831
877
|
}
|
|
832
878
|
return modelId;
|
|
833
879
|
}
|
|
834
880
|
const selection = await selectModelOverlay({ apiKey: activeConnection.id === 'moderado-cloud' ? undefined : activeConnection.apiKey, currentModel, signal, saveSelectionByDefault: true, drawFrame });
|
|
835
|
-
if (selection.modelId) {
|
|
881
|
+
if (selection.modelId && selection.modelId !== currentModel) {
|
|
836
882
|
currentModel = selection.modelId;
|
|
837
883
|
activeConnection = { ...activeConnection, defaultModel: selection.modelId };
|
|
838
884
|
saveConnection(activeConnection);
|
|
839
885
|
config = loadConfig();
|
|
886
|
+
activeSession.providerId = activeConnection.id;
|
|
887
|
+
activeSession.providerName = activeConnection.displayName;
|
|
888
|
+
activeSession.modelId = selection.modelId;
|
|
889
|
+
sessionStore.save(activeSession);
|
|
890
|
+
autoContinueRequested = hasInterruptedTurn(activeSession.messages);
|
|
840
891
|
}
|
|
841
892
|
return selection.modelId;
|
|
842
893
|
},
|
|
@@ -966,15 +1017,17 @@ export async function handleChatSession(args, version, signal) {
|
|
|
966
1017
|
onSession: async (command, drawFrame) => {
|
|
967
1018
|
let action = command.slice('/session'.length).trim();
|
|
968
1019
|
if (!action) {
|
|
969
|
-
|
|
1020
|
+
const sessionActions = [
|
|
970
1021
|
{ label: 'New session', value: 'new', description: 'Start with an empty conversation.' },
|
|
971
1022
|
{ label: 'List or resume', value: 'resume', description: 'Load a saved session for this workspace.' },
|
|
1023
|
+
...(hasInterruptedTurn(activeSession.messages) ? [{ label: 'Continue interrupted task', value: 'continue', description: 'Continue the last stopped task with the current model.' }] : []),
|
|
972
1024
|
{ label: 'Export transcript', value: 'export', description: 'Write a redacted Markdown transcript in this workspace.' },
|
|
973
1025
|
{ label: 'Undo latest agent change', value: 'undo', description: 'Restore the last checkpoint when safe.' },
|
|
974
1026
|
{ label: 'Redo latest agent change', value: 'redo', description: 'Reapply the last safely undone checkpoint.' },
|
|
975
1027
|
{ label: 'Share transcript', value: 'share', description: 'Choose an explicit workspace output path.' },
|
|
976
1028
|
{ label: 'Compact history', value: 'compact', description: 'Reduce older messages without calling a model.' },
|
|
977
|
-
]
|
|
1029
|
+
];
|
|
1030
|
+
action = (await selectListPopup('Session', sessionActions, { drawFrame, signal })) ?? '';
|
|
978
1031
|
}
|
|
979
1032
|
if (action === 'new') {
|
|
980
1033
|
activeSession = createSession(canonicalWorkspace, { providerId: activeConnection?.id, providerName: activeConnection?.displayName, modelId: currentModel, mode: activeMode });
|
|
@@ -988,6 +1041,14 @@ export async function handleChatSession(args, version, signal) {
|
|
|
988
1041
|
sessionStore.save(activeSession);
|
|
989
1042
|
return;
|
|
990
1043
|
}
|
|
1044
|
+
if (action === 'continue') {
|
|
1045
|
+
if (!hasInterruptedTurn(activeSession.messages)) {
|
|
1046
|
+
drawFrame(renderBoxLines('Continue task', ['There is no interrupted task in this session.', '', 'Press Esc or Enter to return.'], 64));
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
autoContinueRequested = true;
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
991
1052
|
if (action === 'list' || action === 'resume') {
|
|
992
1053
|
const sessions = sessionStore.listSessions(canonicalWorkspace);
|
|
993
1054
|
const id = await selectListPopup('Saved sessions', sessions.map((entry) => ({
|
|
@@ -1000,8 +1061,12 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1000
1061
|
activeSession = loaded;
|
|
1001
1062
|
conversationHistory = [...loaded.messages];
|
|
1002
1063
|
sessionTokens = loaded.usage.totalTokens;
|
|
1003
|
-
currentModel = loaded.modelId ?? currentModel;
|
|
1064
|
+
currentModel = loaded.providerId === activeConnection?.id ? loaded.modelId ?? currentModel : currentModel;
|
|
1004
1065
|
activeMode = loaded.mode;
|
|
1066
|
+
const restored = restoreLastSessionTurn(conversationHistory);
|
|
1067
|
+
lastQuestion = restored.question;
|
|
1068
|
+
lastAnswer = restored.answer;
|
|
1069
|
+
lastTurnDone = conversationHistory.length > 0 && !hasInterruptedTurn(conversationHistory);
|
|
1005
1070
|
}
|
|
1006
1071
|
return;
|
|
1007
1072
|
}
|
|
@@ -1201,14 +1266,14 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1201
1266
|
if ((parts[1] === 'on' || parts[1] === 'off') && parts[2]) {
|
|
1202
1267
|
try {
|
|
1203
1268
|
enabledSkillNames = setSkillEnabled(parts[2], parts[1] === 'on');
|
|
1204
|
-
lastAnswer = `User skill ${parts[2]} ${parts[1] === 'on' ? 'enabled' : 'disabled'}.\n\n${formatSkillsList(discoveredSkills, true, undefined, enabledSkillNames)}`;
|
|
1269
|
+
lastAnswer = `User skill${parts[2].includes('*') ? 's matching' : ''} ${parts[2]} ${parts[1] === 'on' ? 'enabled' : 'disabled'}.\n\n${formatSkillsList(discoveredSkills, true, undefined, enabledSkillNames)}`;
|
|
1205
1270
|
}
|
|
1206
1271
|
catch (error) {
|
|
1207
1272
|
lastAnswer = error instanceof Error ? error.message : String(error);
|
|
1208
1273
|
}
|
|
1209
1274
|
}
|
|
1210
1275
|
else if (parts[1] === 'on' || parts[1] === 'off') {
|
|
1211
|
-
lastAnswer = `Usage: /skills ${parts[1]} NAME`;
|
|
1276
|
+
lastAnswer = `Usage: /skills ${parts[1]} NAME|PATTERN (* matches all user skills)`;
|
|
1212
1277
|
}
|
|
1213
1278
|
else if (parts[1]) {
|
|
1214
1279
|
const skill = discoveredSkills.find((item) => item.name === parts[1]);
|
|
@@ -1233,21 +1298,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1233
1298
|
}
|
|
1234
1299
|
if (trimmed === '/help') {
|
|
1235
1300
|
lastQuestion = trimmed;
|
|
1236
|
-
lastAnswer = 'Available slash commands:\n' +
|
|
1237
|
-
' /connect - Choose a Gateway, hosted, or local provider\n' +
|
|
1238
|
-
' /model - Switch active AI model\n' +
|
|
1239
|
-
' /init - Scaffold AGENTS.md from workspace scan\n' +
|
|
1240
|
-
' /mcp - Manage local MCP servers\n' +
|
|
1241
|
-
' /session - Create, resume, undo, redo, share, export, or compact sessions\n' +
|
|
1242
|
-
' /queue - Add, inspect, or clear queued follow-up commands\n' +
|
|
1243
|
-
' /workflow - Inspect Git, build plans, or undo agent changes\n' +
|
|
1244
|
-
' /review - Review current workspace changes for bugs\n' +
|
|
1245
|
-
' /map - Show a compact workspace file map\n' +
|
|
1246
|
-
' /budget - Set a per-task token limit (/budget 5000, /budget off)\n' +
|
|
1247
|
-
' /skills - List skills; use /skills on NAME or /skills off NAME\n' +
|
|
1248
|
-
' /clear - Reset conversation memory\n' +
|
|
1249
|
-
' /help - Display commands, shortcuts & version\n' +
|
|
1250
|
-
' /exit - Exit Moderado';
|
|
1301
|
+
lastAnswer = 'Available slash commands:\n' + getHelpCommandLines().map((line) => line.includes(' - ') ? line : `\n${line}`).join('\n');
|
|
1251
1302
|
lastThoughtTime = 0.001;
|
|
1252
1303
|
lastOutputTokenRate = undefined;
|
|
1253
1304
|
lastTokenUsage = undefined;
|
|
@@ -1264,7 +1315,6 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1264
1315
|
}
|
|
1265
1316
|
const expandedTurn = await expandMentions(trimmed, createWorkspaceFileSource(canonicalWorkspace));
|
|
1266
1317
|
const effectivePrompt = expandedTurn.text;
|
|
1267
|
-
networkAccessApproved = isNetworkConsentReply(trimmed, lastAnswer);
|
|
1268
1318
|
// 1. Instant local meta queries (model/provider status)
|
|
1269
1319
|
const localMetaAnswer = resolveLocalMetaQuery(trimmed, {
|
|
1270
1320
|
currentModel,
|
|
@@ -1405,18 +1455,30 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1405
1455
|
sessionTokens = activeSession.usage.totalTokens;
|
|
1406
1456
|
usageRecorded = true;
|
|
1407
1457
|
};
|
|
1458
|
+
let streamedAnswer = '';
|
|
1408
1459
|
try {
|
|
1409
1460
|
const requestAbort = new AbortController();
|
|
1410
1461
|
const requestSignal = signal ? AbortSignal.any([signal, requestAbort.signal]) : requestAbort.signal;
|
|
1411
|
-
let
|
|
1462
|
+
let lastSessionCheckpointAt = 0;
|
|
1463
|
+
const checkpointInterruptedTurn = () => {
|
|
1464
|
+
activeSession.messages = saveFailedTurnPrompt(conversationHistory, effectivePrompt, streamedAnswer);
|
|
1465
|
+
activeSession.providerId = activeConnection?.id;
|
|
1466
|
+
activeSession.providerName = activeConnection?.displayName;
|
|
1467
|
+
activeSession.modelId = currentModel;
|
|
1468
|
+
activeSession.mode = activeMode;
|
|
1469
|
+
sessionStore.save(activeSession);
|
|
1470
|
+
lastSessionCheckpointAt = Date.now();
|
|
1471
|
+
};
|
|
1412
1472
|
lastAnswer = '';
|
|
1473
|
+
lastTurnDone = false;
|
|
1474
|
+
let taskStep;
|
|
1413
1475
|
let firstAssistantDeltaAt;
|
|
1414
1476
|
let outputTokenRate;
|
|
1415
1477
|
let budgetWarningIssued = false;
|
|
1416
1478
|
let budgetStopped = false;
|
|
1417
1479
|
let lastUsagePaint = 0;
|
|
1418
1480
|
const usedModels = new Set();
|
|
1419
|
-
let answerPosition = { row: 15, column:
|
|
1481
|
+
let answerPosition = { row: 15, column: getChatAnswerArea(process.stdout.columns).startColumn };
|
|
1420
1482
|
let lastErrorEvent;
|
|
1421
1483
|
const redrawChatFrame = () => {
|
|
1422
1484
|
const thoughtTime = Math.max(0.001, (Date.now() - startedAt) / 1000);
|
|
@@ -1433,6 +1495,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1433
1495
|
chatQuestion: trimmed,
|
|
1434
1496
|
chatAnswer: displayAnswer,
|
|
1435
1497
|
chatThoughtTime: thoughtTime,
|
|
1498
|
+
chatTaskDone: lastTurnDone,
|
|
1436
1499
|
outputTokenRate,
|
|
1437
1500
|
tokenUsage: turnUsage,
|
|
1438
1501
|
budgetStatus: budgetStopped ? 'Budget reached' : budgetWarningIssued ? 'Budget ≥80%' : undefined,
|
|
@@ -1441,9 +1504,8 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1441
1504
|
}, process.stdout.rows));
|
|
1442
1505
|
process.stdout.write(renderChatComposerCursor({ width: process.stdout.columns }, commandQueue.currentDraft.length));
|
|
1443
1506
|
if (displayAnswer) {
|
|
1444
|
-
const
|
|
1445
|
-
|
|
1446
|
-
answerPosition = renderChatAnswerDelta(displayAnswer, { row: 15, column: 1 }, maxWidth);
|
|
1507
|
+
const area = getChatAnswerArea(process.stdout.columns);
|
|
1508
|
+
answerPosition = renderChatAnswerDelta(displayAnswer, { row: 15, column: area.startColumn }, area.wrapWidth, area.startColumn);
|
|
1447
1509
|
}
|
|
1448
1510
|
};
|
|
1449
1511
|
const onGenerationKeypress = (str, key) => {
|
|
@@ -1533,7 +1595,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1533
1595
|
redrawChatFrame();
|
|
1534
1596
|
thinkingTimer = setInterval(() => {
|
|
1535
1597
|
if (!firstAssistantDeltaAt && !turnUsage) {
|
|
1536
|
-
process.stdout.write(renderChatThoughtTimeUpdate((Date.now() - startedAt) / 1000));
|
|
1598
|
+
process.stdout.write(renderChatThoughtTimeUpdate((Date.now() - startedAt) / 1000, taskStep, process.stdout.columns));
|
|
1537
1599
|
}
|
|
1538
1600
|
}, 400);
|
|
1539
1601
|
const evidenceTask = liveSearch ? buildSearchAnswerTask(effectivePrompt, liveSearch) : undefined;
|
|
@@ -1545,6 +1607,11 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1545
1607
|
eventListener: (event) => {
|
|
1546
1608
|
if (event.type === 'model_change')
|
|
1547
1609
|
usedModels.add(event.newModelId);
|
|
1610
|
+
if (event.type === 'progress') {
|
|
1611
|
+
taskStep = { step: event.step, maxSteps: event.maxSteps ?? 0 };
|
|
1612
|
+
if (!firstAssistantDeltaAt)
|
|
1613
|
+
process.stdout.write(renderChatThoughtTimeUpdate((Date.now() - startedAt) / 1000, taskStep, process.stdout.columns));
|
|
1614
|
+
}
|
|
1548
1615
|
if (event.type === 'gateway_status')
|
|
1549
1616
|
process.stdout.write(renderGatewayFallbackUpdate(event, process.stdout.columns || 80));
|
|
1550
1617
|
if (event.type === 'usage') {
|
|
@@ -1571,7 +1638,10 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1571
1638
|
clearInterval(thinkingTimer);
|
|
1572
1639
|
}
|
|
1573
1640
|
streamedAnswer += event.delta;
|
|
1574
|
-
|
|
1641
|
+
if (Date.now() - lastSessionCheckpointAt >= 1_000)
|
|
1642
|
+
checkpointInterruptedTurn();
|
|
1643
|
+
const area = getChatAnswerArea(process.stdout.columns);
|
|
1644
|
+
const renderedDelta = renderChatAnswerDelta(event.delta, answerPosition, area.wrapWidth, area.startColumn);
|
|
1575
1645
|
answerPosition = renderedDelta;
|
|
1576
1646
|
process.stdout.write(renderedDelta.sequence);
|
|
1577
1647
|
process.stdout.write(renderChatComposerCursor({ width: process.stdout.columns }, commandQueue.currentDraft.length));
|
|
@@ -1593,6 +1663,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1593
1663
|
});
|
|
1594
1664
|
let result;
|
|
1595
1665
|
try {
|
|
1666
|
+
checkpointInterruptedTurn();
|
|
1596
1667
|
result = await runAgent();
|
|
1597
1668
|
}
|
|
1598
1669
|
catch (error) {
|
|
@@ -1668,7 +1739,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1668
1739
|
streamedAnswer = lastAnswer;
|
|
1669
1740
|
}
|
|
1670
1741
|
else if (resolution.isError) {
|
|
1671
|
-
conversationHistory = saveFailedTurnPrompt(conversationHistory, effectivePrompt);
|
|
1742
|
+
conversationHistory = saveFailedTurnPrompt(conversationHistory, effectivePrompt, streamedAnswer);
|
|
1672
1743
|
activeSession.messages = conversationHistory;
|
|
1673
1744
|
activeSession.providerId = activeConnection?.id;
|
|
1674
1745
|
activeSession.providerName = activeConnection?.displayName;
|
|
@@ -1688,6 +1759,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1688
1759
|
}
|
|
1689
1760
|
if (activeMode === 'Plan')
|
|
1690
1761
|
activePlan = lastAnswer;
|
|
1762
|
+
lastTurnDone = result.status === 'completed' && !budgetStopped && !resolution.isError;
|
|
1691
1763
|
lastQuestion = trimmed;
|
|
1692
1764
|
lastThoughtTime = Math.max(0.001, (Date.now() - startedAt) / 1000);
|
|
1693
1765
|
lastOutputTokenRate = outputTokenRate;
|
|
@@ -1708,7 +1780,7 @@ export async function handleChatSession(args, version, signal) {
|
|
|
1708
1780
|
? ` (other configured providers: ${otherConnections.join(', ')})`
|
|
1709
1781
|
: '';
|
|
1710
1782
|
lastQuestion = trimmed;
|
|
1711
|
-
conversationHistory = saveFailedTurnPrompt(conversationHistory, effectivePrompt);
|
|
1783
|
+
conversationHistory = saveFailedTurnPrompt(conversationHistory, effectivePrompt, streamedAnswer);
|
|
1712
1784
|
activeSession.messages = conversationHistory;
|
|
1713
1785
|
activeSession.providerId = activeConnection?.id;
|
|
1714
1786
|
activeSession.providerName = activeConnection?.displayName;
|