comisai 1.0.8 → 1.0.10
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/node_modules/@comis/agent/dist/executor/executor-post-execution.js +2 -1
- package/node_modules/@comis/agent/dist/executor/executor-response-filter.js +33 -0
- package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +4 -1
- package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +5 -0
- package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +20 -1
- package/node_modules/@comis/agent/dist/executor/schema-stripping.d.ts +1 -1
- package/node_modules/@comis/agent/dist/executor/schema-stripping.js +8 -1
- package/node_modules/@comis/agent/dist/executor/session-snapshot-cleanup.js +2 -1
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/commands/agent.js +19 -27
- package/node_modules/@comis/cli/dist/commands/daemon.js +15 -2
- package/node_modules/@comis/cli/dist/commands/models.js +2 -2
- package/node_modules/@comis/cli/dist/commands/reset.js +19 -3
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/package.json +1 -1
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/dist/builtin/exec-security.js +2 -2
- package/node_modules/@comis/skills/dist/builtin/exec-tool.js +1 -1
- package/node_modules/@comis/skills/dist/integrations/media-handler-audio.js +1 -1
- package/node_modules/@comis/skills/package.json +1 -1
- package/package.json +12 -12
|
@@ -332,6 +332,7 @@ export async function postExecution(params) {
|
|
|
332
332
|
// Strip verbose <functions> blocks from discover_tools results
|
|
333
333
|
// in session history. Runs post-execution so the current turn's model
|
|
334
334
|
// saw full schemas. Safe no-op when no discover_tools results exist.
|
|
335
|
-
|
|
335
|
+
// Fence-aware: entries at or below the cache fence are not stripped to preserve prefix stability.
|
|
336
|
+
stripDiscoverySchemas(sm, deps.logger, finalBreakpointIdx);
|
|
336
337
|
session.dispose();
|
|
337
338
|
}
|
|
@@ -85,6 +85,11 @@ export function scanWithOutputGuard(params) {
|
|
|
85
85
|
// ---------------------------------------------------------------------------
|
|
86
86
|
/** Silent tokens that indicate the final message has no visible content. */
|
|
87
87
|
const SILENT_FINAL_TOKENS = ["NO_REPLY", "HEARTBEAT_OK"];
|
|
88
|
+
/** Tool names whose successful invocation means the agent already delivered
|
|
89
|
+
* content to the user through a side-channel. When one of these tools was
|
|
90
|
+
* called during the execution, a silent final token (NO_REPLY) is intentional
|
|
91
|
+
* and recovery must be suppressed to avoid leaking internal narration. */
|
|
92
|
+
const DELIVERY_TOOL_NAMES = ["message", "notify"];
|
|
88
93
|
/**
|
|
89
94
|
* When the final assistant message is thinking-only or a
|
|
90
95
|
* silent token (NO_REPLY, HEARTBEAT_OK) but text was emitted in earlier
|
|
@@ -107,6 +112,17 @@ export function recoverEmptyFinalResponse(params) {
|
|
|
107
112
|
const isSilentFinalToken = SILENT_FINAL_TOKENS.includes(extractedResponse.trim());
|
|
108
113
|
if ((extractedResponse === "" || isSilentFinalToken) && textEmitted) {
|
|
109
114
|
if (Array.isArray(messages)) {
|
|
115
|
+
// Guard: if the agent already delivered content via a delivery tool
|
|
116
|
+
// (message, notify), the silent final token is intentional — skip
|
|
117
|
+
// recovery to avoid leaking internal narration (e.g. "Now let me
|
|
118
|
+
// generate the chart:" surfaced as a user-visible message).
|
|
119
|
+
if (isSilentFinalToken && hasDeliveryToolCall(messages, lowerBound)) {
|
|
120
|
+
logger.debug({
|
|
121
|
+
hint: "Silent final token after delivery tool call — recovery suppressed",
|
|
122
|
+
extractedResponse,
|
|
123
|
+
}, "Skipping empty-response recovery (delivery tool used)");
|
|
124
|
+
return extractedResponse;
|
|
125
|
+
}
|
|
110
126
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
111
127
|
// Pass 1: backward walk — prefer the most recent standalone response
|
|
112
128
|
// (assistant turns that have text but NO tool call blocks)
|
|
@@ -172,6 +188,23 @@ function extractVisibleText(content) {
|
|
|
172
188
|
return undefined;
|
|
173
189
|
}
|
|
174
190
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
191
|
+
/** Check whether any assistant turn (from lowerBound onward) contains a tool
|
|
192
|
+
* call to a delivery tool (message, notify). These tools send content to the
|
|
193
|
+
* user through a side-channel, so a subsequent NO_REPLY is intentional. */
|
|
194
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
195
|
+
function hasDeliveryToolCall(messages, lowerBound) {
|
|
196
|
+
for (let i = lowerBound; i < messages.length; i++) {
|
|
197
|
+
const msg = messages[i]; // eslint-disable-line security/detect-object-injection
|
|
198
|
+
if (msg?.role === "assistant" && Array.isArray(msg.content)) {
|
|
199
|
+
const hasDelivery = msg.content.some((b) => (b?.type === "toolCall" || b?.type === "tool_use") &&
|
|
200
|
+
DELIVERY_TOOL_NAMES.includes(b?.name));
|
|
201
|
+
if (hasDelivery)
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
175
208
|
// ---------------------------------------------------------------------------
|
|
176
209
|
// SEP plan extraction (extracted from execute() success path)
|
|
177
210
|
// ---------------------------------------------------------------------------
|
|
@@ -50,7 +50,10 @@ import type { AgentExecutor } from "./types.js";
|
|
|
50
50
|
*
|
|
51
51
|
* Extracted as a named function for independent unit testing.
|
|
52
52
|
*/
|
|
53
|
-
export declare function createBeforeToolCallGuard(stepCounter: StepCounter, budgetGuard: BudgetGuard, circuitBreaker: CircuitBreaker, toolRetryBreaker?: ToolRetryBreaker, messageSendLimiter?: MessageSendLimiter): (context: unknown, _signal?: AbortSignal) => Promise<
|
|
53
|
+
export declare function createBeforeToolCallGuard(stepCounter: StepCounter, budgetGuard: BudgetGuard, circuitBreaker: CircuitBreaker, toolRetryBreaker?: ToolRetryBreaker, messageSendLimiter?: MessageSendLimiter): (context: unknown, _signal?: AbortSignal) => Promise<{
|
|
54
|
+
block: boolean;
|
|
55
|
+
reason: string;
|
|
56
|
+
} | undefined>;
|
|
54
57
|
/**
|
|
55
58
|
* Merge SDK session stats into execution result for token totals (R-13).
|
|
56
59
|
*
|
|
@@ -46,6 +46,11 @@ export declare function clearSessionToolNameSnapshot(sessionKey: string): void;
|
|
|
46
46
|
* Call during session cleanup to prevent the Map from growing unbounded.
|
|
47
47
|
*/
|
|
48
48
|
export declare function clearSessionBootstrapFileSnapshot(sessionKey: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Clear the cached prompt skills XML snapshot for a session.
|
|
51
|
+
* Call during session cleanup to prevent the Map from growing unbounded.
|
|
52
|
+
*/
|
|
53
|
+
export declare function clearSessionPromptSkillsXmlSnapshot(sessionKey: string): void;
|
|
49
54
|
/** Frozen prompt state captured after first-turn assembly for sub-agent cache prefix sharing.
|
|
50
55
|
* When propagated to sub-agents via SpawnPacket, allows prefix reuse instead of independent assembly. */
|
|
51
56
|
export interface CacheSafeParams {
|
|
@@ -57,6 +57,11 @@ const sessionBootstrapFileSnapshots = new Map();
|
|
|
57
57
|
* Captured once per session at the end of first assembleExecutionPrompt call.
|
|
58
58
|
* Sub-agents read this via getCacheSafeParams() to reuse parent prefix. */
|
|
59
59
|
const sessionCacheSafeParams = new Map();
|
|
60
|
+
/** Per-session prompt skills XML snapshot for stable system prompt assembly.
|
|
61
|
+
* On first execution, captures the promptSkillsXml string. Subsequent executions
|
|
62
|
+
* reuse the snapshot so skills XML fed to assembleRichSystemPrompt stays constant,
|
|
63
|
+
* preventing cache-invalidating changes when the agent creates skills mid-session. */
|
|
64
|
+
const sessionPromptSkillsXmlSnapshots = new Map();
|
|
60
65
|
// ---------------------------------------------------------------------------
|
|
61
66
|
// Feature flag hash for tool cache key invalidation.
|
|
62
67
|
// Computes a stable string from config fields that affect tool rendering.
|
|
@@ -95,6 +100,13 @@ export function clearSessionToolNameSnapshot(sessionKey) {
|
|
|
95
100
|
export function clearSessionBootstrapFileSnapshot(sessionKey) {
|
|
96
101
|
sessionBootstrapFileSnapshots.delete(sessionKey);
|
|
97
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Clear the cached prompt skills XML snapshot for a session.
|
|
105
|
+
* Call during session cleanup to prevent the Map from growing unbounded.
|
|
106
|
+
*/
|
|
107
|
+
export function clearSessionPromptSkillsXmlSnapshot(sessionKey) {
|
|
108
|
+
sessionPromptSkillsXmlSnapshots.delete(sessionKey);
|
|
109
|
+
}
|
|
98
110
|
/**
|
|
99
111
|
* Get the frozen prompt state for a session (sub-agent cache prefix sharing).
|
|
100
112
|
* Returns undefined if no params captured yet (session hasn't completed first turn).
|
|
@@ -458,7 +470,14 @@ export async function assembleExecutionPrompt(params) {
|
|
|
458
470
|
sessionToolNameSnapshots.set(snapshotKey, toolNames);
|
|
459
471
|
}
|
|
460
472
|
const hasMemoryTools = stableToolNames.includes("memory_store") || stableToolNames.includes("memory_search");
|
|
461
|
-
|
|
473
|
+
// Snapshot promptSkillsXml on first turn to keep system prompt stable.
|
|
474
|
+
// Skills created mid-session grow the XML (~540 chars per skill), invalidating
|
|
475
|
+
// the entire system prompt cache prefix on every subsequent turn.
|
|
476
|
+
let promptSkillsXml = sessionPromptSkillsXmlSnapshots.get(snapshotKey);
|
|
477
|
+
if (promptSkillsXml === undefined && !sessionPromptSkillsXmlSnapshots.has(snapshotKey)) {
|
|
478
|
+
promptSkillsXml = deps.getPromptSkillsXml?.() ?? undefined;
|
|
479
|
+
sessionPromptSkillsXmlSnapshots.set(snapshotKey, promptSkillsXml);
|
|
480
|
+
}
|
|
462
481
|
const activePromptSkillContent = msg.metadata?.promptSkillContent;
|
|
463
482
|
// Extract user's preferred language from USER.md (if present)
|
|
464
483
|
const userLanguage = extractUserLanguage(bootstrapContextFiles);
|
|
@@ -26,15 +26,19 @@
|
|
|
26
26
|
* @param sm - SessionManager instance (from withSession callback)
|
|
27
27
|
* @param logger - Optional logger for DEBUG-level stripping stats
|
|
28
28
|
*/
|
|
29
|
-
export function stripDiscoverySchemas(sm, logger) {
|
|
29
|
+
export function stripDiscoverySchemas(sm, logger, cacheFenceIndex) {
|
|
30
30
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
31
31
|
const sessionManager = sm;
|
|
32
32
|
const fileEntries = sessionManager.fileEntries;
|
|
33
33
|
if (!Array.isArray(fileEntries))
|
|
34
34
|
return;
|
|
35
|
+
const fence = cacheFenceIndex ?? -1;
|
|
35
36
|
let strippedCount = 0;
|
|
36
37
|
let totalCharsSaved = 0;
|
|
38
|
+
let messageIdx = -1;
|
|
37
39
|
for (const entry of fileEntries) {
|
|
40
|
+
if (entry.type === "message")
|
|
41
|
+
messageIdx++;
|
|
38
42
|
if (entry.type !== "message")
|
|
39
43
|
continue;
|
|
40
44
|
const msg = entry.message;
|
|
@@ -42,6 +46,9 @@ export function stripDiscoverySchemas(sm, logger) {
|
|
|
42
46
|
continue;
|
|
43
47
|
if (msg.toolName !== "discover_tools")
|
|
44
48
|
continue;
|
|
49
|
+
// Entries at or below the cache fence must not be modified to preserve prefix stability
|
|
50
|
+
if (messageIdx <= fence)
|
|
51
|
+
continue;
|
|
45
52
|
const content = msg.content;
|
|
46
53
|
if (!Array.isArray(content) || content.length === 0)
|
|
47
54
|
continue;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @module
|
|
15
15
|
*/
|
|
16
16
|
import { formatSessionKey } from "@comis/core";
|
|
17
|
-
import { clearSessionToolNameSnapshot, clearSessionBootstrapFileSnapshot, clearCacheSafeParams } from "./prompt-assembly.js";
|
|
17
|
+
import { clearSessionToolNameSnapshot, clearSessionBootstrapFileSnapshot, clearSessionPromptSkillsXmlSnapshot, clearCacheSafeParams } from "./prompt-assembly.js";
|
|
18
18
|
import { clearSessionDeliveredGuides, clearSessionToolSchemaSnapshot, clearSessionToolSchemaSnapshotHash, clearSessionBreakpointIndex, clearSessionCacheWarm, clearSessionLatches, clearSessionEvictionCooldown, clearSessionCacheSavings } from "./executor-session-state.js";
|
|
19
19
|
import { clearSessionTracker } from "./tool-lifecycle.js";
|
|
20
20
|
import { clearDiscoveryTracker } from "./discovery-tracker.js";
|
|
@@ -31,6 +31,7 @@ import { clearSessionBlockStability } from "./block-stability-tracker.js";
|
|
|
31
31
|
export function clearSessionState(formattedKey) {
|
|
32
32
|
clearSessionToolNameSnapshot(formattedKey);
|
|
33
33
|
clearSessionBootstrapFileSnapshot(formattedKey);
|
|
34
|
+
clearSessionPromptSkillsXmlSnapshot(formattedKey);
|
|
34
35
|
clearCacheSafeParams(formattedKey);
|
|
35
36
|
clearSessionDeliveredGuides(formattedKey);
|
|
36
37
|
clearSessionToolSchemaSnapshot(formattedKey);
|
|
@@ -28,9 +28,7 @@ export function registerAgentCommand(program) {
|
|
|
28
28
|
.action(async (options) => {
|
|
29
29
|
try {
|
|
30
30
|
const result = await withSpinner("Fetching agents...", () => withClient(async (client) => {
|
|
31
|
-
|
|
32
|
-
const routingResult = (await client.call("config.get", { section: "routing" }));
|
|
33
|
-
return { ...agentsResult, ...routingResult };
|
|
31
|
+
return (await client.call("config.get", { section: "agents" }));
|
|
34
32
|
}));
|
|
35
33
|
const agents = extractAgents(result);
|
|
36
34
|
if (agents.length === 0) {
|
|
@@ -64,14 +62,13 @@ export function registerAgentCommand(program) {
|
|
|
64
62
|
try {
|
|
65
63
|
const agentConfig = { name };
|
|
66
64
|
if (options.provider)
|
|
67
|
-
agentConfig["
|
|
65
|
+
agentConfig["provider"] = options.provider;
|
|
68
66
|
if (options.model)
|
|
69
|
-
agentConfig["
|
|
67
|
+
agentConfig["model"] = options.model;
|
|
70
68
|
await withSpinner(`Creating agent "${name}"...`, () => withClient(async (client) => {
|
|
71
|
-
return await client.call("
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
value: agentConfig,
|
|
69
|
+
return await client.call("agents.create", {
|
|
70
|
+
agentId: name,
|
|
71
|
+
config: agentConfig,
|
|
75
72
|
});
|
|
76
73
|
}));
|
|
77
74
|
// Initialize dedicated workspace for the new agent
|
|
@@ -109,14 +106,13 @@ export function registerAgentCommand(program) {
|
|
|
109
106
|
try {
|
|
110
107
|
const updates = {};
|
|
111
108
|
if (options.provider)
|
|
112
|
-
updates["
|
|
109
|
+
updates["provider"] = options.provider;
|
|
113
110
|
if (options.model)
|
|
114
|
-
updates["
|
|
111
|
+
updates["model"] = options.model;
|
|
115
112
|
await withSpinner(`Updating agent "${name}"...`, () => withClient(async (client) => {
|
|
116
|
-
return await client.call("
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
value: updates,
|
|
113
|
+
return await client.call("agents.update", {
|
|
114
|
+
agentId: name,
|
|
115
|
+
config: updates,
|
|
120
116
|
});
|
|
121
117
|
}));
|
|
122
118
|
success(`Agent "${name}" updated`);
|
|
@@ -162,10 +158,8 @@ export function registerAgentCommand(program) {
|
|
|
162
158
|
}
|
|
163
159
|
try {
|
|
164
160
|
await withSpinner(`Deleting agent "${name}"...`, () => withClient(async (client) => {
|
|
165
|
-
return await client.call("
|
|
166
|
-
|
|
167
|
-
key: `agents.${name}`,
|
|
168
|
-
value: null,
|
|
161
|
+
return await client.call("agents.delete", {
|
|
162
|
+
agentId: name,
|
|
169
163
|
});
|
|
170
164
|
}));
|
|
171
165
|
success(`Agent "${name}" deleted`);
|
|
@@ -223,16 +217,16 @@ export function registerAgentCommand(program) {
|
|
|
223
217
|
});
|
|
224
218
|
}
|
|
225
219
|
/**
|
|
226
|
-
* Extract agent entries from
|
|
220
|
+
* Extract agent entries from config.get response for the agents section.
|
|
227
221
|
*
|
|
228
222
|
* Handles various response shapes from the config.get RPC call,
|
|
229
223
|
* normalizing into a flat array of AgentEntry objects.
|
|
230
224
|
*/
|
|
231
225
|
function extractAgents(config) {
|
|
226
|
+
if (!config || typeof config !== "object")
|
|
227
|
+
return [];
|
|
232
228
|
const agents = [];
|
|
233
|
-
|
|
234
|
-
const agentsObj = config["agents"] ??
|
|
235
|
-
config["routing"]?.["agents"];
|
|
229
|
+
const agentsObj = config["agents"];
|
|
236
230
|
if (agentsObj && typeof agentsObj === "object") {
|
|
237
231
|
for (const [name, value] of Object.entries(agentsObj)) {
|
|
238
232
|
if (value && typeof value === "object") {
|
|
@@ -250,10 +244,8 @@ function extractAgents(config) {
|
|
|
250
244
|
}
|
|
251
245
|
}
|
|
252
246
|
}
|
|
253
|
-
// Also check
|
|
254
|
-
const
|
|
255
|
-
const bindings = config["bindings"] ??
|
|
256
|
-
routing?.["bindings"];
|
|
247
|
+
// Also check bindings for agent references
|
|
248
|
+
const bindings = config["bindings"];
|
|
257
249
|
if (Array.isArray(bindings)) {
|
|
258
250
|
for (const binding of bindings) {
|
|
259
251
|
const agentId = typeof binding["agentId"] === "string" ? binding["agentId"] : undefined;
|
|
@@ -190,7 +190,7 @@ async function execSystemctl(manager, ...args) {
|
|
|
190
190
|
// System-scope systemd requires root; escalate via sudo if we're not root
|
|
191
191
|
// eslint-disable-next-line no-restricted-syntax -- CLI bootstrap before SecretManager
|
|
192
192
|
if (manager === "systemd" && process.getuid?.() !== 0) {
|
|
193
|
-
return exec("sudo", ["systemctl", ...fullArgs], { timeout: 15_000 });
|
|
193
|
+
return exec("sudo", ["-n", "systemctl", ...fullArgs], { timeout: 15_000 });
|
|
194
194
|
}
|
|
195
195
|
return exec("systemctl", fullArgs, { timeout: 15_000 });
|
|
196
196
|
}
|
|
@@ -264,7 +264,20 @@ async function handleDaemonStart() {
|
|
|
264
264
|
case "systemd":
|
|
265
265
|
case "systemd-user": {
|
|
266
266
|
if (await isSystemdActive(manager)) {
|
|
267
|
-
|
|
267
|
+
// Verify the CLI can actually talk to the daemon
|
|
268
|
+
try {
|
|
269
|
+
await withClient(async (client) => client.call("system.ping", {}));
|
|
270
|
+
success("Daemon is already running (systemd)");
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
warn("Daemon is running (systemd) but CLI cannot connect");
|
|
274
|
+
if (!existsSync(safePath(os.homedir(), ".comis", "config.yaml"))) {
|
|
275
|
+
info("Run 'comis init' to configure the daemon");
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
info("Try restarting: sudo systemctl restart comis");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
268
281
|
return;
|
|
269
282
|
}
|
|
270
283
|
const scope = manager === "systemd-user" ? "systemd (user scope)" : "systemd";
|
|
@@ -180,8 +180,8 @@ export function registerModelsCommand(program) {
|
|
|
180
180
|
return; // Unreachable but satisfies TS control flow
|
|
181
181
|
}
|
|
182
182
|
// Find the agent entry
|
|
183
|
-
const agentsNode = doc.getIn(["agents"])
|
|
184
|
-
const agentsPath =
|
|
183
|
+
const agentsNode = doc.getIn(["agents"]);
|
|
184
|
+
const agentsPath = ["agents"];
|
|
185
185
|
if (!agentsNode || typeof agentsNode !== "object") {
|
|
186
186
|
error("No agents section found in config");
|
|
187
187
|
info("Configure agents first with 'comis init' or edit config manually");
|
|
@@ -67,20 +67,36 @@ function safeUnlink(filePath) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Check whether an error indicates the daemon is unreachable (not running,
|
|
72
|
+
* wrong port, connection refused) vs. a method-level RPC error.
|
|
73
|
+
*/
|
|
74
|
+
function isDaemonUnreachable(err) {
|
|
75
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
76
|
+
return msg.includes("Cannot connect to daemon") || msg.includes("timed out") || msg.includes("ECONNREFUSED");
|
|
77
|
+
}
|
|
70
78
|
/**
|
|
71
79
|
* Execute reset for sessions target.
|
|
72
80
|
*
|
|
73
|
-
* Tries RPC-based session deletion first (daemon running)
|
|
81
|
+
* Tries RPC-based session deletion first (daemon running): lists all sessions
|
|
82
|
+
* via session.list, then deletes each via session.delete.
|
|
74
83
|
* Falls back to direct SQLite database file removal if daemon is not running.
|
|
75
84
|
*/
|
|
76
85
|
async function resetSessions(dataDir) {
|
|
77
86
|
try {
|
|
78
87
|
await withSpinner("Clearing sessions via daemon...", () => withClient(async (client) => {
|
|
79
|
-
|
|
88
|
+
const result = await client.call("session.list");
|
|
89
|
+
if (result.total === 0)
|
|
90
|
+
return;
|
|
91
|
+
for (const session of result.sessions) {
|
|
92
|
+
await client.call("session.delete", { session_key: session.sessionKey });
|
|
93
|
+
}
|
|
80
94
|
}));
|
|
81
95
|
success("All sessions deleted");
|
|
82
96
|
}
|
|
83
|
-
catch {
|
|
97
|
+
catch (err) {
|
|
98
|
+
if (!isDaemonUnreachable(err))
|
|
99
|
+
throw err;
|
|
84
100
|
// Daemon not running -- try direct file deletion
|
|
85
101
|
const dbPath = dataDir + "/memory.db";
|
|
86
102
|
try {
|
|
@@ -284,11 +284,11 @@ export const DANGEROUS_COMMAND_PATTERNS = [
|
|
|
284
284
|
{ pattern: /\beval\s/, reason: "Shell eval executes arbitrary code" },
|
|
285
285
|
{
|
|
286
286
|
pattern: /\bsource\s/,
|
|
287
|
-
reason: "Shell source executes arbitrary script file",
|
|
287
|
+
reason: "Shell source executes arbitrary script file. For virtualenvs, call the venv binary directly (e.g. .venv/bin/python3, .venv/bin/pip) instead of sourcing activate",
|
|
288
288
|
},
|
|
289
289
|
{
|
|
290
290
|
pattern: /^\.\s+\//,
|
|
291
|
-
reason: "POSIX source (.) executes arbitrary script file",
|
|
291
|
+
reason: "POSIX source (.) executes arbitrary script file. For virtualenvs, call the venv binary directly (e.g. .venv/bin/python3, .venv/bin/pip) instead of sourcing activate",
|
|
292
292
|
},
|
|
293
293
|
// Category H -- Indirect command execution
|
|
294
294
|
{
|
|
@@ -366,7 +366,7 @@ export function createExecTool(workspacePath, registry, secretManager, platformS
|
|
|
366
366
|
}
|
|
367
367
|
// Detect --break-system-packages for post-execution warning
|
|
368
368
|
const breakSystemWarning = command.includes("--break-system-packages")
|
|
369
|
-
? "\u26a0\ufe0f WARNING: --break-system-packages modifies the system Python. Use a virtualenv instead: python3 -m venv .venv &&
|
|
369
|
+
? "\u26a0\ufe0f WARNING: --break-system-packages modifies the system Python. Use a virtualenv instead: python3 -m venv .venv && .venv/bin/pip install ...\n\n"
|
|
370
370
|
: "";
|
|
371
371
|
// Log command start (truncate command to 200 chars for security)
|
|
372
372
|
logger?.debug({ toolName: "exec", command: command.slice(0, 200), background, pty, ...(description && { description }) }, "Exec command start");
|
|
@@ -58,5 +58,5 @@ export async function processAudioAttachment(att, deps, buildHint) {
|
|
|
58
58
|
deps.logger.warn({ url: att.url, error: String(e), hint: "Unexpected STT error; voice message will not be transcribed", errorKind: "internal" }, "Transcription threw unexpectedly");
|
|
59
59
|
deps.logger.debug?.({ url: att.url, reason: "stt-failed", err: String(e) }, "Transcription threw unexpectedly");
|
|
60
60
|
}
|
|
61
|
-
return {};
|
|
61
|
+
return { textPrefix: "[Voice message received but transcription failed — ask the user to send a text message instead]" };
|
|
62
62
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comisai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"author": "Moshe Anconina",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "Security-first AI agent platform — connects AI agents to Discord, Telegram, Slack, WhatsApp, and more",
|
|
@@ -115,17 +115,17 @@
|
|
|
115
115
|
"@comis/daemon"
|
|
116
116
|
],
|
|
117
117
|
"dependencies": {
|
|
118
|
-
"@comis/shared": "1.0.
|
|
119
|
-
"@comis/core": "1.0.
|
|
120
|
-
"@comis/infra": "1.0.
|
|
121
|
-
"@comis/memory": "1.0.
|
|
122
|
-
"@comis/gateway": "1.0.
|
|
123
|
-
"@comis/skills": "1.0.
|
|
124
|
-
"@comis/scheduler": "1.0.
|
|
125
|
-
"@comis/agent": "1.0.
|
|
126
|
-
"@comis/channels": "1.0.
|
|
127
|
-
"@comis/cli": "1.0.
|
|
128
|
-
"@comis/daemon": "1.0.
|
|
118
|
+
"@comis/shared": "1.0.10",
|
|
119
|
+
"@comis/core": "1.0.10",
|
|
120
|
+
"@comis/infra": "1.0.10",
|
|
121
|
+
"@comis/memory": "1.0.10",
|
|
122
|
+
"@comis/gateway": "1.0.10",
|
|
123
|
+
"@comis/skills": "1.0.10",
|
|
124
|
+
"@comis/scheduler": "1.0.10",
|
|
125
|
+
"@comis/agent": "1.0.10",
|
|
126
|
+
"@comis/channels": "1.0.10",
|
|
127
|
+
"@comis/cli": "1.0.10",
|
|
128
|
+
"@comis/daemon": "1.0.10",
|
|
129
129
|
"@agentclientprotocol/sdk": "^0.15.0",
|
|
130
130
|
"@clack/core": "^1.1.0",
|
|
131
131
|
"@clack/prompts": "^1.1.0",
|