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,167 +0,0 @@
1
- /**
2
- * Grep tool output mode wrapper.
3
- *
4
- * Post-processes grep tool results based on an `output_mode` parameter added
5
- * at the Comis layer. The upstream SDK grep always returns full match output
6
- * (file:line: content). This wrapper provides additional output formats:
7
- * - `content` (default): unchanged upstream output
8
- * - `files_with_matches`: deduplicated file paths only
9
- * - `count`: per-file match counts, sorted by count descending
10
- *
11
- * @module
12
- */
13
- import { Type } from "@sinclair/typebox";
14
- /**
15
- * Regex to extract file path and line number from grep output lines.
16
- *
17
- * Match lines: `filepath:linenum: content`
18
- * Context lines: `filepath-linenum- content`
19
- *
20
- * The file path is everything before the first `:linenum:` or `-linenum-`.
21
- */
22
- const MATCH_LINE_RE = /^(.+?):(\d+): /;
23
- const CONTEXT_LINE_RE = /^(.+?)-(\d+)- /;
24
- /** Sentinel for no-match output from pi-coding-agent grep. */
25
- const NO_MATCHES = "No matches found";
26
- /**
27
- * Extract trailing notice lines (starting with `[`) from the output.
28
- * Notices appear after a blank line at the end of grep output.
29
- */
30
- function splitNotices(text) {
31
- const lines = text.split("\n");
32
- const notices = [];
33
- // Walk backwards to find trailing notice lines
34
- let i = lines.length - 1;
35
- while (i >= 0 && (lines[i].startsWith("[") || lines[i].trim() === "")) {
36
- if (lines[i].startsWith("[")) {
37
- notices.unshift(lines[i]);
38
- }
39
- i--;
40
- }
41
- const body = lines.slice(0, i + 1).join("\n");
42
- return { body, notices };
43
- }
44
- /**
45
- * Parse grep output into file-path-keyed match counts.
46
- * Only counts actual match lines (`:linenum: `), not context lines (`-linenum- `).
47
- */
48
- function countMatchesByFile(body) {
49
- const counts = new Map();
50
- for (const line of body.split("\n")) {
51
- const match = MATCH_LINE_RE.exec(line);
52
- if (match) {
53
- const filePath = match[1];
54
- counts.set(filePath, (counts.get(filePath) ?? 0) + 1);
55
- }
56
- }
57
- return counts;
58
- }
59
- /**
60
- * Extract unique file paths from grep output (both match and context lines).
61
- */
62
- function extractUniquePaths(body) {
63
- const seen = new Set();
64
- const paths = [];
65
- for (const line of body.split("\n")) {
66
- const matchResult = MATCH_LINE_RE.exec(line) ?? CONTEXT_LINE_RE.exec(line);
67
- if (matchResult) {
68
- const filePath = matchResult[1];
69
- if (!seen.has(filePath)) {
70
- seen.add(filePath);
71
- paths.push(filePath);
72
- }
73
- }
74
- }
75
- return paths;
76
- }
77
- /**
78
- * Wrap the grep tool to support `output_mode` parameter.
79
- *
80
- * Extends the tool's parameter schema to add `output_mode`, strips it
81
- * before delegating to the original tool, then post-processes the result
82
- * based on the requested output mode.
83
- *
84
- * @param tool - The grep tool (already wrapped with safePath)
85
- * @returns A new AgentTool with output_mode support
86
- */
87
- export function wrapGrepWithOutputMode(
88
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- AgentTool generic requires `any` per pi-agent-core API
89
- tool) {
90
- // Extend parameter schema with output_mode
91
- const originalParams = tool.parameters;
92
- const extendedParams = Type.Object({
93
- ...originalParams.properties,
94
- output_mode: Type.Optional(Type.Union([
95
- Type.Literal("content"),
96
- Type.Literal("files_with_matches"),
97
- Type.Literal("count"),
98
- ], {
99
- description: "Output format: 'content' (default, full match lines), 'files_with_matches' (file paths only), or 'count' (match counts per file)",
100
- })),
101
- offset: Type.Optional(Type.Number({
102
- description: "Skip the first N result entries (lines for content mode, file paths for files_with_matches, count entries for count mode). Default: 0.",
103
- })),
104
- });
105
- return {
106
- ...tool,
107
- parameters: extendedParams,
108
- async execute(toolCallId, params, signal, onUpdate) {
109
- // Extract and strip output_mode + offset before delegating to upstream
110
- const outputMode = params.output_mode ?? "content";
111
- const offset = params.offset ?? 0;
112
- const upstreamParams = { ...params };
113
- delete upstreamParams.output_mode;
114
- delete upstreamParams.offset;
115
- // Delegate to original grep tool
116
- const result = await tool.execute(toolCallId, upstreamParams, signal, onUpdate);
117
- // Extract text content from result
118
- const textParts = result.content
119
- .filter((c) => c.type === "text")
120
- .map((c) => c.text);
121
- const fullText = textParts.join("");
122
- // Passthrough for empty or no-match results
123
- if (fullText.trim() === "" || fullText.trim() === NO_MATCHES) {
124
- return result;
125
- }
126
- // For default/content mode, apply offset if needed then return
127
- if (outputMode === "content") {
128
- if (offset <= 0) {
129
- return result;
130
- }
131
- const { body, notices } = splitNotices(fullText);
132
- const lines = body.split("\n").slice(offset);
133
- let transformedText = lines.join("\n");
134
- if (notices.length > 0) {
135
- transformedText += (transformedText ? "\n\n" : "") + notices.join("\n");
136
- }
137
- return {
138
- ...result,
139
- content: [{ type: "text", text: transformedText }],
140
- };
141
- }
142
- // Separate output body from trailing notices
143
- const { body, notices } = splitNotices(fullText);
144
- let transformedText;
145
- if (outputMode === "files_with_matches") {
146
- const paths = extractUniquePaths(body).slice(offset);
147
- transformedText = paths.join("\n");
148
- }
149
- else {
150
- // count mode
151
- const counts = countMatchesByFile(body);
152
- const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(offset);
153
- transformedText = sorted
154
- .map(([filePath, count]) => `${filePath}: ${count} ${count === 1 ? "match" : "matches"}`)
155
- .join("\n");
156
- }
157
- // Append notices if present
158
- if (notices.length > 0) {
159
- transformedText += (transformedText ? "\n\n" : "") + notices.join("\n");
160
- }
161
- return {
162
- ...result,
163
- content: [{ type: "text", text: transformedText }],
164
- };
165
- },
166
- };
167
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Task plan tool: stateless formatter for multi-step work plans.
3
- *
4
- * Agents use this tool to structure and communicate multi-step plans
5
- * visible in conversation history. Pure formatting -- no persistence,
6
- * no side effects, no state.
7
- *
8
- * @module
9
- */
10
- import type { AgentTool } from "@mariozechner/pi-agent-core";
11
- declare const TaskPlanParams: import("@sinclair/typebox").TObject<{
12
- tasks: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
13
- id: import("@sinclair/typebox").TString;
14
- description: import("@sinclair/typebox").TString;
15
- status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"pending">, import("@sinclair/typebox").TLiteral<"in-progress">, import("@sinclair/typebox").TLiteral<"done">, import("@sinclair/typebox").TLiteral<"skipped">]>;
16
- }>>;
17
- title: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
18
- }>;
19
- /**
20
- * Create a stateless task_plan tool for structuring multi-step work plans.
21
- *
22
- * @returns AgentTool that formats task lists into readable plan text
23
- */
24
- export declare function createTaskPlanTool(): AgentTool<typeof TaskPlanParams>;
25
- export {};
@@ -1,67 +0,0 @@
1
- /**
2
- * Task plan tool: stateless formatter for multi-step work plans.
3
- *
4
- * Agents use this tool to structure and communicate multi-step plans
5
- * visible in conversation history. Pure formatting -- no persistence,
6
- * no side effects, no state.
7
- *
8
- * @module
9
- */
10
- import { Type } from "@sinclair/typebox";
11
- import { jsonResult } from "./platform/tool-helpers.js";
12
- // ---------------------------------------------------------------------------
13
- // Parameter schema
14
- // ---------------------------------------------------------------------------
15
- const TaskPlanParams = Type.Object({
16
- tasks: Type.Array(Type.Object({
17
- id: Type.String({ description: "Unique task identifier" }),
18
- description: Type.String({ description: "What needs to be done" }),
19
- status: Type.Union([
20
- Type.Literal("pending"),
21
- Type.Literal("in-progress"),
22
- Type.Literal("done"),
23
- Type.Literal("skipped"),
24
- ], { description: "Current task status" }),
25
- }), { description: "List of tasks in the plan" }),
26
- title: Type.Optional(Type.String({ description: "Optional plan title (defaults to 'Task Plan')" })),
27
- });
28
- // ---------------------------------------------------------------------------
29
- // Status icon mapping
30
- // ---------------------------------------------------------------------------
31
- const STATUS_ICONS = {
32
- pending: "[ ]",
33
- "in-progress": "[~]",
34
- done: "[x]",
35
- skipped: "[-]",
36
- };
37
- // ---------------------------------------------------------------------------
38
- // Factory
39
- // ---------------------------------------------------------------------------
40
- /**
41
- * Create a stateless task_plan tool for structuring multi-step work plans.
42
- *
43
- * @returns AgentTool that formats task lists into readable plan text
44
- */
45
- export function createTaskPlanTool() {
46
- return {
47
- name: "task_plan",
48
- label: "Task Plan",
49
- description: "Structure and display a multi-step task plan. Returns a formatted summary. " +
50
- "Stateless -- use this to organize your work and communicate progress.",
51
- parameters: TaskPlanParams,
52
- async execute(_toolCallId, params) {
53
- const p = params;
54
- const title = p.title ?? "Task Plan";
55
- const tasks = p.tasks ?? [];
56
- // Format each task line
57
- const lines = tasks.map((t) => `${STATUS_ICONS[t.status]} ${t.id}: ${t.description}`);
58
- // Count completed
59
- const doneCount = tasks.filter((t) => t.status === "done").length;
60
- const totalCount = tasks.length;
61
- const summary = `${doneCount}/${totalCount} tasks completed`;
62
- // Build plan text
63
- const plan = [title, "", ...lines, "", summary].join("\n");
64
- return jsonResult({ plan, summary, tasks });
65
- },
66
- };
67
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * MCP Tool Bridge: Converts MCP tool definitions to AgentTool instances.
3
- *
4
- * Follows the pattern established by skill-tool-bridge.ts for converting
5
- * external tool definitions into the AgentTool format expected by the
6
- * agent executor (pi-agent-core).
7
- *
8
- * Key functions:
9
- * - mcpToolsToAgentTools: Batch convert MCP tools to AgentTool[]
10
- * - jsonSchemaToTypeBox: Basic JSON Schema -> TypeBox conversion
11
- *
12
- * @module
13
- */
14
- import type { AgentTool } from "@mariozechner/pi-agent-core";
15
- import { type TSchema } from "@sinclair/typebox";
16
- import { type ToolSourceProfile } from "../builtin/tool-source-profiles.js";
17
- import type { McpToolDefinition, McpClientManager } from "./mcp-client.js";
18
- /**
19
- * Extract the MCP server name from a sanitized tool name.
20
- *
21
- * Sanitized MCP tool names use the format `mcp__serverName--toolName`.
22
- * Returns `undefined` for non-MCP tools or malformed names.
23
- *
24
- * @example
25
- * extractMcpServerName("mcp__context7--resolve-library-id") // "context7"
26
- * extractMcpServerName("mcp__srv__v2--ns--tool") // "srv__v2"
27
- * extractMcpServerName("bash") // undefined
28
- */
29
- export declare function extractMcpServerName(toolName: string): string | undefined;
30
- /**
31
- * Classify an MCP error message into a category for observability.
32
- *
33
- * Returns one of: "timeout", "connection", "tool_error", "transport", "unknown".
34
- */
35
- export declare function classifyMcpErrorType(errorText: string | undefined): string;
36
- /**
37
- * Convert a basic JSON Schema definition to a TypeBox TSchema.
38
- *
39
- * Handles primitive types, arrays, and objects. Complex schema features
40
- * (oneOf, allOf, $ref, etc.) fall back to Type.Any().
41
- *
42
- * This is intentionally simple -- MCP tool schemas are typically flat
43
- * objects with primitive properties. Complex schemas still work but
44
- * lose TypeBox-level validation detail.
45
- */
46
- export declare function jsonSchemaToTypeBox(schema: Record<string, unknown>): TSchema;
47
- /** Maximum characters for LLM-facing MCP tool descriptions. */
48
- export declare const MAX_LLM_DESCRIPTION_CHARS = 2048;
49
- /**
50
- * Sanitize a qualified MCP tool name for use as an LLM API tool name.
51
- *
52
- * LLM APIs (Anthropic, OpenAI) require tool names to match `^[a-zA-Z0-9_-]{1,128}$`.
53
- * Qualified names like "mcp:context7/resolve-library-id" contain invalid characters
54
- * (`:`, `/`). This function replaces them: "mcp:" -> "mcp__", "/" -> "--".
55
- *
56
- * Example: "mcp:context7/resolve-library-id" -> "mcp__context7--resolve-library-id"
57
- */
58
- export declare function sanitizeMcpToolName(qualifiedName: string): string;
59
- /**
60
- * Convert an array of MCP tool definitions to AgentTool instances.
61
- *
62
- * Each AgentTool's execute() delegates to the provided callTool function,
63
- * which dispatches to the correct MCP server connection. Error results
64
- * from the MCP server are returned as text content (not thrown), matching
65
- * the AgentTool contract.
66
- *
67
- * Successful results are capped to the resolved source profile's maxChars
68
- * limit, preventing oversized MCP responses from consuming agent context.
69
- *
70
- * @param tools - MCP tool definitions from McpClientManager.getTools()
71
- * @param callTool - McpClientManager.callTool bound function
72
- * @param toolSourceProfiles - Optional per-tool overrides for source profiles
73
- * @returns AgentTool instances ready for the agent executor
74
- */
75
- export declare function mcpToolsToAgentTools(tools: McpToolDefinition[], callTool: McpClientManager["callTool"], toolSourceProfiles?: Record<string, Partial<ToolSourceProfile>>): AgentTool<any>[];
@@ -1,235 +0,0 @@
1
- /**
2
- * MCP Tool Bridge: Converts MCP tool definitions to AgentTool instances.
3
- *
4
- * Follows the pattern established by skill-tool-bridge.ts for converting
5
- * external tool definitions into the AgentTool format expected by the
6
- * agent executor (pi-agent-core).
7
- *
8
- * Key functions:
9
- * - mcpToolsToAgentTools: Batch convert MCP tools to AgentTool[]
10
- * - jsonSchemaToTypeBox: Basic JSON Schema -> TypeBox conversion
11
- *
12
- * @module
13
- */
14
- import { Type } from "@sinclair/typebox";
15
- import { registerToolMetadata } from "@comis/core";
16
- import { resolveSourceProfile } from "../builtin/tool-source-profiles.js";
17
- import { sanitizeMcpToolResult } from "./mcp-result-sanitizer.js";
18
- // ---------------------------------------------------------------------------
19
- // MCP tool name parsing and error classification
20
- // ---------------------------------------------------------------------------
21
- /**
22
- * Extract the MCP server name from a sanitized tool name.
23
- *
24
- * Sanitized MCP tool names use the format `mcp__serverName--toolName`.
25
- * Returns `undefined` for non-MCP tools or malformed names.
26
- *
27
- * @example
28
- * extractMcpServerName("mcp__context7--resolve-library-id") // "context7"
29
- * extractMcpServerName("mcp__srv__v2--ns--tool") // "srv__v2"
30
- * extractMcpServerName("bash") // undefined
31
- */
32
- export function extractMcpServerName(toolName) {
33
- if (!toolName.startsWith("mcp__"))
34
- return undefined;
35
- const rest = toolName.slice(5); // strip "mcp__"
36
- const sepIdx = rest.indexOf("--");
37
- if (sepIdx <= 0)
38
- return undefined; // no separator or empty server name
39
- return rest.slice(0, sepIdx);
40
- }
41
- /**
42
- * Classify an MCP error message into a category for observability.
43
- *
44
- * Returns one of: "timeout", "connection", "tool_error", "transport", "unknown".
45
- */
46
- export function classifyMcpErrorType(errorText) {
47
- if (!errorText)
48
- return "unknown";
49
- const lower = errorText.toLowerCase();
50
- if (lower.includes("timed out") || lower.includes("timeout"))
51
- return "timeout";
52
- if (lower.includes("not connected") || lower.includes("disconnected"))
53
- return "connection";
54
- if (lower.includes("crashed unexpectedly") || lower.includes("pipe") || lower.includes("epipe") || lower.includes("econnreset"))
55
- return "transport";
56
- if (lower.includes("mcp tool error:") || lower.includes("mcp tool returned an error"))
57
- return "tool_error";
58
- return "unknown";
59
- }
60
- // ---------------------------------------------------------------------------
61
- // JSON Schema -> TypeBox conversion
62
- // ---------------------------------------------------------------------------
63
- /**
64
- * Convert a basic JSON Schema definition to a TypeBox TSchema.
65
- *
66
- * Handles primitive types, arrays, and objects. Complex schema features
67
- * (oneOf, allOf, $ref, etc.) fall back to Type.Any().
68
- *
69
- * This is intentionally simple -- MCP tool schemas are typically flat
70
- * objects with primitive properties. Complex schemas still work but
71
- * lose TypeBox-level validation detail.
72
- */
73
- export function jsonSchemaToTypeBox(schema) {
74
- const type = schema.type;
75
- if (type === "string") {
76
- return Type.String();
77
- }
78
- if (type === "number") {
79
- return Type.Number();
80
- }
81
- if (type === "integer") {
82
- return Type.Integer();
83
- }
84
- if (type === "boolean") {
85
- return Type.Boolean();
86
- }
87
- if (type === "array") {
88
- const items = schema.items;
89
- if (items) {
90
- return Type.Array(jsonSchemaToTypeBox(items));
91
- }
92
- return Type.Array(Type.Any());
93
- }
94
- if (type === "object") {
95
- const properties = schema.properties;
96
- const required = schema.required ?? [];
97
- if (!properties) {
98
- return Type.Object({});
99
- }
100
- const typeboxProps = {};
101
- for (const [key, propSchema] of Object.entries(properties)) {
102
- const converted = jsonSchemaToTypeBox(propSchema);
103
- typeboxProps[key] = required.includes(key) ? converted : Type.Optional(converted);
104
- }
105
- return Type.Object(typeboxProps);
106
- }
107
- // Fallback for unknown or complex schema types
108
- return Type.Any();
109
- }
110
- // ---------------------------------------------------------------------------
111
- // Description truncation (CTX-01)
112
- // ---------------------------------------------------------------------------
113
- /** Maximum characters for LLM-facing MCP tool descriptions. */
114
- export const MAX_LLM_DESCRIPTION_CHARS = 2048;
115
- const TRUNCATED_SUFFIX = " [truncated]";
116
- /**
117
- * Truncate a tool description for LLM consumption. Returns the original
118
- * string when it fits within the budget, or a truncated version with
119
- * "[truncated]" suffix. Returns undefined for undefined input.
120
- */
121
- function truncateDescription(desc) {
122
- if (!desc || desc.length <= MAX_LLM_DESCRIPTION_CHARS)
123
- return desc;
124
- return desc.slice(0, MAX_LLM_DESCRIPTION_CHARS - TRUNCATED_SUFFIX.length) + TRUNCATED_SUFFIX;
125
- }
126
- // ---------------------------------------------------------------------------
127
- // MCP tool -> AgentTool conversion
128
- // ---------------------------------------------------------------------------
129
- /**
130
- * Extract the server name from a qualified tool name "mcp:{server}/{tool}".
131
- * Returns the full name as fallback.
132
- */
133
- function extractServerName(qualifiedName) {
134
- const match = qualifiedName.match(/^mcp:([^/]+)\//);
135
- return match ? match[1] : qualifiedName;
136
- }
137
- /**
138
- * Sanitize a qualified MCP tool name for use as an LLM API tool name.
139
- *
140
- * LLM APIs (Anthropic, OpenAI) require tool names to match `^[a-zA-Z0-9_-]{1,128}$`.
141
- * Qualified names like "mcp:context7/resolve-library-id" contain invalid characters
142
- * (`:`, `/`). This function replaces them: "mcp:" -> "mcp__", "/" -> "--".
143
- *
144
- * Example: "mcp:context7/resolve-library-id" -> "mcp__context7--resolve-library-id"
145
- */
146
- export function sanitizeMcpToolName(qualifiedName) {
147
- return qualifiedName.replace(/:/g, "__").replace(/\//g, "--");
148
- }
149
- /**
150
- * Convert an array of MCP tool definitions to AgentTool instances.
151
- *
152
- * Each AgentTool's execute() delegates to the provided callTool function,
153
- * which dispatches to the correct MCP server connection. Error results
154
- * from the MCP server are returned as text content (not thrown), matching
155
- * the AgentTool contract.
156
- *
157
- * Successful results are capped to the resolved source profile's maxChars
158
- * limit, preventing oversized MCP responses from consuming agent context.
159
- *
160
- * @param tools - MCP tool definitions from McpClientManager.getTools()
161
- * @param callTool - McpClientManager.callTool bound function
162
- * @param toolSourceProfiles - Optional per-tool overrides for source profiles
163
- * @returns AgentTool instances ready for the agent executor
164
- */
165
- export function mcpToolsToAgentTools(tools, callTool, toolSourceProfiles) {
166
- return tools.map((tool) => {
167
- const typeboxSchema = jsonSchemaToTypeBox(tool.inputSchema);
168
- const serverName = extractServerName(tool.qualifiedName);
169
- const sanitizedName = sanitizeMcpToolName(tool.qualifiedName);
170
- // CTX-02: Register full description as searchHint for BM25 scoring in discover_tools
171
- registerToolMetadata(sanitizedName, { searchHint: tool.description ?? "" });
172
- return {
173
- name: sanitizedName,
174
- label: tool.name,
175
- // CTX-01: Truncate description for LLM token budget; full text preserved in searchHint above
176
- description: truncateDescription(tool.description) ?? `MCP tool from ${serverName}`,
177
- parameters: typeboxSchema,
178
- async execute(_toolCallId, params) {
179
- try {
180
- const result = await callTool(tool.qualifiedName, params);
181
- if (!result.ok) {
182
- return {
183
- content: [{ type: "text", text: `MCP tool error: ${result.error.message}` }],
184
- details: { success: false },
185
- };
186
- }
187
- const value = result.value;
188
- if (value.isError) {
189
- const errorText = value.content
190
- .filter((c) => c.type === "text" && c.text)
191
- .map((c) => c.text)
192
- .join("\n");
193
- return {
194
- content: [
195
- {
196
- type: "text",
197
- text: errorText || "MCP tool returned an error with no details",
198
- },
199
- ],
200
- details: { success: false },
201
- };
202
- }
203
- // Collect text content from the MCP result
204
- let textParts = value.content
205
- .filter((c) => c.type === "text" && c.text)
206
- .map((c) => c.text)
207
- .join("\n");
208
- // CTX-07: Sanitize MCP result (NFKC normalization + invisible char removal)
209
- textParts = sanitizeMcpToolResult(textParts);
210
- // Source-gate: cap text to resolved profile's maxChars limit
211
- const profile = resolveSourceProfile(sanitizedName, toolSourceProfiles?.[sanitizedName]);
212
- if (textParts.length > profile.maxChars) {
213
- const originalLen = textParts.length;
214
- textParts = textParts.slice(0, profile.maxChars) +
215
- `\n\n[MCP tool result truncated: ${originalLen} chars -> ${profile.maxChars} chars]`;
216
- }
217
- return {
218
- content: [{ type: "text", text: textParts || "Tool returned no text content" }],
219
- details: { success: true },
220
- };
221
- }
222
- catch (error) {
223
- // Defense-in-depth: callTool returns Result and should never throw,
224
- // but if something unexpected happens, return a clean error to the agent
225
- // instead of letting it propagate and produce an opaque SDK error message.
226
- const message = error instanceof Error ? error.message : String(error);
227
- return {
228
- content: [{ type: "text", text: `MCP tool "${tool.qualifiedName}" crashed unexpectedly: ${message}` }],
229
- details: { success: false },
230
- };
231
- }
232
- },
233
- };
234
- });
235
- }