@heysalad/cheri-cli 1.3.0 โ 1.3.2
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 +49 -0
- package/package.json +1 -1
- package/src/commands/agent.js +78 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Cheri CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.2] - 2026-02-17
|
|
6
|
+
|
|
7
|
+
### ๐ง Improvements
|
|
8
|
+
|
|
9
|
+
#### CLI Core
|
|
10
|
+
- **Version from package.json**: CLI now reads version dynamically from `package.json` instead of hardcoding
|
|
11
|
+
- **Agent & usage commands registered**: `cheri agent` and `cheri usage` now properly registered in the main binary
|
|
12
|
+
- **Interactive REPL on no args**: Running `cheri` with no arguments now launches the interactive REPL instead of showing help
|
|
13
|
+
|
|
14
|
+
#### Configuration
|
|
15
|
+
- **Multi-layer config system**: Config now merges defaults โ user config โ project config โ env vars (highest priority)
|
|
16
|
+
- **Deep merge**: Nested config objects merge correctly instead of overwriting
|
|
17
|
+
- **Environment variable support**: All settings overridable via `CHERI_*` env vars
|
|
18
|
+
|
|
19
|
+
#### Commands
|
|
20
|
+
- **workspace**: Extracted `launchWorkspace()`, `stopWorkspace()`, `listWorkspaces()` as importable functions
|
|
21
|
+
- **status**: Extracted `showStatus()` as importable function for use in REPL
|
|
22
|
+
- **memory, config, init**: Consistent refactor to exportable functions
|
|
23
|
+
|
|
24
|
+
#### Backend
|
|
25
|
+
- **Default model switched to GPT-OSS 120B**: Faster true streaming via Bedrock OpenAI-compatible endpoint
|
|
26
|
+
- **Durable Objects fixed**: WorkspaceDO and SessionDO now correctly bound in deployed worker
|
|
27
|
+
- **Claude Sonnet 4.5 added**: Available as selectable model (`us.anthropic.claude-sonnet-4-5-20250929-v1:0`)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## [1.3.1] - 2026-02-17
|
|
32
|
+
|
|
33
|
+
### ๐ Bug Fixes
|
|
34
|
+
|
|
35
|
+
#### Permission System
|
|
36
|
+
- **Fixed spinner blocking approval prompts**: Approval prompts now appear BEFORE spinners start, preventing terminal blocking
|
|
37
|
+
- **Added upfront permission request**: Tasks with write operations now ask for permissions at the start
|
|
38
|
+
- **Better user experience**: Users can approve entire session with [a]lways option
|
|
39
|
+
|
|
40
|
+
#### How It Works
|
|
41
|
+
1. Agent analyzes task for write operations (build, create, modify, etc.)
|
|
42
|
+
2. Shows upfront permission request: `[Y]es, [n]o, [a]lways`
|
|
43
|
+
3. If approved, proceeds with task
|
|
44
|
+
4. Individual risky operations still prompt for confirmation (unless 'always' was chosen)
|
|
45
|
+
|
|
46
|
+
#### Changes
|
|
47
|
+
- Moved approval logic before `ToolPanel` creation to avoid spinner blocking input
|
|
48
|
+
- Added `requestUpfrontPermissions()` function to analyze and request batch permissions
|
|
49
|
+
- Added `skipApproval` parameter to `executeTool()` to avoid double prompting
|
|
50
|
+
- Improved permission flow for better UX
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
5
54
|
## [1.3.0] - 2026-02-17
|
|
6
55
|
|
|
7
56
|
### ๐ฏ Major Feature - Token-Tracked Sessions with Predictable Costs
|
package/package.json
CHANGED
package/src/commands/agent.js
CHANGED
|
@@ -181,7 +181,7 @@ async function executeLocalToolEnhanced(name, args) {
|
|
|
181
181
|
// โโ Main tool execution with approval + hooks + permissions โโโโโโโโโโโโโโโโโโโ
|
|
182
182
|
let sessionAutoApprove = false;
|
|
183
183
|
|
|
184
|
-
async function executeTool(name, args, orchestrator, allTools, parseSSE, mcpManager) {
|
|
184
|
+
async function executeTool(name, args, orchestrator, allTools, parseSSE, mcpManager, skipApproval = false) {
|
|
185
185
|
// Permission rules check
|
|
186
186
|
const permission = checkPermission(name, args);
|
|
187
187
|
if (permission === "deny") return { error: `Tool ${name} is denied by permission rules` };
|
|
@@ -212,7 +212,9 @@ async function executeTool(name, args, orchestrator, allTools, parseSSE, mcpMana
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
// Local tools โ enhanced approval system
|
|
215
|
-
|
|
215
|
+
// Note: For tools executed via executeOne in agent loop, approval is handled
|
|
216
|
+
// BEFORE spinner creation to avoid blocking terminal. This is a fallback.
|
|
217
|
+
if (!sessionAutoApprove && !skipApproval) {
|
|
216
218
|
const decision = shouldApprove(name, args);
|
|
217
219
|
if (decision === "deny") return { error: "Denied by approval policy" };
|
|
218
220
|
if (decision === "ask" || decision === "suggest") {
|
|
@@ -286,6 +288,51 @@ async function* parseSSEStream(response) {
|
|
|
286
288
|
}
|
|
287
289
|
}
|
|
288
290
|
|
|
291
|
+
// โโ Upfront permission request โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
292
|
+
async function requestUpfrontPermissions(userRequest) {
|
|
293
|
+
const approvalMode = getApprovalMode();
|
|
294
|
+
|
|
295
|
+
// Skip if in auto mode or already auto-approved this session
|
|
296
|
+
if (approvalMode === "auto" || sessionAutoApprove) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Check if request likely involves write operations
|
|
301
|
+
const writeKeywords = ['build', 'create', 'make', 'write', 'add', 'implement', 'generate', 'fix', 'update', 'modify', 'delete', 'install', 'setup'];
|
|
302
|
+
const hasWriteIntent = writeKeywords.some(kw => userRequest.toLowerCase().includes(kw));
|
|
303
|
+
|
|
304
|
+
if (!hasWriteIntent) {
|
|
305
|
+
return true; // No write operations expected, proceed
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Show upfront permission request
|
|
309
|
+
console.log("");
|
|
310
|
+
log.info("This task may require file modifications and command execution.");
|
|
311
|
+
console.log("");
|
|
312
|
+
|
|
313
|
+
return new Promise((resolve) => {
|
|
314
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
315
|
+
rl.question(
|
|
316
|
+
chalk.yellow(` Grant permissions for this session? ${chalk.dim("[Y]es, [n]o, [a]lways")} `),
|
|
317
|
+
(answer) => {
|
|
318
|
+
rl.close();
|
|
319
|
+
const a = answer.trim().toLowerCase();
|
|
320
|
+
if (a === "a" || a === "always") {
|
|
321
|
+
sessionAutoApprove = true;
|
|
322
|
+
log.success("Auto-approve enabled for this session");
|
|
323
|
+
resolve(true);
|
|
324
|
+
} else if (a === "n" || a === "no") {
|
|
325
|
+
log.warn("Permissions denied");
|
|
326
|
+
resolve(false);
|
|
327
|
+
} else {
|
|
328
|
+
log.success("Permissions granted - will ask before risky operations");
|
|
329
|
+
resolve(true);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
289
336
|
// โโ Active session state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
290
337
|
let currentSession = null;
|
|
291
338
|
|
|
@@ -297,6 +344,13 @@ export async function runAgent(userRequest, options = {}) {
|
|
|
297
344
|
const memoryContext = getMemoryContext();
|
|
298
345
|
const skillContext = getSkillContext(plugins.skills, userRequest);
|
|
299
346
|
|
|
347
|
+
// Request upfront permissions if needed
|
|
348
|
+
const hasPermission = await requestUpfrontPermissions(userRequest);
|
|
349
|
+
if (!hasPermission) {
|
|
350
|
+
log.error("Task cancelled - permissions denied");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
300
354
|
// Create provider (defaults to Cheri cloud which uses AWS Bedrock)
|
|
301
355
|
const providerName = getConfigValue("agent.provider") || getConfigValue("ai.provider") || "cheri";
|
|
302
356
|
let provider;
|
|
@@ -567,7 +621,26 @@ export async function runAgent(userRequest, options = {}) {
|
|
|
567
621
|
const isMcp = mcpManager.isMcpTool(tc.name);
|
|
568
622
|
const isLocal = !CLOUD_TOOL_NAMES.has(tc.name) && !AGENT_TOOL_NAMES.has(tc.name) && !isMcp;
|
|
569
623
|
|
|
570
|
-
//
|
|
624
|
+
// Check if approval is needed BEFORE creating spinner
|
|
625
|
+
let needsApproval = false;
|
|
626
|
+
if (isLocal && !sessionAutoApprove) {
|
|
627
|
+
const decision = shouldApprove(tc.name, input);
|
|
628
|
+
if (decision === "ask" || decision === "suggest") {
|
|
629
|
+
needsApproval = true;
|
|
630
|
+
|
|
631
|
+
// Get approval WITHOUT spinner running
|
|
632
|
+
const approved = await promptApproval(tc.name, input, decision);
|
|
633
|
+
if (approved === "auto") {
|
|
634
|
+
sessionAutoApprove = true;
|
|
635
|
+
} else if (!approved) {
|
|
636
|
+
return { id: tc.id, result: { error: "User denied execution" } };
|
|
637
|
+
}
|
|
638
|
+
} else if (decision === "deny") {
|
|
639
|
+
return { id: tc.id, result: { error: "Denied by approval policy" } };
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Create animated panel for this tool execution (AFTER approval)
|
|
571
644
|
const panel = new ToolPanel(tc.name, input);
|
|
572
645
|
|
|
573
646
|
// Command safety label for run_command
|
|
@@ -577,7 +650,8 @@ export async function runAgent(userRequest, options = {}) {
|
|
|
577
650
|
panel.update(`running ${safetyLabel}`);
|
|
578
651
|
}
|
|
579
652
|
|
|
580
|
-
|
|
653
|
+
// Skip approval in executeTool since we already handled it above
|
|
654
|
+
const result = await executeTool(tc.name, input, orchestrator, ALL_TOOLS, parseSSEStream, mcpManager, needsApproval);
|
|
581
655
|
|
|
582
656
|
// Determine result message based on tool type
|
|
583
657
|
let resultMsg = '';
|