comisai 1.0.18 → 1.0.22
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/dist/cli-entry.js +0 -0
- package/node_modules/@comis/agent/dist/context-engine/context-engine.js +43 -2
- package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +51 -0
- package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +110 -0
- package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +54 -0
- package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +145 -0
- package/node_modules/@comis/agent/dist/context-engine/types-core.d.ts +17 -0
- package/node_modules/@comis/agent/dist/executor/error-classifier.d.ts +11 -1
- package/node_modules/@comis/agent/dist/executor/error-classifier.js +13 -0
- package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.d.ts +1 -0
- package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.js +55 -0
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +106 -5
- package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +1 -0
- package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +1 -4
- package/node_modules/@comis/agent/dist/executor/replay-drift-detector.d.ts +85 -0
- package/node_modules/@comis/agent/dist/executor/replay-drift-detector.js +92 -0
- package/node_modules/@comis/agent/dist/executor/signature-block-scrubber.d.ts +34 -0
- package/node_modules/@comis/agent/dist/executor/signature-block-scrubber.js +69 -0
- package/node_modules/@comis/agent/dist/executor/signed-replay-detector.d.ts +39 -0
- package/node_modules/@comis/agent/dist/executor/signed-replay-detector.js +72 -0
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/cli.js +0 -0
- package/node_modules/@comis/cli/dist/wizard/steps/12-finish.d.ts +4 -5
- package/node_modules/@comis/cli/dist/wizard/steps/12-finish.js +99 -40
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/dist/config/git-manager.js +10 -4
- package/node_modules/@comis/core/dist/config/index.d.ts +1 -0
- package/node_modules/@comis/core/dist/config/index.js +2 -0
- package/node_modules/@comis/core/dist/config/managed-sections.d.ts +67 -0
- package/node_modules/@comis/core/dist/config/managed-sections.js +124 -0
- package/node_modules/@comis/core/dist/config/schema-agent.d.ts +28 -10
- package/node_modules/@comis/core/dist/config/schema-agent.js +6 -0
- package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +2 -2
- package/node_modules/@comis/core/dist/config/schema.d.ts +65 -64
- package/node_modules/@comis/core/dist/event-bus/events-messaging.d.ts +16 -0
- package/node_modules/@comis/core/dist/exports/config.d.ts +1 -1
- package/node_modules/@comis/core/dist/exports/config.js +1 -1
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/init-skill.py +0 -0
- package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/validate-skill.py +0 -0
- package/node_modules/@comis/daemon/dist/daemon.js +0 -0
- package/node_modules/@comis/daemon/dist/rpc/config-handlers.js +20 -7
- package/node_modules/@comis/daemon/dist/rpc/session-handlers.js +27 -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/bridge/tool-metadata-registry.js +23 -8
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +18 -14
- package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.js +1 -1
- package/node_modules/@comis/skills/package.json +1 -1
- package/node_modules/@comis/web/package.json +1 -1
- package/package.json +24 -26
- package/node_modules/@comis/agent/dist/provider/response/strip-minimax-xml.d.ts +0 -9
- package/node_modules/@comis/agent/dist/provider/response/strip-minimax-xml.js +0 -17
- package/node_modules/@comis/agent/dist/provider/response/strip-model-tokens.d.ts +0 -13
- package/node_modules/@comis/agent/dist/provider/response/strip-model-tokens.js +0 -19
- package/node_modules/@comis/agent/dist/provider/response/strip-tool-text.d.ts +0 -11
- package/node_modules/@comis/agent/dist/provider/response/strip-tool-text.js +0 -32
- package/node_modules/@comis/agent/dist/safety/follow-through-detector.d.ts +0 -46
- package/node_modules/@comis/agent/dist/safety/follow-through-detector.js +0 -76
- package/node_modules/@comis/agent/dist/safety/post-compaction-safety.d.ts +0 -30
- package/node_modules/@comis/agent/dist/safety/post-compaction-safety.js +0 -51
- package/node_modules/@comis/agent/dist/safety/schema-normalizer.d.ts +0 -37
- package/node_modules/@comis/agent/dist/safety/schema-normalizer.js +0 -137
- package/node_modules/@comis/agent/dist/safety/schema-pruning.d.ts +0 -50
- package/node_modules/@comis/agent/dist/safety/schema-pruning.js +0 -112
- package/node_modules/@comis/agent/dist/safety/tool-image-sanitizer.d.ts +0 -43
- package/node_modules/@comis/agent/dist/safety/tool-image-sanitizer.js +0 -96
- package/node_modules/@comis/agent/dist/safety/tool-sanitizer.d.ts +0 -44
- package/node_modules/@comis/agent/dist/safety/tool-sanitizer.js +0 -94
- package/node_modules/@comis/channels/dist/shared/thinking-tag-filter.d.ts +0 -28
- package/node_modules/@comis/channels/dist/shared/thinking-tag-filter.js +0 -206
- package/node_modules/@comis/cli/dist/wizard/config-writer.d.ts +0 -25
- package/node_modules/@comis/cli/dist/wizard/config-writer.js +0 -144
- package/node_modules/@comis/cli/dist/wizard/flow-types.d.ts +0 -48
- package/node_modules/@comis/cli/dist/wizard/flow-types.js +0 -70
- package/node_modules/@comis/cli/dist/wizard/manual-flow.d.ts +0 -21
- package/node_modules/@comis/cli/dist/wizard/manual-flow.js +0 -345
- package/node_modules/@comis/cli/dist/wizard/quickstart-flow.d.ts +0 -21
- package/node_modules/@comis/cli/dist/wizard/quickstart-flow.js +0 -116
- package/node_modules/@comis/core/dist/config/schema-agent-model.d.ts +0 -135
- package/node_modules/@comis/core/dist/config/schema-agent-model.js +0 -114
- package/node_modules/@comis/core/dist/config/schema-agent-session.d.ts +0 -177
- package/node_modules/@comis/core/dist/config/schema-agent-session.js +0 -116
- package/node_modules/@comis/core/dist/config/schema-context-engine.d.ts +0 -92
- package/node_modules/@comis/core/dist/config/schema-context-engine.js +0 -92
- package/node_modules/@comis/core/dist/config/schema-context-guard.d.ts +0 -34
- package/node_modules/@comis/core/dist/config/schema-context-guard.js +0 -32
- package/node_modules/@comis/core/dist/config/schema-delivery-mirror.d.ts +0 -27
- package/node_modules/@comis/core/dist/config/schema-delivery-mirror.js +0 -26
- package/node_modules/@comis/core/dist/config/schema-delivery-queue.d.ts +0 -31
- package/node_modules/@comis/core/dist/config/schema-delivery-queue.js +0 -30
- package/node_modules/@comis/core/dist/config/schema-delivery-timing.d.ts +0 -41
- package/node_modules/@comis/core/dist/config/schema-delivery-timing.js +0 -31
- package/node_modules/@comis/core/dist/config/schema-monitoring.d.ts +0 -105
- package/node_modules/@comis/core/dist/config/schema-monitoring.js +0 -67
- package/node_modules/@comis/core/dist/ports/media-ports.d.ts +0 -278
- package/node_modules/@comis/core/dist/ports/media-ports.js +0 -1
- package/node_modules/@comis/core/dist/security/input-guard.d.ts +0 -46
- package/node_modules/@comis/core/dist/security/input-guard.js +0 -166
- package/node_modules/@comis/core/dist/security/scoped-secret-manager.d.ts +0 -38
- package/node_modules/@comis/core/dist/security/scoped-secret-manager.js +0 -94
- package/node_modules/@comis/daemon/dist/observability/delivery-context.d.ts +0 -37
- package/node_modules/@comis/daemon/dist/observability/delivery-context.js +0 -1
- package/node_modules/@comis/daemon/dist/observability/log-level-manager.d.ts +0 -23
- package/node_modules/@comis/daemon/dist/observability/log-level-manager.js +0 -34
- package/node_modules/@comis/daemon/dist/observability/log-transport.d.ts +0 -44
- package/node_modules/@comis/daemon/dist/observability/log-transport.js +0 -74
- package/node_modules/@comis/daemon/dist/observability/obs-write-buffer.d.ts +0 -53
- package/node_modules/@comis/daemon/dist/observability/obs-write-buffer.js +0 -68
- package/node_modules/@comis/daemon/dist/observability/types.d.ts +0 -6
- package/node_modules/@comis/daemon/dist/observability/types.js +0 -1
- package/node_modules/@comis/daemon/dist/wiring/seed-bundled-skills.d.ts +0 -41
- package/node_modules/@comis/daemon/dist/wiring/seed-bundled-skills.js +0 -84
- package/node_modules/@comis/daemon/dist/wiring/setup-delivery-mirror.d.ts +0 -24
- package/node_modules/@comis/daemon/dist/wiring/setup-delivery-mirror.js +0 -88
- package/node_modules/@comis/daemon/dist/wiring/setup-delivery-queue.d.ts +0 -31
- package/node_modules/@comis/daemon/dist/wiring/setup-delivery-queue.js +0 -132
- package/node_modules/@comis/daemon/dist/wiring/setup-monitoring.d.ts +0 -38
- package/node_modules/@comis/daemon/dist/wiring/setup-monitoring.js +0 -100
- package/node_modules/@comis/daemon/dist/wiring/setup-rpc-bridge.d.ts +0 -34
- package/node_modules/@comis/daemon/dist/wiring/setup-rpc-bridge.js +0 -52
- package/node_modules/@comis/daemon/dist/wiring/setup-task-extraction.d.ts +0 -41
- package/node_modules/@comis/daemon/dist/wiring/setup-task-extraction.js +0 -86
- package/node_modules/@comis/memory/dist/embedding-cache.d.ts +0 -36
- package/node_modules/@comis/memory/dist/embedding-cache.js +0 -94
- package/node_modules/@comis/skills/dist/bridge/tool-output-schemas.d.ts +0 -17
- package/node_modules/@comis/skills/dist/bridge/tool-output-schemas.js +0 -125
- package/node_modules/@comis/skills/dist/bridge/tool-parallelism-metadata.d.ts +0 -14
- package/node_modules/@comis/skills/dist/bridge/tool-parallelism-metadata.js +0 -92
- package/node_modules/@comis/skills/dist/bridge/tool-result-caps.d.ts +0 -14
- package/node_modules/@comis/skills/dist/bridge/tool-result-caps.js +0 -36
- package/node_modules/@comis/skills/dist/bridge/tool-search-hints.d.ts +0 -15
- package/node_modules/@comis/skills/dist/bridge/tool-search-hints.js +0 -68
- package/node_modules/@comis/skills/dist/bridge/tool-validators.d.ts +0 -11
- package/node_modules/@comis/skills/dist/bridge/tool-validators.js +0 -105
- package/node_modules/@comis/skills/dist/builtin/file/find-sort-wrapper.d.ts +0 -22
- package/node_modules/@comis/skills/dist/builtin/file/find-sort-wrapper.js +0 -95
- package/node_modules/@comis/skills/dist/builtin/file/grep-output-mode-wrapper.d.ts +0 -24
- package/node_modules/@comis/skills/dist/builtin/file/grep-output-mode-wrapper.js +0 -167
- package/node_modules/@comis/skills/dist/builtin/task-plan-tool.d.ts +0 -25
- package/node_modules/@comis/skills/dist/builtin/task-plan-tool.js +0 -67
- package/node_modules/@comis/skills/dist/integrations/mcp-tool-bridge.d.ts +0 -75
- package/node_modules/@comis/skills/dist/integrations/mcp-tool-bridge.js +0 -235
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool image sanitizer -- validates, resizes, and re-encodes images
|
|
3
|
-
* from tool results before they enter LLM context.
|
|
4
|
-
*
|
|
5
|
-
* Prevents memory spikes from oversized images and rejects corrupt
|
|
6
|
-
* or unsupported image data. Uses sharp for processing with
|
|
7
|
-
* decompression bomb protection via limitInputPixels.
|
|
8
|
-
*/
|
|
9
|
-
import sharp from "sharp";
|
|
10
|
-
import { ok, err } from "@comis/shared";
|
|
11
|
-
// Disable sharp cache to prevent memory accumulation across calls
|
|
12
|
-
sharp.cache(false);
|
|
13
|
-
/** Default limit for decompression bomb protection (268 million pixels). */
|
|
14
|
-
const DEFAULT_LIMIT_INPUT_PIXELS = 268_402_689;
|
|
15
|
-
/**
|
|
16
|
-
* Create a tool image sanitizer instance.
|
|
17
|
-
*
|
|
18
|
-
* @param opts - Optional configuration overriding defaults.
|
|
19
|
-
* @returns A ToolImageSanitizer instance.
|
|
20
|
-
*/
|
|
21
|
-
export function createToolImageSanitizer(opts) {
|
|
22
|
-
const maxWidth = opts?.maxWidth ?? 1024;
|
|
23
|
-
const maxHeight = opts?.maxHeight ?? 1024;
|
|
24
|
-
const maxInputBytes = opts?.maxInputBytes ?? 10_485_760;
|
|
25
|
-
const outputFormat = opts?.outputFormat ?? "png";
|
|
26
|
-
const quality = opts?.quality ?? 85;
|
|
27
|
-
return {
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
29
|
-
async sanitize(base64Data, _mimeType) {
|
|
30
|
-
// Reject empty input
|
|
31
|
-
if (!base64Data || base64Data.length === 0) {
|
|
32
|
-
return err("Empty image data provided");
|
|
33
|
-
}
|
|
34
|
-
// Decode base64 to buffer
|
|
35
|
-
let inputBuffer;
|
|
36
|
-
try {
|
|
37
|
-
inputBuffer = Buffer.from(base64Data, "base64");
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return err("Failed to decode base64 image data");
|
|
41
|
-
}
|
|
42
|
-
// Reject if decoded buffer is empty (empty base64 decodes to empty buffer)
|
|
43
|
-
if (inputBuffer.length === 0) {
|
|
44
|
-
return err("Empty image data after base64 decode");
|
|
45
|
-
}
|
|
46
|
-
// Check size against maxInputBytes
|
|
47
|
-
if (inputBuffer.length > maxInputBytes) {
|
|
48
|
-
return err(`Image size ${inputBuffer.length} bytes exceeds maximum allowed ${maxInputBytes} bytes`);
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
// Create sharp instance with decompression bomb protection
|
|
52
|
-
const image = sharp(inputBuffer, {
|
|
53
|
-
limitInputPixels: DEFAULT_LIMIT_INPUT_PIXELS,
|
|
54
|
-
});
|
|
55
|
-
// Read metadata to determine if resize is needed
|
|
56
|
-
const metadata = await image.metadata();
|
|
57
|
-
if (!metadata.width || !metadata.height) {
|
|
58
|
-
return err("Unable to read image dimensions -- possibly corrupt or unsupported format");
|
|
59
|
-
}
|
|
60
|
-
// Resize if exceeds limits (fit: inside preserves aspect ratio)
|
|
61
|
-
const needsResize = metadata.width > maxWidth || metadata.height > maxHeight;
|
|
62
|
-
if (needsResize) {
|
|
63
|
-
image.resize(maxWidth, maxHeight, { fit: "inside" });
|
|
64
|
-
}
|
|
65
|
-
// Convert to output format
|
|
66
|
-
let outputBuffer;
|
|
67
|
-
switch (outputFormat) {
|
|
68
|
-
case "jpeg":
|
|
69
|
-
outputBuffer = await image.jpeg({ quality }).toBuffer();
|
|
70
|
-
break;
|
|
71
|
-
case "webp":
|
|
72
|
-
outputBuffer = await image.webp({ quality }).toBuffer();
|
|
73
|
-
break;
|
|
74
|
-
case "png":
|
|
75
|
-
default:
|
|
76
|
-
outputBuffer = await image.png().toBuffer();
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
// Read final metadata from the output
|
|
80
|
-
const outputMeta = await sharp(outputBuffer).metadata();
|
|
81
|
-
return ok({
|
|
82
|
-
buffer: outputBuffer,
|
|
83
|
-
format: outputFormat,
|
|
84
|
-
width: outputMeta.width ?? (needsResize ? maxWidth : metadata.width),
|
|
85
|
-
height: outputMeta.height ?? (needsResize ? maxHeight : metadata.height),
|
|
86
|
-
originalBytes: inputBuffer.length,
|
|
87
|
-
sanitizedBytes: outputBuffer.length,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
catch (e) {
|
|
91
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
92
|
-
return err(`Image processing failed: ${message}`);
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool output sanitizer -- strips indirect prompt injection patterns.
|
|
3
|
-
*
|
|
4
|
-
* Detects instruction-like text in tool outputs that could be used
|
|
5
|
-
* to hijack the agent's behavior via indirect prompt injection.
|
|
6
|
-
* Replaces detected patterns with "[REDACTED]" and truncates
|
|
7
|
-
* oversized output at newline boundaries.
|
|
8
|
-
*/
|
|
9
|
-
/**
|
|
10
|
-
* Normalize text for secure pattern matching.
|
|
11
|
-
* 1. Apply Unicode NFKC normalization (compatibility decomposition + canonical composition)
|
|
12
|
-
* 2. Strip zero-width and invisible formatting characters (including tag block bypass)
|
|
13
|
-
*
|
|
14
|
-
* IMPORTANT: Always normalize FIRST, then do pattern matching on the normalized string.
|
|
15
|
-
* NFKC can change string length (e.g., fullwidth A -> A, ligatures decompose).
|
|
16
|
-
*/
|
|
17
|
-
export declare function normalizeForMatching(text: string): string;
|
|
18
|
-
/**
|
|
19
|
-
* Regex patterns that indicate potential prompt injection attempts.
|
|
20
|
-
*
|
|
21
|
-
* Imported from @comis/core injection-patterns.ts (single source of truth).
|
|
22
|
-
* Each pattern uses the `gi` flag for case-insensitive global matching.
|
|
23
|
-
*
|
|
24
|
-
* Note: The `system\s*:\s+` pattern requires whitespace after colon
|
|
25
|
-
* to avoid matching URLs like `https://system.example.com:8080`
|
|
26
|
-
* or code like `process.env.system`.
|
|
27
|
-
*/
|
|
28
|
-
export declare const INSTRUCTION_PATTERNS: readonly RegExp[];
|
|
29
|
-
/**
|
|
30
|
-
* Sanitize tool output against indirect prompt injection.
|
|
31
|
-
*
|
|
32
|
-
* 1. Checks for Unicode tag block bypass characters (for caller-side INFO logging)
|
|
33
|
-
* 2. Replaces instruction-like injection patterns with "[REDACTED]"
|
|
34
|
-
* 3. Truncates output exceeding `maxChars` at the last newline before
|
|
35
|
-
* the 95% mark, appending a truncation notice
|
|
36
|
-
*
|
|
37
|
-
* @param text - Raw tool output text
|
|
38
|
-
* @param maxChars - Maximum allowed characters (default: 50,000)
|
|
39
|
-
* @param options - Optional callbacks for tag block detection logging
|
|
40
|
-
* @returns Sanitized text with injections redacted and size enforced
|
|
41
|
-
*/
|
|
42
|
-
export declare function sanitizeToolOutput(text: string, maxChars?: number, options?: {
|
|
43
|
-
onTagBlockDetected?: () => void;
|
|
44
|
-
}): string;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool output sanitizer -- strips indirect prompt injection patterns.
|
|
3
|
-
*
|
|
4
|
-
* Detects instruction-like text in tool outputs that could be used
|
|
5
|
-
* to hijack the agent's behavior via indirect prompt injection.
|
|
6
|
-
* Replaces detected patterns with "[REDACTED]" and truncates
|
|
7
|
-
* oversized output at newline boundaries.
|
|
8
|
-
*/
|
|
9
|
-
import { stripInvisible, containsTagBlockChars, IGNORE_PREV_INSTRUCTIONS, YOU_ARE_NOW, FORGET_EVERYTHING, NEW_INSTRUCTIONS, SYSTEM_COLON, SYSTEM_BRACKET, INST_BRACKET, SYSTEM_TAG, IMPORTANT_OVERRIDE, DISREGARD_INSTRUCTIONS, ACT_AS_ROLE, ASSISTANT_ROLE_MARKER, SPECIAL_TOKEN_DELIMITERS, CONTEXT_RESET, RULE_REPLACEMENT, OVERRIDE_SAFETY, } from "@comis/core";
|
|
10
|
-
/**
|
|
11
|
-
* Normalize text for secure pattern matching.
|
|
12
|
-
* 1. Apply Unicode NFKC normalization (compatibility decomposition + canonical composition)
|
|
13
|
-
* 2. Strip zero-width and invisible formatting characters (including tag block bypass)
|
|
14
|
-
*
|
|
15
|
-
* IMPORTANT: Always normalize FIRST, then do pattern matching on the normalized string.
|
|
16
|
-
* NFKC can change string length (e.g., fullwidth A -> A, ligatures decompose).
|
|
17
|
-
*/
|
|
18
|
-
export function normalizeForMatching(text) {
|
|
19
|
-
return stripInvisible(text.normalize("NFKC")).text;
|
|
20
|
-
}
|
|
21
|
-
/** Default maximum characters for tool output */
|
|
22
|
-
const DEFAULT_MAX_CHARS = 50_000;
|
|
23
|
-
/** Truncation message appended when output is cut */
|
|
24
|
-
const TRUNCATION_MSG = "\n[Content truncated -- exceeded size limit]";
|
|
25
|
-
/**
|
|
26
|
-
* Regex patterns that indicate potential prompt injection attempts.
|
|
27
|
-
*
|
|
28
|
-
* Imported from @comis/core injection-patterns.ts (single source of truth).
|
|
29
|
-
* Each pattern uses the `gi` flag for case-insensitive global matching.
|
|
30
|
-
*
|
|
31
|
-
* Note: The `system\s*:\s+` pattern requires whitespace after colon
|
|
32
|
-
* to avoid matching URLs like `https://system.example.com:8080`
|
|
33
|
-
* or code like `process.env.system`.
|
|
34
|
-
*/
|
|
35
|
-
export const INSTRUCTION_PATTERNS = [
|
|
36
|
-
IGNORE_PREV_INSTRUCTIONS, // /ignore\s+(all\s+)?previous\s+instructions/gi
|
|
37
|
-
YOU_ARE_NOW, // /you\s+are\s+now\s+/gi
|
|
38
|
-
FORGET_EVERYTHING, // /forget\s+(everything|all|your)\s/gi
|
|
39
|
-
NEW_INSTRUCTIONS, // /new\s+instructions?\s*:/gi
|
|
40
|
-
SYSTEM_COLON, // /system\s*:\s+/gi
|
|
41
|
-
SYSTEM_BRACKET, // /\[SYSTEM\]/gi
|
|
42
|
-
INST_BRACKET, // /\[INST\]/gi
|
|
43
|
-
SYSTEM_TAG, // /<\/?system>/gi
|
|
44
|
-
IMPORTANT_OVERRIDE, // /IMPORTANT\s*:\s*override/gi
|
|
45
|
-
DISREGARD_INSTRUCTIONS, // /disregard ... instructions/
|
|
46
|
-
ACT_AS_ROLE, // /act as root|admin|.../
|
|
47
|
-
ASSISTANT_ROLE_MARKER, // /assistant:|user:/
|
|
48
|
-
SPECIAL_TOKEN_DELIMITERS, // /<|...|>/
|
|
49
|
-
CONTEXT_RESET, // /context reset|cleared|.../
|
|
50
|
-
RULE_REPLACEMENT, // /new rules:|updated guidelines:/
|
|
51
|
-
OVERRIDE_SAFETY, // /override safety|bypass security/
|
|
52
|
-
];
|
|
53
|
-
/**
|
|
54
|
-
* Sanitize tool output against indirect prompt injection.
|
|
55
|
-
*
|
|
56
|
-
* 1. Checks for Unicode tag block bypass characters (for caller-side INFO logging)
|
|
57
|
-
* 2. Replaces instruction-like injection patterns with "[REDACTED]"
|
|
58
|
-
* 3. Truncates output exceeding `maxChars` at the last newline before
|
|
59
|
-
* the 95% mark, appending a truncation notice
|
|
60
|
-
*
|
|
61
|
-
* @param text - Raw tool output text
|
|
62
|
-
* @param maxChars - Maximum allowed characters (default: 50,000)
|
|
63
|
-
* @param options - Optional callbacks for tag block detection logging
|
|
64
|
-
* @returns Sanitized text with injections redacted and size enforced
|
|
65
|
-
*/
|
|
66
|
-
export function sanitizeToolOutput(text, maxChars = DEFAULT_MAX_CHARS, options) {
|
|
67
|
-
if (text.length === 0)
|
|
68
|
-
return text;
|
|
69
|
-
// Check for tag block bypass BEFORE normalization strips them
|
|
70
|
-
if (containsTagBlockChars(text)) {
|
|
71
|
-
options?.onTagBlockDetected?.();
|
|
72
|
-
}
|
|
73
|
-
// Phase 1: Normalize for pattern matching (NFKC + strip zero-width + tag block)
|
|
74
|
-
let sanitized = normalizeForMatching(text);
|
|
75
|
-
// Phase 2: Redact injection patterns (on normalized text)
|
|
76
|
-
for (const pattern of INSTRUCTION_PATTERNS) {
|
|
77
|
-
// Reset lastIndex for sticky/global regexes across multiple calls
|
|
78
|
-
pattern.lastIndex = 0;
|
|
79
|
-
sanitized = sanitized.replace(pattern, "[REDACTED]");
|
|
80
|
-
}
|
|
81
|
-
// Phase 3: Truncate oversized output
|
|
82
|
-
if (sanitized.length > maxChars) {
|
|
83
|
-
const cutPoint = Math.floor(maxChars * 0.95);
|
|
84
|
-
const lastNewline = sanitized.lastIndexOf("\n", cutPoint);
|
|
85
|
-
if (lastNewline > 0) {
|
|
86
|
-
sanitized = sanitized.slice(0, lastNewline + 1) + TRUNCATION_MSG;
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// No newline found -- hard cut at 95%
|
|
90
|
-
sanitized = sanitized.slice(0, cutPoint) + TRUNCATION_MSG;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return sanitized;
|
|
94
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thinking tag filter -- strips <think>, <thinking>, and <final> blocks
|
|
3
|
-
* from streaming deltas using a character-level state machine.
|
|
4
|
-
*
|
|
5
|
-
* Handles tags split across chunk boundaries by buffering partial
|
|
6
|
-
* tag sequences and resolving them when more data arrives.
|
|
7
|
-
*
|
|
8
|
-
* @module
|
|
9
|
-
*/
|
|
10
|
-
/** Streaming thinking tag filter interface. */
|
|
11
|
-
export interface ThinkingTagFilter {
|
|
12
|
-
/** Process a streaming delta, returning only visible text. */
|
|
13
|
-
feed(delta: string): string;
|
|
14
|
-
/** Flush any buffered partial tag text (call at stream end). */
|
|
15
|
-
flush(): string;
|
|
16
|
-
/** Reset state for a new message. */
|
|
17
|
-
reset(): void;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Create a thinking tag filter that strips thinking blocks from streaming deltas.
|
|
21
|
-
*
|
|
22
|
-
* The filter processes characters one at a time through a state machine:
|
|
23
|
-
* - `passthrough`: Normal text flows through. On `<`, transition to `buffering`.
|
|
24
|
-
* - `buffering`: Accumulating chars after `<` to identify an opening or closing tag.
|
|
25
|
-
* - `inside_block`: All text is suppressed. On `<`, transition to `close_buffering`.
|
|
26
|
-
* - `close_buffering`: Inside a block, checking for the matching closing tag.
|
|
27
|
-
*/
|
|
28
|
-
export declare function createThinkingTagFilter(): ThinkingTagFilter;
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thinking tag filter -- strips <think>, <thinking>, and <final> blocks
|
|
3
|
-
* from streaming deltas using a character-level state machine.
|
|
4
|
-
*
|
|
5
|
-
* Handles tags split across chunk boundaries by buffering partial
|
|
6
|
-
* tag sequences and resolving them when more data arrives.
|
|
7
|
-
*
|
|
8
|
-
* @module
|
|
9
|
-
*/
|
|
10
|
-
/** Recognized thinking tag names (lowercased).
|
|
11
|
-
* Note: "final" is NOT included — <final> wraps the actual answer content
|
|
12
|
-
* and should be unwrapped (tags stripped, content kept), not suppressed.
|
|
13
|
-
* See sanitizeAssistantResponse() in @comis/agent sanitize-pipeline.ts. */
|
|
14
|
-
const THINKING_TAGS = new Set(["think", "thinking"]);
|
|
15
|
-
/** Maximum buffer length before we give up matching a tag. */
|
|
16
|
-
const MAX_BUFFER = 24;
|
|
17
|
-
/**
|
|
18
|
-
* Create a thinking tag filter that strips thinking blocks from streaming deltas.
|
|
19
|
-
*
|
|
20
|
-
* The filter processes characters one at a time through a state machine:
|
|
21
|
-
* - `passthrough`: Normal text flows through. On `<`, transition to `buffering`.
|
|
22
|
-
* - `buffering`: Accumulating chars after `<` to identify an opening or closing tag.
|
|
23
|
-
* - `inside_block`: All text is suppressed. On `<`, transition to `close_buffering`.
|
|
24
|
-
* - `close_buffering`: Inside a block, checking for the matching closing tag.
|
|
25
|
-
*/
|
|
26
|
-
export function createThinkingTagFilter() {
|
|
27
|
-
let state = "passthrough";
|
|
28
|
-
let buffer = "";
|
|
29
|
-
let activeTag = ""; // The tag name that opened the current block (lowercased)
|
|
30
|
-
/**
|
|
31
|
-
* Try to resolve the buffer as a tag.
|
|
32
|
-
*
|
|
33
|
-
* Returns:
|
|
34
|
-
* - `{ type: "open", tagName }` if buffer is a complete opening tag like `<think>`
|
|
35
|
-
* - `{ type: "close", tagName }` if buffer is a complete closing tag like `</think>`
|
|
36
|
-
* - `{ type: "not_tag" }` if buffer cannot be a thinking tag (flush as text)
|
|
37
|
-
* - `{ type: "partial" }` if buffer could still become a thinking tag (keep buffering)
|
|
38
|
-
*/
|
|
39
|
-
function classifyBuffer(buf) {
|
|
40
|
-
// Buffer must start with <
|
|
41
|
-
if (!buf.startsWith("<"))
|
|
42
|
-
return { type: "not_tag" };
|
|
43
|
-
const lower = buf.toLowerCase();
|
|
44
|
-
// Check for closing tag pattern: </tagname>
|
|
45
|
-
if (lower.length >= 2 && lower[1] === "/") {
|
|
46
|
-
const rest = lower.slice(2);
|
|
47
|
-
// Check if it ends with >
|
|
48
|
-
if (rest.endsWith(">")) {
|
|
49
|
-
const tagName = rest.slice(0, -1);
|
|
50
|
-
if (THINKING_TAGS.has(tagName)) {
|
|
51
|
-
return { type: "close", tagName };
|
|
52
|
-
}
|
|
53
|
-
return { type: "not_tag" };
|
|
54
|
-
}
|
|
55
|
-
// Still could be partial: check if any thinking tag starts with what we have
|
|
56
|
-
for (const tag of THINKING_TAGS) {
|
|
57
|
-
if (tag.startsWith(rest) || rest.startsWith(tag)) {
|
|
58
|
-
return { type: "partial" };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return { type: "not_tag" };
|
|
62
|
-
}
|
|
63
|
-
// Check for opening tag pattern: <tagname> or <tagname/>
|
|
64
|
-
const rest = lower.slice(1);
|
|
65
|
-
// If it ends with ">", check the tag name
|
|
66
|
-
if (rest.endsWith(">")) {
|
|
67
|
-
// Could be <tagname> or <tagname/>
|
|
68
|
-
let tagName = rest.slice(0, -1);
|
|
69
|
-
if (tagName.endsWith("/")) {
|
|
70
|
-
tagName = tagName.slice(0, -1);
|
|
71
|
-
}
|
|
72
|
-
if (THINKING_TAGS.has(tagName)) {
|
|
73
|
-
return { type: "open", tagName };
|
|
74
|
-
}
|
|
75
|
-
return { type: "not_tag" };
|
|
76
|
-
}
|
|
77
|
-
// Still accumulating -- check if any thinking tag could match
|
|
78
|
-
for (const tag of THINKING_TAGS) {
|
|
79
|
-
// rest could be a prefix of the tag name (e.g., "thi" prefix of "think")
|
|
80
|
-
if (tag.startsWith(rest)) {
|
|
81
|
-
return { type: "partial" };
|
|
82
|
-
}
|
|
83
|
-
// rest could be the tag name followed by "/" (self-closing start)
|
|
84
|
-
if (rest.startsWith(tag) && (rest.length === tag.length || rest[tag.length] === "/")) {
|
|
85
|
-
return { type: "partial" };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return { type: "not_tag" };
|
|
89
|
-
}
|
|
90
|
-
function processChar(ch, output) {
|
|
91
|
-
switch (state) {
|
|
92
|
-
case "passthrough":
|
|
93
|
-
if (ch === "<") {
|
|
94
|
-
buffer = "<";
|
|
95
|
-
state = "buffering";
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
output.push(ch);
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
case "buffering":
|
|
102
|
-
buffer += ch;
|
|
103
|
-
// Safety limit
|
|
104
|
-
if (buffer.length > MAX_BUFFER) {
|
|
105
|
-
output.push(buffer);
|
|
106
|
-
buffer = "";
|
|
107
|
-
state = "passthrough";
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
{
|
|
111
|
-
const result = classifyBuffer(buffer);
|
|
112
|
-
if (result.type === "open") {
|
|
113
|
-
// Enter inside_block, suppress everything
|
|
114
|
-
activeTag = result.tagName;
|
|
115
|
-
buffer = "";
|
|
116
|
-
state = "inside_block";
|
|
117
|
-
}
|
|
118
|
-
else if (result.type === "close") {
|
|
119
|
-
// Closing tag outside a block -- just flush it as text
|
|
120
|
-
output.push(buffer);
|
|
121
|
-
buffer = "";
|
|
122
|
-
state = "passthrough";
|
|
123
|
-
}
|
|
124
|
-
else if (result.type === "not_tag") {
|
|
125
|
-
// Not a thinking tag -- flush buffer as normal text
|
|
126
|
-
output.push(buffer);
|
|
127
|
-
buffer = "";
|
|
128
|
-
state = "passthrough";
|
|
129
|
-
}
|
|
130
|
-
// "partial" -- keep buffering
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
133
|
-
case "inside_block":
|
|
134
|
-
if (ch === "<") {
|
|
135
|
-
buffer = "<";
|
|
136
|
-
state = "close_buffering";
|
|
137
|
-
}
|
|
138
|
-
// else: suppress character
|
|
139
|
-
break;
|
|
140
|
-
case "close_buffering":
|
|
141
|
-
buffer += ch;
|
|
142
|
-
// Safety limit
|
|
143
|
-
if (buffer.length > MAX_BUFFER) {
|
|
144
|
-
// Not a closing tag -- stay inside block
|
|
145
|
-
buffer = "";
|
|
146
|
-
state = "inside_block";
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
{
|
|
150
|
-
const result = classifyBuffer(buffer);
|
|
151
|
-
if (result.type === "close" && result.tagName === activeTag) {
|
|
152
|
-
// Matching closing tag found -- exit block
|
|
153
|
-
buffer = "";
|
|
154
|
-
activeTag = "";
|
|
155
|
-
state = "passthrough";
|
|
156
|
-
}
|
|
157
|
-
else if (result.type === "close") {
|
|
158
|
-
// Non-matching closing tag -- stay inside block
|
|
159
|
-
buffer = "";
|
|
160
|
-
state = "inside_block";
|
|
161
|
-
}
|
|
162
|
-
else if (result.type === "open") {
|
|
163
|
-
// Nested opening tag inside block -- just suppress, stay in block
|
|
164
|
-
buffer = "";
|
|
165
|
-
state = "inside_block";
|
|
166
|
-
}
|
|
167
|
-
else if (result.type === "not_tag") {
|
|
168
|
-
// Not a tag at all -- suppress it, stay in block
|
|
169
|
-
buffer = "";
|
|
170
|
-
state = "inside_block";
|
|
171
|
-
}
|
|
172
|
-
// "partial" -- keep buffering
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
feed(delta) {
|
|
179
|
-
const output = [];
|
|
180
|
-
for (let i = 0; i < delta.length; i++) {
|
|
181
|
-
processChar(delta[i], output);
|
|
182
|
-
}
|
|
183
|
-
return output.join("");
|
|
184
|
-
},
|
|
185
|
-
flush() {
|
|
186
|
-
// Return any buffered text that never resolved to a thinking tag
|
|
187
|
-
if (buffer && (state === "buffering")) {
|
|
188
|
-
const result = buffer;
|
|
189
|
-
buffer = "";
|
|
190
|
-
state = "passthrough";
|
|
191
|
-
return result;
|
|
192
|
-
}
|
|
193
|
-
// If inside a block or close_buffering, the block was never closed.
|
|
194
|
-
// Discard -- thinking content should not leak to output.
|
|
195
|
-
buffer = "";
|
|
196
|
-
state = "passthrough";
|
|
197
|
-
activeTag = "";
|
|
198
|
-
return "";
|
|
199
|
-
},
|
|
200
|
-
reset() {
|
|
201
|
-
state = "passthrough";
|
|
202
|
-
buffer = "";
|
|
203
|
-
activeTag = "";
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config writer for wizard results.
|
|
3
|
-
*
|
|
4
|
-
* Converts WizardResult into a valid config.yaml and .env file
|
|
5
|
-
* using the yaml library's stringify (not hand-rolled YAML).
|
|
6
|
-
* Uses safePath from @comis/core for all file write paths.
|
|
7
|
-
*
|
|
8
|
-
* @module
|
|
9
|
-
*/
|
|
10
|
-
import { type Result } from "@comis/shared";
|
|
11
|
-
import type { WizardResult } from "./flow-types.js";
|
|
12
|
-
/**
|
|
13
|
-
* Write a config.yaml file from wizard results.
|
|
14
|
-
*
|
|
15
|
-
* Builds a config object matching AppConfigSchema shape and serializes
|
|
16
|
-
* it to YAML via the yaml library. Returns the written file path.
|
|
17
|
-
*/
|
|
18
|
-
export declare function writeWizardConfig(result: WizardResult, configDir: string): Result<string, Error>;
|
|
19
|
-
/**
|
|
20
|
-
* Write a .env file from wizard results.
|
|
21
|
-
*
|
|
22
|
-
* Sets the appropriate provider API key environment variable.
|
|
23
|
-
* File is written with mode 0o600 (owner read/write only).
|
|
24
|
-
*/
|
|
25
|
-
export declare function writeWizardEnv(result: WizardResult, configDir: string): Result<string, Error>;
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
/**
|
|
3
|
-
* Config writer for wizard results.
|
|
4
|
-
*
|
|
5
|
-
* Converts WizardResult into a valid config.yaml and .env file
|
|
6
|
-
* using the yaml library's stringify (not hand-rolled YAML).
|
|
7
|
-
* Uses safePath from @comis/core for all file write paths.
|
|
8
|
-
*
|
|
9
|
-
* @module
|
|
10
|
-
*/
|
|
11
|
-
import * as fs from "node:fs";
|
|
12
|
-
import * as os from "node:os";
|
|
13
|
-
import { stringify } from "yaml";
|
|
14
|
-
import { safePath } from "@comis/core";
|
|
15
|
-
import { ok, err } from "@comis/shared";
|
|
16
|
-
import { PROVIDER_ENV_KEYS, CHANNEL_ENV_KEYS } from "./flow-types.js";
|
|
17
|
-
/**
|
|
18
|
-
* Write a config.yaml file from wizard results.
|
|
19
|
-
*
|
|
20
|
-
* Builds a config object matching AppConfigSchema shape and serializes
|
|
21
|
-
* it to YAML via the yaml library. Returns the written file path.
|
|
22
|
-
*/
|
|
23
|
-
export function writeWizardConfig(result, configDir) {
|
|
24
|
-
try {
|
|
25
|
-
const defaultDir = os.homedir() + "/.comis";
|
|
26
|
-
const config = {
|
|
27
|
-
logLevel: "info",
|
|
28
|
-
dataDir: result.dataDir ?? defaultDir,
|
|
29
|
-
agents: {
|
|
30
|
-
default: {
|
|
31
|
-
name: result.agentName,
|
|
32
|
-
provider: result.provider,
|
|
33
|
-
model: result.model ?? getDefaultModel(result.provider),
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
// Gateway section
|
|
38
|
-
const gatewayConfig = {
|
|
39
|
-
enabled: result.gatewayEnabled ?? false,
|
|
40
|
-
};
|
|
41
|
-
if (result.gatewayEnabled) {
|
|
42
|
-
gatewayConfig.host = result.gatewayHost ?? "127.0.0.1";
|
|
43
|
-
gatewayConfig.port = result.gatewayPort ?? 3000;
|
|
44
|
-
if (result.gatewayToken) {
|
|
45
|
-
gatewayConfig.tokens = [
|
|
46
|
-
{ id: "default", secret: "${COMIS_GATEWAY_TOKEN}", scopes: ["*"] },
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
config.gateway = gatewayConfig;
|
|
51
|
-
// Channels section
|
|
52
|
-
if (result.channels && result.channels.length > 0) {
|
|
53
|
-
const channels = {};
|
|
54
|
-
for (const ch of result.channels) {
|
|
55
|
-
const entry = { enabled: true };
|
|
56
|
-
// Use ${VAR} substitution — credentials go in .env, not config.yaml
|
|
57
|
-
if (ch.type === "telegram" && ch.botToken)
|
|
58
|
-
entry.botToken = "${TELEGRAM_BOT_TOKEN}";
|
|
59
|
-
if (ch.type === "discord" && ch.botToken)
|
|
60
|
-
entry.botToken = "${DISCORD_BOT_TOKEN}";
|
|
61
|
-
if (ch.type === "slack" && ch.botToken)
|
|
62
|
-
entry.botToken = "${SLACK_BOT_TOKEN}";
|
|
63
|
-
if (ch.type === "slack" && ch.apiKey)
|
|
64
|
-
entry.signingSecret = "${SLACK_SIGNING_SECRET}";
|
|
65
|
-
if (ch.type === "whatsapp" && ch.botToken)
|
|
66
|
-
entry.accessToken = "${WHATSAPP_ACCESS_TOKEN}";
|
|
67
|
-
if (ch.type === "line" && ch.botToken)
|
|
68
|
-
entry.channelAccessToken = "${LINE_CHANNEL_ACCESS_TOKEN}";
|
|
69
|
-
if (ch.type === "line" && ch.apiKey)
|
|
70
|
-
entry.channelSecret = "${LINE_CHANNEL_SECRET}";
|
|
71
|
-
// Generic fallback for other channel types
|
|
72
|
-
if (ch.botToken && !entry.botToken && !entry.accessToken && !entry.channelAccessToken) {
|
|
73
|
-
entry.botToken = `\${${ch.type.toUpperCase()}_BOT_TOKEN}`;
|
|
74
|
-
}
|
|
75
|
-
if (ch.apiKey && !entry.signingSecret && !entry.channelSecret) {
|
|
76
|
-
entry.apiKey = `\${${ch.type.toUpperCase()}_API_KEY}`;
|
|
77
|
-
}
|
|
78
|
-
if (ch.appToken)
|
|
79
|
-
entry.appToken = `\${${ch.type.toUpperCase()}_APP_TOKEN}`;
|
|
80
|
-
channels[ch.type] = entry;
|
|
81
|
-
}
|
|
82
|
-
config.channels = channels;
|
|
83
|
-
}
|
|
84
|
-
const yaml = stringify(config, { lineWidth: 0 });
|
|
85
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
86
|
-
const configPath = safePath(configDir, "config.yaml");
|
|
87
|
-
fs.writeFileSync(configPath, yaml, "utf-8");
|
|
88
|
-
return ok(configPath);
|
|
89
|
-
}
|
|
90
|
-
catch (e) {
|
|
91
|
-
return err(e instanceof Error ? e : new Error(String(e)));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Write a .env file from wizard results.
|
|
96
|
-
*
|
|
97
|
-
* Sets the appropriate provider API key environment variable.
|
|
98
|
-
* File is written with mode 0o600 (owner read/write only).
|
|
99
|
-
*/
|
|
100
|
-
export function writeWizardEnv(result, configDir) {
|
|
101
|
-
try {
|
|
102
|
-
const lines = ["# Comis secrets -- generated by init wizard"];
|
|
103
|
-
if (result.provider && result.apiKey) {
|
|
104
|
-
const envKey = PROVIDER_ENV_KEYS[result.provider];
|
|
105
|
-
if (envKey) {
|
|
106
|
-
lines.push(`${envKey}=${result.apiKey}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
// Write channel credentials to .env
|
|
110
|
-
if (result.channels) {
|
|
111
|
-
for (const ch of result.channels) {
|
|
112
|
-
const envKeys = CHANNEL_ENV_KEYS[ch.type];
|
|
113
|
-
if (ch.botToken && envKeys?.[0])
|
|
114
|
-
lines.push(`${envKeys[0]}=${ch.botToken}`);
|
|
115
|
-
if (ch.apiKey && envKeys?.[1])
|
|
116
|
-
lines.push(`${envKeys[1]}=${ch.apiKey}`);
|
|
117
|
-
if (ch.appToken)
|
|
118
|
-
lines.push(`${ch.type.toUpperCase()}_APP_TOKEN=${ch.appToken}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
// Write gateway token to .env
|
|
122
|
-
if (result.gatewayToken) {
|
|
123
|
-
lines.push(`COMIS_GATEWAY_TOKEN=${result.gatewayToken}`);
|
|
124
|
-
}
|
|
125
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
126
|
-
const envPath = safePath(configDir, ".env");
|
|
127
|
-
fs.writeFileSync(envPath, lines.join("\n") + "\n", { mode: 0o600 });
|
|
128
|
-
return ok(envPath);
|
|
129
|
-
}
|
|
130
|
-
catch (e) {
|
|
131
|
-
return err(e instanceof Error ? e : new Error(String(e)));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/** Get a sensible default model for a provider. */
|
|
135
|
-
function getDefaultModel(provider) {
|
|
136
|
-
const defaults = {
|
|
137
|
-
anthropic: "claude-sonnet-4-5-20250929",
|
|
138
|
-
openai: "gpt-4o",
|
|
139
|
-
google: "gemini-2.0-flash",
|
|
140
|
-
groq: "llama-3.3-70b-versatile",
|
|
141
|
-
ollama: "llama3",
|
|
142
|
-
};
|
|
143
|
-
return defaults[provider] ?? "default";
|
|
144
|
-
}
|