comisai 1.0.19 → 1.0.23

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 (154) 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/pi-executor.js +30 -3
  16. package/node_modules/@comis/agent/dist/executor/replay-drift-detector.d.ts +85 -0
  17. package/node_modules/@comis/agent/dist/executor/replay-drift-detector.js +92 -0
  18. package/node_modules/@comis/agent/dist/executor/signature-block-scrubber.d.ts +34 -0
  19. package/node_modules/@comis/agent/dist/executor/signature-block-scrubber.js +69 -0
  20. package/node_modules/@comis/agent/dist/executor/signed-replay-detector.d.ts +39 -0
  21. package/node_modules/@comis/agent/dist/executor/signed-replay-detector.js +72 -0
  22. package/node_modules/@comis/agent/package.json +1 -1
  23. package/node_modules/@comis/channels/package.json +1 -1
  24. package/node_modules/@comis/cli/dist/cli.js +0 -0
  25. package/node_modules/@comis/cli/package.json +1 -1
  26. package/node_modules/@comis/core/dist/config/git-manager.js +10 -4
  27. package/node_modules/@comis/core/dist/config/index.d.ts +1 -0
  28. package/node_modules/@comis/core/dist/config/index.js +2 -0
  29. package/node_modules/@comis/core/dist/config/managed-sections.d.ts +67 -0
  30. package/node_modules/@comis/core/dist/config/managed-sections.js +124 -0
  31. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +28 -10
  32. package/node_modules/@comis/core/dist/config/schema-agent.js +6 -0
  33. package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +2 -2
  34. package/node_modules/@comis/core/dist/config/schema.d.ts +65 -64
  35. package/node_modules/@comis/core/dist/event-bus/events-messaging.d.ts +16 -0
  36. package/node_modules/@comis/core/dist/exports/config.d.ts +1 -1
  37. package/node_modules/@comis/core/dist/exports/config.js +1 -1
  38. package/node_modules/@comis/core/package.json +1 -1
  39. package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/init-skill.py +0 -0
  40. package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/validate-skill.py +0 -0
  41. package/node_modules/@comis/daemon/dist/daemon.js +11 -4
  42. package/node_modules/@comis/daemon/dist/rpc/config-handlers.js +20 -7
  43. package/node_modules/@comis/daemon/dist/rpc/session-handlers.js +27 -1
  44. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.d.ts +22 -0
  45. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +34 -8
  46. package/node_modules/@comis/daemon/dist/wiring/setup-tools.js +14 -1
  47. package/node_modules/@comis/daemon/package.json +1 -1
  48. package/node_modules/@comis/gateway/package.json +1 -1
  49. package/node_modules/@comis/infra/dist/logging/log-fields.d.ts +2 -2
  50. package/node_modules/@comis/infra/package.json +1 -1
  51. package/node_modules/@comis/memory/package.json +1 -1
  52. package/node_modules/@comis/scheduler/package.json +1 -1
  53. package/node_modules/@comis/shared/package.json +1 -1
  54. package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +23 -8
  55. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
  56. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +18 -14
  57. package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.js +1 -1
  58. package/node_modules/@comis/skills/dist/builtin/sandbox/detect-provider.d.ts +1 -0
  59. package/node_modules/@comis/skills/dist/builtin/sandbox/detect-provider.js +78 -5
  60. package/node_modules/@comis/skills/package.json +1 -1
  61. package/node_modules/@comis/web/package.json +1 -1
  62. package/package.json +24 -26
  63. package/node_modules/@comis/agent/dist/provider/response/strip-minimax-xml.d.ts +0 -9
  64. package/node_modules/@comis/agent/dist/provider/response/strip-minimax-xml.js +0 -17
  65. package/node_modules/@comis/agent/dist/provider/response/strip-model-tokens.d.ts +0 -13
  66. package/node_modules/@comis/agent/dist/provider/response/strip-model-tokens.js +0 -19
  67. package/node_modules/@comis/agent/dist/provider/response/strip-tool-text.d.ts +0 -11
  68. package/node_modules/@comis/agent/dist/provider/response/strip-tool-text.js +0 -32
  69. package/node_modules/@comis/agent/dist/safety/follow-through-detector.d.ts +0 -46
  70. package/node_modules/@comis/agent/dist/safety/follow-through-detector.js +0 -76
  71. package/node_modules/@comis/agent/dist/safety/post-compaction-safety.d.ts +0 -30
  72. package/node_modules/@comis/agent/dist/safety/post-compaction-safety.js +0 -51
  73. package/node_modules/@comis/agent/dist/safety/schema-normalizer.d.ts +0 -37
  74. package/node_modules/@comis/agent/dist/safety/schema-normalizer.js +0 -137
  75. package/node_modules/@comis/agent/dist/safety/schema-pruning.d.ts +0 -50
  76. package/node_modules/@comis/agent/dist/safety/schema-pruning.js +0 -112
  77. package/node_modules/@comis/agent/dist/safety/tool-image-sanitizer.d.ts +0 -43
  78. package/node_modules/@comis/agent/dist/safety/tool-image-sanitizer.js +0 -96
  79. package/node_modules/@comis/agent/dist/safety/tool-sanitizer.d.ts +0 -44
  80. package/node_modules/@comis/agent/dist/safety/tool-sanitizer.js +0 -94
  81. package/node_modules/@comis/channels/dist/shared/thinking-tag-filter.d.ts +0 -28
  82. package/node_modules/@comis/channels/dist/shared/thinking-tag-filter.js +0 -206
  83. package/node_modules/@comis/cli/dist/wizard/config-writer.d.ts +0 -25
  84. package/node_modules/@comis/cli/dist/wizard/config-writer.js +0 -144
  85. package/node_modules/@comis/cli/dist/wizard/flow-types.d.ts +0 -48
  86. package/node_modules/@comis/cli/dist/wizard/flow-types.js +0 -70
  87. package/node_modules/@comis/cli/dist/wizard/manual-flow.d.ts +0 -21
  88. package/node_modules/@comis/cli/dist/wizard/manual-flow.js +0 -345
  89. package/node_modules/@comis/cli/dist/wizard/quickstart-flow.d.ts +0 -21
  90. package/node_modules/@comis/cli/dist/wizard/quickstart-flow.js +0 -116
  91. package/node_modules/@comis/core/dist/config/schema-agent-model.d.ts +0 -135
  92. package/node_modules/@comis/core/dist/config/schema-agent-model.js +0 -114
  93. package/node_modules/@comis/core/dist/config/schema-agent-session.d.ts +0 -177
  94. package/node_modules/@comis/core/dist/config/schema-agent-session.js +0 -116
  95. package/node_modules/@comis/core/dist/config/schema-context-engine.d.ts +0 -92
  96. package/node_modules/@comis/core/dist/config/schema-context-engine.js +0 -92
  97. package/node_modules/@comis/core/dist/config/schema-context-guard.d.ts +0 -34
  98. package/node_modules/@comis/core/dist/config/schema-context-guard.js +0 -32
  99. package/node_modules/@comis/core/dist/config/schema-delivery-mirror.d.ts +0 -27
  100. package/node_modules/@comis/core/dist/config/schema-delivery-mirror.js +0 -26
  101. package/node_modules/@comis/core/dist/config/schema-delivery-queue.d.ts +0 -31
  102. package/node_modules/@comis/core/dist/config/schema-delivery-queue.js +0 -30
  103. package/node_modules/@comis/core/dist/config/schema-delivery-timing.d.ts +0 -41
  104. package/node_modules/@comis/core/dist/config/schema-delivery-timing.js +0 -31
  105. package/node_modules/@comis/core/dist/config/schema-monitoring.d.ts +0 -105
  106. package/node_modules/@comis/core/dist/config/schema-monitoring.js +0 -67
  107. package/node_modules/@comis/core/dist/ports/media-ports.d.ts +0 -278
  108. package/node_modules/@comis/core/dist/ports/media-ports.js +0 -1
  109. package/node_modules/@comis/core/dist/security/input-guard.d.ts +0 -46
  110. package/node_modules/@comis/core/dist/security/input-guard.js +0 -166
  111. package/node_modules/@comis/core/dist/security/scoped-secret-manager.d.ts +0 -38
  112. package/node_modules/@comis/core/dist/security/scoped-secret-manager.js +0 -94
  113. package/node_modules/@comis/daemon/dist/observability/delivery-context.d.ts +0 -37
  114. package/node_modules/@comis/daemon/dist/observability/delivery-context.js +0 -1
  115. package/node_modules/@comis/daemon/dist/observability/log-level-manager.d.ts +0 -23
  116. package/node_modules/@comis/daemon/dist/observability/log-level-manager.js +0 -34
  117. package/node_modules/@comis/daemon/dist/observability/log-transport.d.ts +0 -44
  118. package/node_modules/@comis/daemon/dist/observability/log-transport.js +0 -74
  119. package/node_modules/@comis/daemon/dist/observability/obs-write-buffer.d.ts +0 -53
  120. package/node_modules/@comis/daemon/dist/observability/obs-write-buffer.js +0 -68
  121. package/node_modules/@comis/daemon/dist/observability/types.d.ts +0 -6
  122. package/node_modules/@comis/daemon/dist/observability/types.js +0 -1
  123. package/node_modules/@comis/daemon/dist/wiring/seed-bundled-skills.d.ts +0 -41
  124. package/node_modules/@comis/daemon/dist/wiring/seed-bundled-skills.js +0 -84
  125. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-mirror.d.ts +0 -24
  126. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-mirror.js +0 -88
  127. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-queue.d.ts +0 -31
  128. package/node_modules/@comis/daemon/dist/wiring/setup-delivery-queue.js +0 -132
  129. package/node_modules/@comis/daemon/dist/wiring/setup-monitoring.d.ts +0 -38
  130. package/node_modules/@comis/daemon/dist/wiring/setup-monitoring.js +0 -100
  131. package/node_modules/@comis/daemon/dist/wiring/setup-rpc-bridge.d.ts +0 -34
  132. package/node_modules/@comis/daemon/dist/wiring/setup-rpc-bridge.js +0 -52
  133. package/node_modules/@comis/daemon/dist/wiring/setup-task-extraction.d.ts +0 -41
  134. package/node_modules/@comis/daemon/dist/wiring/setup-task-extraction.js +0 -86
  135. package/node_modules/@comis/memory/dist/embedding-cache.d.ts +0 -36
  136. package/node_modules/@comis/memory/dist/embedding-cache.js +0 -94
  137. package/node_modules/@comis/skills/dist/bridge/tool-output-schemas.d.ts +0 -17
  138. package/node_modules/@comis/skills/dist/bridge/tool-output-schemas.js +0 -125
  139. package/node_modules/@comis/skills/dist/bridge/tool-parallelism-metadata.d.ts +0 -14
  140. package/node_modules/@comis/skills/dist/bridge/tool-parallelism-metadata.js +0 -92
  141. package/node_modules/@comis/skills/dist/bridge/tool-result-caps.d.ts +0 -14
  142. package/node_modules/@comis/skills/dist/bridge/tool-result-caps.js +0 -36
  143. package/node_modules/@comis/skills/dist/bridge/tool-search-hints.d.ts +0 -15
  144. package/node_modules/@comis/skills/dist/bridge/tool-search-hints.js +0 -68
  145. package/node_modules/@comis/skills/dist/bridge/tool-validators.d.ts +0 -11
  146. package/node_modules/@comis/skills/dist/bridge/tool-validators.js +0 -105
  147. package/node_modules/@comis/skills/dist/builtin/file/find-sort-wrapper.d.ts +0 -22
  148. package/node_modules/@comis/skills/dist/builtin/file/find-sort-wrapper.js +0 -95
  149. package/node_modules/@comis/skills/dist/builtin/file/grep-output-mode-wrapper.d.ts +0 -24
  150. package/node_modules/@comis/skills/dist/builtin/file/grep-output-mode-wrapper.js +0 -167
  151. package/node_modules/@comis/skills/dist/builtin/task-plan-tool.d.ts +0 -25
  152. package/node_modules/@comis/skills/dist/builtin/task-plan-tool.js +0 -67
  153. package/node_modules/@comis/skills/dist/integrations/mcp-tool-bridge.d.ts +0 -75
  154. package/node_modules/@comis/skills/dist/integrations/mcp-tool-bridge.js +0 -235
