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.
Files changed (149) hide show
  1. package/dist/cli-entry.js +0 -0
  2. package/node_modules/@comis/agent/dist/context-engine/context-engine.js +43 -2
  3. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +51 -0
  4. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +110 -0
  5. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +54 -0
  6. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +145 -0
  7. package/node_modules/@comis/agent/dist/context-engine/types-core.d.ts +17 -0
  8. package/node_modules/@comis/agent/dist/executor/error-classifier.d.ts +11 -1
  9. package/node_modules/@comis/agent/dist/executor/error-classifier.js +13 -0
  10. package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.d.ts +1 -0
  11. package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.js +55 -0
  12. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +106 -5
  13. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +1 -0
  14. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +1 -4
  15. package/node_modules/@comis/agent/dist/executor/replay-drift-detector.d.ts +85 -0
  16. package/node_modules/@comis/agent/dist/executor/replay-drift-detector.js +92 -0
  17. package/node_modules/@comis/agent/dist/executor/signature-block-scrubber.d.ts +34 -0
  18. package/node_modules/@comis/agent/dist/executor/signature-block-scrubber.js +69 -0
  19. package/node_modules/@comis/agent/dist/executor/signed-replay-detector.d.ts +39 -0
  20. package/node_modules/@comis/agent/dist/executor/signed-replay-detector.js +72 -0
  21. package/node_modules/@comis/agent/package.json +1 -1
  22. package/node_modules/@comis/channels/package.json +1 -1
  23. package/node_modules/@comis/cli/dist/cli.js +0 -0
  24. package/node_modules/@comis/cli/dist/wizard/steps/12-finish.d.ts +4 -5
  25. package/node_modules/@comis/cli/dist/wizard/steps/12-finish.js +99 -40
  26. package/node_modules/@comis/cli/package.json +1 -1
  27. package/node_modules/@comis/core/dist/config/git-manager.js +10 -4
  28. package/node_modules/@comis/core/dist/config/index.d.ts +1 -0
  29. package/node_modules/@comis/core/dist/config/index.js +2 -0
  30. package/node_modules/@comis/core/dist/config/managed-sections.d.ts +67 -0
  31. package/node_modules/@comis/core/dist/config/managed-sections.js +124 -0
  32. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +28 -10
  33. package/node_modules/@comis/core/dist/config/schema-agent.js +6 -0
  34. package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +2 -2
  35. package/node_modules/@comis/core/dist/config/schema.d.ts +65 -64
  36. package/node_modules/@comis/core/dist/event-bus/events-messaging.d.ts +16 -0
  37. package/node_modules/@comis/core/dist/exports/config.d.ts +1 -1
  38. package/node_modules/@comis/core/dist/exports/config.js +1 -1
  39. package/node_modules/@comis/core/package.json +1 -1
  40. package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/init-skill.py +0 -0
  41. package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/validate-skill.py +0 -0
  42. package/node_modules/@comis/daemon/dist/daemon.js +0 -0
  43. package/node_modules/@comis/daemon/dist/rpc/config-handlers.js +20 -7
  44. package/node_modules/@comis/daemon/dist/rpc/session-handlers.js +27 -1
  45. package/node_modules/@comis/daemon/package.json +1 -1
  46. package/node_modules/@comis/gateway/package.json +1 -1
  47. package/node_modules/@comis/infra/package.json +1 -1
  48. package/node_modules/@comis/memory/package.json +1 -1
  49. package/node_modules/@comis/scheduler/package.json +1 -1
  50. package/node_modules/@comis/shared/package.json +1 -1
  51. package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +23 -8
  52. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
  53. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +18 -14
  54. package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.js +1 -1
  55. package/node_modules/@comis/skills/package.json +1 -1
  56. package/node_modules/@comis/web/package.json +1 -1
  57. package/package.json +24 -26
  58. package/node_modules/@comis/agent/dist/provider/response/strip-minimax-xml.d.ts +0 -9
  59. package/node_modules/@comis/agent/dist/provider/response/strip-minimax-xml.js +0 -17
  60. package/node_modules/@comis/agent/dist/provider/response/strip-model-tokens.d.ts +0 -13
  61. package/node_modules/@comis/agent/dist/provider/response/strip-model-tokens.js +0 -19
  62. package/node_modules/@comis/agent/dist/provider/response/strip-tool-text.d.ts +0 -11
  63. package/node_modules/@comis/agent/dist/provider/response/strip-tool-text.js +0 -32
  64. package/node_modules/@comis/agent/dist/safety/follow-through-detector.d.ts +0 -46
  65. package/node_modules/@comis/agent/dist/safety/follow-through-detector.js +0 -76
  66. package/node_modules/@comis/agent/dist/safety/post-compaction-safety.d.ts +0 -30
  67. package/node_modules/@comis/agent/dist/safety/post-compaction-safety.js +0 -51
  68. package/node_modules/@comis/agent/dist/safety/schema-normalizer.d.ts +0 -37
  69. package/node_modules/@comis/agent/dist/safety/schema-normalizer.js +0 -137
  70. package/node_modules/@comis/agent/dist/safety/schema-pruning.d.ts +0 -50
  71. package/node_modules/@comis/agent/dist/safety/schema-pruning.js +0 -112
  72. package/node_modules/@comis/agent/dist/safety/tool-image-sanitizer.d.ts +0 -43
  73. package/node_modules/@comis/agent/dist/safety/tool-image-sanitizer.js +0 -96
  74. package/node_modules/@comis/agent/dist/safety/tool-sanitizer.d.ts +0 -44
  75. package/node_modules/@comis/agent/dist/safety/tool-sanitizer.js +0 -94
  76. package/node_modules/@comis/channels/dist/shared/thinking-tag-filter.d.ts +0 -28
  77. package/node_modules/@comis/channels/dist/shared/thinking-tag-filter.js +0 -206
  78. package/node_modules/@comis/cli/dist/wizard/config-writer.d.ts +0 -25
  79. package/node_modules/@comis/cli/dist/wizard/config-writer.js +0 -144
  80. package/node_modules/@comis/cli/dist/wizard/flow-types.d.ts +0 -48
  81. package/node_modules/@comis/cli/dist/wizard/flow-types.js +0 -70
  82. package/node_modules/@comis/cli/dist/wizard/manual-flow.d.ts +0 -21
  83. package/node_modules/@comis/cli/dist/wizard/manual-flow.js +0 -345
  84. package/node_modules/@comis/cli/dist/wizard/quickstart-flow.d.ts +0 -21
  85. package/node_modules/@comis/cli/dist/wizard/quickstart-flow.js +0 -116
  86. package/node_modules/@comis/core/dist/config/schema-agent-model.d.ts +0 -135
  87. package/node_modules/@comis/core/dist/config/schema-agent-model.js +0 -114
  88. package/node_modules/@comis/core/dist/config/schema-agent-session.d.ts +0 -177
  89. package/node_modules/@comis/core/dist/config/schema-agent-session.js +0 -116
  90. package/node_modules/@comis/core/dist/config/schema-context-engine.d.ts +0 -92
  91. package/node_modules/@comis/core/dist/config/schema-context-engine.js +0 -92
  92. package/node_modules/@comis/core/dist/config/schema-context-guard.d.ts +0 -34
  93. package/node_modules/@comis/core/dist/config/schema-context-guard.js +0 -32
  94. package/node_modules/@comis/core/dist/config/schema-delivery-mirror.d.ts +0 -27
  95. package/node_modules/@comis/core/dist/config/schema-delivery-mirror.js +0 -26
  96. package/node_modules/@comis/core/dist/config/schema-delivery-queue.d.ts +0 -31
  97. package/node_modules/@comis/core/dist/config/schema-delivery-queue.js +0 -30
  98. package/node_modules/@comis/core/dist/config/schema-delivery-timing.d.ts +0 -41
  99. package/node_modules/@comis/core/dist/config/schema-delivery-timing.js +0 -31
  100. package/node_modules/@comis/core/dist/config/schema-monitoring.d.ts +0 -105
  101. package/node_modules/@comis/core/dist/config/schema-monitoring.js +0 -67
  102. package/node_modules/@comis/core/dist/ports/media-ports.d.ts +0 -278
  103. package/node_modules/@comis/core/dist/ports/media-ports.js +0 -1
  104. package/node_modules/@comis/core/dist/security/input-guard.d.ts +0 -46
  105. package/node_modules/@comis/core/dist/security/input-guard.js +0 -166
  106. package/node_modules/@comis/core/dist/security/scoped-secret-manager.d.ts +0 -38
  107. package/node_modules/@comis/core/dist/security/scoped-secret-manager.js +0 -94
  108. package/node_modules/@comis/daemon/dist/observability/delivery-context.d.ts +0 -37
  109. package/node_modules/@comis/daemon/dist/observability/delivery-context.js +0 -1
  110. package/node_modules/@comis/daemon/dist/observability/log-level-manager.d.ts +0 -23
  111. package/node_modules/@comis/daemon/dist/observability/log-level-manager.js +0 -34
  112. package/node_modules/@comis/daemon/dist/observability/log-transport.d.ts +0 -44
  113. package/node_modules/@comis/daemon/dist/observability/log-transport.js +0 -74
  114. package/node_modules/@comis/daemon/dist/observability/obs-write-buffer.d.ts +0 -53
  115. package/node_modules/@comis/daemon/dist/observability/obs-write-buffer.js +0 -68
  116. package/node_modules/@comis/daemon/dist/observability/types.d.ts +0 -6
  117. package/node_modules/@comis/daemon/dist/observability/types.js +0 -1
  118. package/node_modules/@comis/daemon/dist/wiring/seed-bundled-skills.d.ts +0 -41
  119. package/node_modules/@comis/daemon/dist/wiring/seed-bundled-skills.js +0 -84
  120. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-mirror.d.ts +0 -24
  121. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-mirror.js +0 -88
  122. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-queue.d.ts +0 -31
  123. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-queue.js +0 -132
  124. package/node_modules/@comis/daemon/dist/wiring/setup-monitoring.d.ts +0 -38
  125. package/node_modules/@comis/daemon/dist/wiring/setup-monitoring.js +0 -100
  126. package/node_modules/@comis/daemon/dist/wiring/setup-rpc-bridge.d.ts +0 -34
  127. package/node_modules/@comis/daemon/dist/wiring/setup-rpc-bridge.js +0 -52
  128. package/node_modules/@comis/daemon/dist/wiring/setup-task-extraction.d.ts +0 -41
  129. package/node_modules/@comis/daemon/dist/wiring/setup-task-extraction.js +0 -86
  130. package/node_modules/@comis/memory/dist/embedding-cache.d.ts +0 -36
  131. package/node_modules/@comis/memory/dist/embedding-cache.js +0 -94
  132. package/node_modules/@comis/skills/dist/bridge/tool-output-schemas.d.ts +0 -17
  133. package/node_modules/@comis/skills/dist/bridge/tool-output-schemas.js +0 -125
  134. package/node_modules/@comis/skills/dist/bridge/tool-parallelism-metadata.d.ts +0 -14
  135. package/node_modules/@comis/skills/dist/bridge/tool-parallelism-metadata.js +0 -92
  136. package/node_modules/@comis/skills/dist/bridge/tool-result-caps.d.ts +0 -14
  137. package/node_modules/@comis/skills/dist/bridge/tool-result-caps.js +0 -36
  138. package/node_modules/@comis/skills/dist/bridge/tool-search-hints.d.ts +0 -15
  139. package/node_modules/@comis/skills/dist/bridge/tool-search-hints.js +0 -68
  140. package/node_modules/@comis/skills/dist/bridge/tool-validators.d.ts +0 -11
  141. package/node_modules/@comis/skills/dist/bridge/tool-validators.js +0 -105
  142. package/node_modules/@comis/skills/dist/builtin/file/find-sort-wrapper.d.ts +0 -22
  143. package/node_modules/@comis/skills/dist/builtin/file/find-sort-wrapper.js +0 -95
  144. package/node_modules/@comis/skills/dist/builtin/file/grep-output-mode-wrapper.d.ts +0 -24
  145. package/node_modules/@comis/skills/dist/builtin/file/grep-output-mode-wrapper.js +0 -167
  146. package/node_modules/@comis/skills/dist/builtin/task-plan-tool.d.ts +0 -25
  147. package/node_modules/@comis/skills/dist/builtin/task-plan-tool.js +0 -67
  148. package/node_modules/@comis/skills/dist/integrations/mcp-tool-bridge.d.ts +0 -75
  149. 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
- }