@@ -1,68 +0,0 @@
1
- /**
2
- * Generic batched write buffer for observability persistence.
3
- *
4
- * Sits between event bus listeners and ObservabilityStore: listeners push items
5
- * into the buffer; the buffer flushes batches to SQLite periodically or when
6
- * the buffer reaches its size threshold.
7
- *
8
- * Design:
9
- * - Plain array (not ring buffer) per research recommendation
10
- * - Dual trigger: size threshold (default 50) OR timer interval (default 500ms)
11
- * - Timer uses unref() so it does not prevent Node.js process exit
12
- * - drain() provides synchronous final flush for clean shutdown
13
- * - flushFn is synchronous (better-sqlite3 db.transaction() is sync)
14
- *
15
- * @module obs-write-buffer
16
- */
17
- /**
18
- * Create a generic batched write buffer.
19
- *
20
- * Usage:
21
- * ```ts
22
- * const buf = createObsWriteBuffer<TokenUsageRow>({
23
- * flushFn: (items) => store.insertTokenUsageBatch(items),
24
- * maxSize: 50,
25
- * intervalMs: 500,
26
- * });
27
- *
28
- * eventBus.on("token:usage", (entry) => buf.push(toRow(entry)));
29
- * // On shutdown:
30
- * buf.drain();
31
- * ```
32
- */
33
- export function createObsWriteBuffer(opts) {
34
- const { flushFn, maxSize = 50, intervalMs = 500 } = opts;
35
- let buffer = [];
36
- // Start periodic flush timer. unref() ensures it does not keep the process alive.
37
- const timer = setInterval(() => {
38
- flush();
39
- }, intervalMs);
40
- timer.unref();
41
- function flush() {
42
- if (buffer.length === 0)
43
- return;
44
- // Swap-before-call: if flushFn throws, the batch is in the local variable,
45
- // not lost back in the buffer. The caller of flushFn can decide recovery.
46
- const batch = buffer;
47
- buffer = [];
48
- flushFn(batch);
49
- }
50
- function push(item) {
51
- buffer.push(item);
52
- if (buffer.length >= maxSize) {
53
- flush();
54
- }
55
- }
56
- function drain() {
57
- clearInterval(timer);
58
- flush();
59
- }
60
- return {
61
- push,
62
- flush,
63
- drain,
64
- get pending() {
65
- return buffer.length;
66
- },
67
- };
68
- }
@@ -1,6 +0,0 @@
1
- import type { EventMap, EventHandler } from "@comis/core";
2
- /** Reference to a registered EventBus handler for cleanup. */
3
- export interface HandlerRef {
4
- event: keyof EventMap;
5
- handler: EventHandler<keyof EventMap>;
6
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Idempotent seeding of bundled skills from the repo's skills/ directory
3
- * into the user data directory (~/.comis/skills/).
4
- *
5
- * On first startup, copies each skill directory that does not already exist
6
- * in the target. Existing user skills are never overwritten.
7
- *
8
- * @module
9
- */
10
- /** Minimal Pino-compatible logger for seeding diagnostics. */
11
- interface SeedLogger {
12
- info(obj: Record<string, unknown>, msg: string): void;
13
- debug(obj: Record<string, unknown>, msg: string): void;
14
- }
15
- /** Result of a seeding operation. */
16
- export interface SeedResult {
17
- /** Skill directory names that were newly copied into targetDir. */
18
- seeded: string[];
19
- /** Skill directory names that already existed in targetDir and were skipped. */
20
- skipped: string[];
21
- }
22
- /**
23
- * Seed bundled skills from sourceDir into targetDir (idempotent).
24
- *
25
- * For each subdirectory in sourceDir, checks whether a same-named directory
26
- * already exists in targetDir. If not, copies the entire directory tree
27
- * recursively. Existing skill directories are never overwritten.
28
- *
29
- * @param opts.sourceDir - Absolute path to the repo's skills/ directory.
30
- * If omitted, resolves from __dirname (../../../../skills).
31
- * @param opts.targetDir - Absolute path to the user data skills directory
32
- * (e.g., ~/.comis/skills/).
33
- * @param opts.logger - Optional structured logger for diagnostics.
34
- * @returns SeedResult with seeded and skipped skill names.
35
- */
36
- export declare function seedBundledSkills(opts: {
37
- sourceDir?: string;
38
- targetDir: string;
39
- logger?: SeedLogger;
40
- }): SeedResult;
41
- export {};
@@ -1,84 +0,0 @@
1
- /**
2
- * Idempotent seeding of bundled skills from the repo's skills/ directory
3
- * into the user data directory (~/.comis/skills/).
4
- *
5
- * On first startup, copies each skill directory that does not already exist
6
- * in the target. Existing user skills are never overwritten.
7
- *
8
- * @module
9
- */
10
- import { existsSync, mkdirSync, readdirSync, cpSync, statSync } from "node:fs";
11
- import { resolve } from "node:path";
12
- import { fileURLToPath } from "node:url";
13
- /**
14
- * Seed bundled skills from sourceDir into targetDir (idempotent).
15
- *
16
- * For each subdirectory in sourceDir, checks whether a same-named directory
17
- * already exists in targetDir. If not, copies the entire directory tree
18
- * recursively. Existing skill directories are never overwritten.
19
- *
20
- * @param opts.sourceDir - Absolute path to the repo's skills/ directory.
21
- * If omitted, resolves from __dirname (../../../../skills).
22
- * @param opts.targetDir - Absolute path to the user data skills directory
23
- * (e.g., ~/.comis/skills/).
24
- * @param opts.logger - Optional structured logger for diagnostics.
25
- * @returns SeedResult with seeded and skipped skill names.
26
- */
27
- export function seedBundledSkills(opts) {
28
- const seeded = [];
29
- const skipped = [];
30
- // Resolve source directory: either provided or derived from module location
31
- const sourceDir = opts.sourceDir ?? resolveDefaultSourceDir();
32
- // Handle missing source directory gracefully
33
- if (!existsSync(sourceDir)) {
34
- opts.logger?.debug({ sourceDir }, "Bundled skills source directory not found, skipping seed");
35
- return { seeded, skipped };
36
- }
37
- // Ensure target directory exists
38
- mkdirSync(opts.targetDir, { recursive: true });
39
- // Enumerate skill directories in source
40
- let entries;
41
- try {
42
- entries = readdirSync(sourceDir);
43
- }
44
- catch {
45
- return { seeded, skipped };
46
- }
47
- for (const entry of entries) {
48
- const srcPath = resolve(sourceDir, entry);
49
- // Only process directories (skip files like README.md)
50
- try {
51
- if (!statSync(srcPath).isDirectory())
52
- continue;
53
- }
54
- catch {
55
- continue;
56
- }
57
- const destPath = resolve(opts.targetDir, entry);
58
- if (existsSync(destPath)) {
59
- skipped.push(entry);
60
- opts.logger?.debug({ skill: entry }, "Bundled skill already exists in target, skipping");
61
- }
62
- else {
63
- cpSync(srcPath, destPath, { recursive: true });
64
- seeded.push(entry);
65
- opts.logger?.debug({ skill: entry }, "Bundled skill seeded into target");
66
- }
67
- }
68
- if (seeded.length > 0 || skipped.length > 0) {
69
- opts.logger?.info({ seeded: seeded.length, skipped: skipped.length }, "Bundled skills seeded");
70
- }
71
- return { seeded, skipped };
72
- }
73
- // ---------------------------------------------------------------------------
74
- // Internal helpers
75
- // ---------------------------------------------------------------------------
76
- /**
77
- * Resolve the default bundled skills source directory relative to this module.
78
- * From packages/daemon/dist/wiring/ -> repo root skills/
79
- */
80
- function resolveDefaultSourceDir() {
81
- const __filename = fileURLToPath(import.meta.url);
82
- const __dirname = resolve(__filename, "..");
83
- return resolve(__dirname, "../../../../skills");
84
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * Delivery mirror wiring: adapter creation, hook-based recording, periodic prune.
3
- *
4
- * Phase 502: Session Mirroring.
5
- * @module
6
- */
7
- import type { AppConfig, DeliveryMirrorPort } from "@comis/core";
8
- import type { ComisLogger } from "@comis/infra";
9
- import type { PluginRegistry } from "@comis/core";
10
- export interface DeliveryMirrorResult {
11
- /** The delivery mirror adapter (real or no-op), available immediately. */
12
- deliveryMirror: DeliveryMirrorPort;
13
- /** Starts the periodic prune timer. Call AFTER setupChannels. */
14
- startPrune: () => void;
15
- /** Clears the prune interval timer (call on shutdown). */
16
- shutdown: () => void;
17
- }
18
- export declare function setupDeliveryMirror(deps: {
19
- /** Raw better-sqlite3 database handle (typed as unknown to avoid cross-package type dep). */
20
- db: unknown;
21
- config: AppConfig;
22
- pluginRegistry: PluginRegistry;
23
- logger: ComisLogger;
24
- }): Promise<DeliveryMirrorResult>;
@@ -1,88 +0,0 @@
1
- /**
2
- * Delivery mirror wiring: adapter creation, hook-based recording, periodic prune.
3
- *
4
- * Phase 502: Session Mirroring.
5
- * @module
6
- */
7
- import { createNoOpDeliveryMirror } from "@comis/core";
8
- import { createSqliteDeliveryMirror } from "@comis/memory";
9
- import { ok } from "@comis/shared";
10
- import { createHash } from "node:crypto";
11
- // ---------------------------------------------------------------------------
12
- // Idempotency key computation
13
- // ---------------------------------------------------------------------------
14
- /**
15
- * Compute an idempotency key for a mirror entry.
16
- * Uses session key + text hash + 1-second time bucket to deduplicate
17
- * repeated deliveries of the same text within the same second.
18
- */
19
- function computeIdempotencyKey(sessionKey, text, timestamp) {
20
- const textHash = createHash("sha256").update(text).digest("hex").slice(0, 16);
21
- const bucket = Math.floor(timestamp / 1000);
22
- return `${sessionKey}:${textHash}:${bucket}`;
23
- }
24
- // ---------------------------------------------------------------------------
25
- // Setup function
26
- // ---------------------------------------------------------------------------
27
- export async function setupDeliveryMirror(deps) {
28
- const { db, config, pluginRegistry, logger } = deps;
29
- const mirrorConfig = config.deliveryMirror;
30
- // 1. No-op when disabled
31
- if (!mirrorConfig?.enabled) {
32
- logger.debug("Delivery mirror disabled by config");
33
- return {
34
- deliveryMirror: createNoOpDeliveryMirror(),
35
- startPrune: () => { },
36
- shutdown: () => { },
37
- };
38
- }
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- db is better-sqlite3 Database; typed as unknown to avoid cross-package type dependency
40
- const deliveryMirror = createSqliteDeliveryMirror(db);
41
- logger.info({ retentionMs: mirrorConfig.retentionMs, maxEntriesPerInjection: mirrorConfig.maxEntriesPerInjection, maxCharsPerInjection: mirrorConfig.maxCharsPerInjection }, "Delivery mirror enabled");
42
- // 2. Hook registration: record delivered text via after_delivery hook
43
- pluginRegistry.register({
44
- id: "comis:delivery-mirror",
45
- name: "Delivery Mirror",
46
- version: "1.0.0",
47
- register(api) {
48
- api.registerHook("after_delivery", async (event, ctx) => {
49
- if (!ctx.sessionKey)
50
- return; // No session context -- skip (Pitfall 1)
51
- const now = Date.now();
52
- const idempotencyKey = computeIdempotencyKey(ctx.sessionKey, event.text, now);
53
- const result = await deliveryMirror.record({
54
- sessionKey: ctx.sessionKey,
55
- text: event.text,
56
- mediaUrls: [], // Phase 502 baseline -- HookAfterDeliveryEvent has no mediaUrls field; media URL mirroring deferred
57
- channelType: event.channelType,
58
- channelId: event.channelId,
59
- origin: event.origin,
60
- idempotencyKey,
61
- });
62
- if (result.ok) {
63
- logger.debug({ sessionKey: ctx.sessionKey, channelType: event.channelType, idempotencyKey }, "Mirror entry recorded");
64
- }
65
- // Recording failures are silently tolerated (fire-and-forget hook)
66
- });
67
- return ok(undefined);
68
- },
69
- });
70
- // 3. Prune timer
71
- let pruneInterval;
72
- const startPrune = () => {
73
- pruneInterval = setInterval(async () => {
74
- const result = await deliveryMirror.pruneOld(mirrorConfig.retentionMs);
75
- if (result.ok && result.value > 0) {
76
- logger.debug({ pruned: result.value }, "Delivery mirror pruned");
77
- }
78
- }, mirrorConfig.pruneIntervalMs);
79
- pruneInterval.unref();
80
- };
81
- const shutdown = () => {
82
- if (pruneInterval) {
83
- clearInterval(pruneInterval);
84
- pruneInterval = undefined;
85
- }
86
- };
87
- return { deliveryMirror, startPrune, shutdown };
88
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * Delivery queue wiring: adapter creation, startup drain, periodic prune.
3
- *
4
- * Two-phase lifecycle resolves the circular dependency between the queue and
5
- * channel adapters:
6
- * 1. setupDeliveryQueue() creates the adapter immediately (before setupChannels).
7
- * 2. drainAndStartPrune() runs drain + starts prune timer AFTER setupChannels
8
- * populates channelAdapters.
9
- *
10
- * Phase 499: Crash-Safe Delivery Queue.
11
- * @module
12
- */
13
- import type { AppConfig, TypedEventBus, DeliveryQueuePort } from "@comis/core";
14
- import { type DeliveryAdapter } from "@comis/channels";
15
- import type { ComisLogger } from "@comis/infra";
16
- export interface DeliveryQueueResult {
17
- /** The delivery queue adapter (real or no-op), available immediately. */
18
- deliveryQueue: DeliveryQueuePort;
19
- /** Runs startup drain then starts periodic prune timer. Call AFTER setupChannels. */
20
- drainAndStartPrune: () => Promise<void>;
21
- /** Clears the prune interval timer (call on shutdown). */
22
- shutdown: () => void;
23
- }
24
- export declare function setupDeliveryQueue(deps: {
25
- /** Raw better-sqlite3 database handle (typed as unknown to avoid cross-package type dep). */
26
- db: unknown;
27
- config: AppConfig;
28
- eventBus: TypedEventBus;
29
- logger: ComisLogger;
30
- channelAdapters: Map<string, DeliveryAdapter>;
31
- }): Promise<DeliveryQueueResult>;
@@ -1,132 +0,0 @@
1
- /**
2
- * Delivery queue wiring: adapter creation, startup drain, periodic prune.
3
- *
4
- * Two-phase lifecycle resolves the circular dependency between the queue and
5
- * channel adapters:
6
- * 1. setupDeliveryQueue() creates the adapter immediately (before setupChannels).
7
- * 2. drainAndStartPrune() runs drain + starts prune timer AFTER setupChannels
8
- * populates channelAdapters.
9
- *
10
- * Phase 499: Crash-Safe Delivery Queue.
11
- * @module
12
- */
13
- import { createNoOpDeliveryQueue } from "@comis/core";
14
- import { createSqliteDeliveryQueue } from "@comis/memory";
15
- import { isPermanentError, computeQueueBackoff } from "@comis/channels";
16
- // ---------------------------------------------------------------------------
17
- // Setup function
18
- // ---------------------------------------------------------------------------
19
- export async function setupDeliveryQueue(deps) {
20
- const { db, config, eventBus, logger, channelAdapters } = deps;
21
- const queueConfig = config.deliveryQueue;
22
- // 1. Adapter creation: no-op when disabled
23
- if (!queueConfig.enabled) {
24
- logger.debug("Delivery queue disabled by config");
25
- return {
26
- deliveryQueue: createNoOpDeliveryQueue(),
27
- drainAndStartPrune: async () => { },
28
- shutdown: () => { },
29
- };
30
- }
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- db is better-sqlite3 Database; typed as unknown to avoid cross-package type dependency
32
- const deliveryQueue = createSqliteDeliveryQueue(db);
33
- logger.info({ maxQueueDepth: queueConfig.maxQueueDepth, defaultMaxAttempts: queueConfig.defaultMaxAttempts }, "Delivery queue enabled");
34
- let pruneInterval;
35
- // 2. Startup drain + 3. Periodic prune (deferred until channelAdapters populated)
36
- const drainAndStartPrune = async () => {
37
- // --- Drain ---
38
- if (queueConfig.drainOnStartup) {
39
- await drainDeliveryQueue({
40
- deliveryQueue,
41
- channelAdapters,
42
- eventBus,
43
- logger,
44
- drainBudgetMs: queueConfig.drainBudgetMs,
45
- defaultMaxAttempts: queueConfig.defaultMaxAttempts,
46
- });
47
- }
48
- // --- Prune timer ---
49
- pruneInterval = setInterval(async () => {
50
- const result = await deliveryQueue.pruneExpired();
51
- if (result.ok && result.value > 0) {
52
- logger.debug({ pruned: result.value }, "Delivery queue pruned");
53
- }
54
- }, queueConfig.pruneIntervalMs);
55
- pruneInterval.unref();
56
- };
57
- const shutdown = () => {
58
- if (pruneInterval) {
59
- clearInterval(pruneInterval);
60
- pruneInterval = undefined;
61
- }
62
- };
63
- return { deliveryQueue, drainAndStartPrune, shutdown };
64
- }
65
- // ---------------------------------------------------------------------------
66
- // Drain implementation
67
- // ---------------------------------------------------------------------------
68
- async function drainDeliveryQueue(deps) {
69
- const { deliveryQueue, channelAdapters, eventBus, logger, drainBudgetMs, defaultMaxAttempts } = deps;
70
- const drainStart = Date.now();
71
- const deadline = drainStart + drainBudgetMs;
72
- const pendingResult = await deliveryQueue.pendingEntries();
73
- if (!pendingResult.ok) {
74
- logger.warn({ err: pendingResult.error, hint: "Could not fetch pending entries for drain cycle", errorKind: "internal" }, "Delivery queue drain: failed to fetch pending entries");
75
- return;
76
- }
77
- const entries = pendingResult.value;
78
- if (entries.length === 0) {
79
- logger.debug("Delivery queue drain: no pending entries");
80
- return;
81
- }
82
- let attempted = 0;
83
- let delivered = 0;
84
- let failed = 0;
85
- for (const entry of entries) {
86
- // Budget exhaustion check
87
- if (Date.now() > deadline) {
88
- logger.info({ budgetMs: drainBudgetMs, attempted, remaining: entries.length - attempted }, "Delivery queue drain: budget exhausted");
89
- break;
90
- }
91
- attempted++;
92
- const adapter = channelAdapters.get(entry.channelType);
93
- if (!adapter) {
94
- await deliveryQueue.fail(entry.id, `No adapter for channel type: ${entry.channelType}`);
95
- failed++;
96
- continue;
97
- }
98
- let options = {};
99
- try {
100
- options = JSON.parse(entry.optionsJson);
101
- }
102
- catch {
103
- // Invalid JSON -- send without options
104
- }
105
- const sendResult = await adapter.sendMessage(entry.channelId, entry.text, options);
106
- if (sendResult.ok) {
107
- await deliveryQueue.ack(entry.id, sendResult.value);
108
- delivered++;
109
- }
110
- else {
111
- const errorMsg = sendResult.error.message;
112
- if (isPermanentError(errorMsg) || entry.attemptCount >= (entry.maxAttempts || defaultMaxAttempts)) {
113
- await deliveryQueue.fail(entry.id, errorMsg);
114
- failed++;
115
- }
116
- else {
117
- const nextRetryAt = Date.now() + computeQueueBackoff(entry.attemptCount);
118
- await deliveryQueue.nack(entry.id, errorMsg, nextRetryAt);
119
- failed++;
120
- }
121
- }
122
- }
123
- const durationMs = Date.now() - drainStart;
124
- eventBus.emit("delivery:queue_drained", {
125
- entriesAttempted: attempted,
126
- entriesDelivered: delivered,
127
- entriesFailed: failed,
128
- durationMs,
129
- timestamp: Date.now(),
130
- });
131
- logger.info({ entriesAttempted: attempted, entriesDelivered: delivered, entriesFailed: failed, durationMs }, "Delivery queue drained");
132
- }
@@ -1,38 +0,0 @@
1
- /**
2
- * Monitoring setup: heartbeat runner with configurable monitoring sources.
3
- *
4
- * Extracted from daemon.ts step 6.7 to isolate heartbeat/monitoring
5
- * initialization from the main wiring sequence.
6
- *
7
- * @module
8
- */
9
- import type { AppContainer, ChannelPort } from "@comis/core";
10
- import type { ComisLogger } from "@comis/infra";
11
- import { type HeartbeatRunner, type DuplicateDetector } from "@comis/scheduler";
12
- /** Dependencies for monitoring setup. */
13
- export interface MonitoringDeps {
14
- /** Bootstrap output (config.monitoring, config.scheduler, eventBus). */
15
- container: AppContainer;
16
- /** Module-bound logger for scheduler subsystem. */
17
- schedulerLogger: ComisLogger;
18
- /** Root logger for notification callbacks. */
19
- logger: ComisLogger;
20
- /** Channel adapters for heartbeat delivery (optional -- delivery skipped if not provided). */
21
- adaptersByType?: ReadonlyMap<string, ChannelPort>;
22
- }
23
- /** All services produced by the monitoring setup phase. */
24
- export interface MonitoringResult {
25
- /** Heartbeat runner for periodic health checks (optional). */
26
- heartbeatRunner?: HeartbeatRunner;
27
- /** Duplicate detector shared between global and per-agent heartbeat delivery. */
28
- duplicateDetector?: DuplicateDetector;
29
- }
30
- /**
31
- * Create monitoring heartbeat sources and runner based on config toggles.
32
- *
33
- * Synchronous setup -- creates sources array, builds runner if any are
34
- * enabled, starts the runner, and returns the handle.
35
- *
36
- * @param deps - Monitoring dependencies
37
- */
38
- export declare function setupMonitoring(deps: MonitoringDeps): MonitoringResult;
@@ -1,100 +0,0 @@
1
- /**
2
- * Monitoring setup: heartbeat runner with configurable monitoring sources.
3
- *
4
- * Extracted from daemon.ts step 6.7 to isolate heartbeat/monitoring
5
- * initialization from the main wiring sequence.
6
- *
7
- * @module
8
- */
9
- import { createHeartbeatRunner, createDuplicateDetector, deliverHeartbeatNotification, } from "@comis/scheduler";
10
- import { createDiskSpaceSource, createSystemResourcesSource, createSystemdServiceSource, createSecurityUpdateSource, createGitWatcherSource, } from "../monitoring/index.js";
11
- // ---------------------------------------------------------------------------
12
- // Setup function
13
- // ---------------------------------------------------------------------------
14
- /**
15
- * Create monitoring heartbeat sources and runner based on config toggles.
16
- *
17
- * Synchronous setup -- creates sources array, builds runner if any are
18
- * enabled, starts the runner, and returns the handle.
19
- *
20
- * @param deps - Monitoring dependencies
21
- */
22
- export function setupMonitoring(deps) {
23
- const { container, schedulerLogger, logger, adaptersByType } = deps;
24
- let heartbeatRunner;
25
- const monitoringConfig = container.config.monitoring;
26
- const schedulerConfig = container.config.scheduler;
27
- const monitoringSources = [];
28
- // Create shared duplicate detector for 24h dedup (DLVR-07)
29
- const duplicateDetector = createDuplicateDetector();
30
- if (monitoringConfig.disk.enabled) {
31
- monitoringSources.push(createDiskSpaceSource(monitoringConfig.disk));
32
- }
33
- if (monitoringConfig.resources.enabled) {
34
- monitoringSources.push(createSystemResourcesSource(monitoringConfig.resources));
35
- }
36
- if (monitoringConfig.systemd.enabled) {
37
- monitoringSources.push(createSystemdServiceSource(monitoringConfig.systemd));
38
- }
39
- if (monitoringConfig.securityUpdates.enabled) {
40
- monitoringSources.push(createSecurityUpdateSource(monitoringConfig.securityUpdates));
41
- }
42
- if (monitoringConfig.git.enabled) {
43
- monitoringSources.push(createGitWatcherSource(monitoringConfig.git));
44
- }
45
- if (monitoringSources.length > 0) {
46
- heartbeatRunner = createHeartbeatRunner({
47
- sources: monitoringSources,
48
- eventBus: container.eventBus,
49
- logger: schedulerLogger,
50
- config: {
51
- intervalMs: schedulerConfig.heartbeat.intervalMs,
52
- showOk: schedulerConfig.heartbeat.showOk,
53
- showAlerts: schedulerConfig.heartbeat.showAlerts,
54
- },
55
- quietHoursConfig: schedulerConfig.quietHours,
56
- criticalBypass: schedulerConfig.quietHours.criticalBypass,
57
- onNotification: (notification) => {
58
- const msg = `Monitoring: ${notification.text}`;
59
- if (notification.level === "critical") {
60
- logger.error({ sourceId: notification.sourceId, level: notification.level, hint: "Investigate the monitoring source for critical conditions", errorKind: "resource" }, msg);
61
- }
62
- else if (notification.level === "alert") {
63
- logger.warn({ sourceId: notification.sourceId, level: notification.level, hint: "Review the monitoring source alert details", errorKind: "resource" }, msg);
64
- }
65
- else {
66
- logger.info({ sourceId: notification.sourceId, level: notification.level }, msg);
67
- }
68
- // Deliver to configured target channel (fire-and-forget)
69
- // Global heartbeat uses scheduler.heartbeat config -- per-agent delivery targets are wired in Phase 329.
70
- if (adaptersByType && adaptersByType.size > 0) {
71
- const globalTarget = resolveGlobalDeliveryTarget(container.config);
72
- if (globalTarget) {
73
- void deliverHeartbeatNotification({ adaptersByType, duplicateDetector, eventBus: container.eventBus, logger: schedulerLogger }, globalTarget, notification, { agentId: "system" }).catch((err) => {
74
- const errMsg = err instanceof Error ? err.message : String(err);
75
- schedulerLogger.warn({ err: errMsg, hint: "Heartbeat delivery failed unexpectedly", errorKind: "internal" }, "Heartbeat delivery error");
76
- });
77
- }
78
- }
79
- },
80
- });
81
- heartbeatRunner.start();
82
- schedulerLogger.info({ sourceCount: monitoringSources.length }, "Monitoring heartbeat runner started");
83
- }
84
- return { heartbeatRunner, duplicateDetector };
85
- }
86
- // ---------------------------------------------------------------------------
87
- // Helpers
88
- // ---------------------------------------------------------------------------
89
- /**
90
- * Resolve global heartbeat delivery target from config.
91
- *
92
- * Currently returns undefined -- global system monitoring does not have a
93
- * delivery target. Per-agent delivery is handled by PerAgentHeartbeatRunner
94
- * (Phase 329). This function exists so that adding a global delivery target
95
- * in a future phase is a one-line change.
96
- */
97
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
98
- function resolveGlobalDeliveryTarget(_config) {
99
- return undefined;
100
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * RPC bridge setup: rpcCall outer wrapper with deferred dispatch wiring.
3
- *
4
- * Extracted from daemon.ts to isolate the rpcCall closure pattern (debug/error
5
- * logging, classifyRpcError) and the deferred rpcCallInner assignment. The
6
- * rpcCall function is needed by setupTools (early in the boot sequence), but
7
- * rpcCallInner depends on heartbeatRunner from setupMonitoring (later in the
8
- * sequence). This module encapsulates the deferred wiring via wireDispatch().
9
- *
10
- * @module
11
- */
12
- import type { ComisLogger } from "@comis/infra";
13
- import type { RpcCall } from "@comis/skills";
14
- import type { RpcDispatchDeps } from "../rpc/rpc-dispatch.js";
15
- /** All services produced by the RPC bridge setup phase. */
16
- export interface RpcBridgeResult {
17
- /** The rpcCall function usable immediately (delegates to inner dispatch once wired). */
18
- rpcCall: RpcCall;
19
- /** Call after setupMonitoring to wire the real dispatch with all deps including heartbeatRunner. */
20
- wireDispatch: (deps: RpcDispatchDeps) => void;
21
- }
22
- /**
23
- * Create the rpcCall wrapper and deferred dispatch mechanism.
24
- *
25
- * The returned rpcCall can be passed to setupTools immediately. After
26
- * setupMonitoring resolves the heartbeatRunner TDZ, call wireDispatch()
27
- * with the full RpcDispatchDeps to wire the real dispatch function.
28
- *
29
- * @param deps.gatewayLogger - Logger for RPC call tracing
30
- * @returns rpcCall function and wireDispatch callback
31
- */
32
- export declare function setupRpcBridge(deps: {
33
- gatewayLogger: ComisLogger;
34
- }): RpcBridgeResult;