@oka-core/reason 0.2.15 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abort-controller.d.ts +19 -0
- package/dist/abort-controller.d.ts.map +1 -0
- package/dist/abort-controller.js +53 -0
- package/dist/activity-tracker.d.ts +48 -0
- package/dist/activity-tracker.d.ts.map +1 -0
- package/dist/activity-tracker.js +80 -0
- package/dist/analytics.d.ts +49 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +88 -0
- package/dist/array.d.ts +12 -0
- package/dist/array.d.ts.map +1 -0
- package/dist/array.js +20 -0
- package/dist/async-context.d.ts +20 -0
- package/dist/async-context.d.ts.map +1 -0
- package/dist/async-context.js +25 -0
- package/dist/binary-check.d.ts +16 -0
- package/dist/binary-check.d.ts.map +1 -0
- package/dist/binary-check.js +43 -0
- package/dist/buffered-writer.d.ts +30 -0
- package/dist/buffered-writer.d.ts.map +1 -0
- package/dist/buffered-writer.js +87 -0
- package/dist/circular-buffer.d.ts +28 -0
- package/dist/circular-buffer.d.ts.map +1 -0
- package/dist/circular-buffer.js +61 -0
- package/dist/cleanup-registry.d.ts +23 -0
- package/dist/cleanup-registry.d.ts.map +1 -0
- package/dist/cleanup-registry.js +34 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +32 -10
- package/dist/combined-abort-signal.d.ts +25 -0
- package/dist/combined-abort-signal.d.ts.map +1 -0
- package/dist/combined-abort-signal.js +47 -0
- package/dist/cron-lock.d.ts +29 -0
- package/dist/cron-lock.d.ts.map +1 -0
- package/dist/cron-lock.js +127 -0
- package/dist/cron-scheduler.d.ts +41 -0
- package/dist/cron-scheduler.d.ts.map +1 -0
- package/dist/cron-scheduler.js +189 -0
- package/dist/cron-tasks.d.ts +86 -0
- package/dist/cron-tasks.d.ts.map +1 -0
- package/dist/cron-tasks.js +205 -0
- package/dist/cron.d.ts +35 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +215 -0
- package/dist/env.d.ts +26 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +50 -0
- package/dist/errors.d.ts +99 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +214 -0
- package/dist/format.d.ts +21 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +48 -0
- package/dist/fps-tracker.d.ts +22 -0
- package/dist/fps-tracker.d.ts.map +1 -0
- package/dist/fps-tracker.js +44 -0
- package/dist/graceful-shutdown.d.ts +35 -0
- package/dist/graceful-shutdown.d.ts.map +1 -0
- package/dist/graceful-shutdown.js +89 -0
- package/dist/hash.d.ts +21 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +31 -0
- package/dist/heap-diagnostics.d.ts +68 -0
- package/dist/heap-diagnostics.d.ts.map +1 -0
- package/dist/heap-diagnostics.js +110 -0
- package/dist/idle-timeout.d.ts +21 -0
- package/dist/idle-timeout.d.ts.map +1 -0
- package/dist/idle-timeout.js +42 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/intl.d.ts +18 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +75 -0
- package/dist/jsonl.d.ts +16 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/jsonl.js +60 -0
- package/dist/lazy-schema.d.ts +6 -0
- package/dist/lazy-schema.d.ts.map +1 -0
- package/dist/lazy-schema.js +8 -0
- package/dist/memo.d.ts +64 -0
- package/dist/memo.d.ts.map +1 -0
- package/dist/memo.js +162 -0
- package/dist/pkce.d.ts +13 -0
- package/dist/pkce.d.ts.map +1 -0
- package/dist/pkce.js +28 -0
- package/dist/priority-queue.d.ts +36 -0
- package/dist/priority-queue.d.ts.map +1 -0
- package/dist/priority-queue.js +97 -0
- package/dist/process-utils.d.ts +20 -0
- package/dist/process-utils.d.ts.map +1 -0
- package/dist/process-utils.js +54 -0
- package/dist/query-guard.d.ts +34 -0
- package/dist/query-guard.d.ts.map +1 -0
- package/dist/query-guard.js +74 -0
- package/dist/retry.d.ts +60 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +89 -0
- package/dist/schemas.d.ts +6 -6
- package/dist/secrets.d.ts +44 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +115 -0
- package/dist/semantic-types.d.ts +39 -0
- package/dist/semantic-types.d.ts.map +1 -0
- package/dist/semantic-types.js +49 -0
- package/dist/sequential.d.ts +21 -0
- package/dist/sequential.d.ts.map +1 -0
- package/dist/sequential.js +49 -0
- package/dist/signal.d.ts +29 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +39 -0
- package/dist/sleep.d.ts +21 -0
- package/dist/sleep.d.ts.map +1 -0
- package/dist/sleep.js +58 -0
- package/dist/slow-ops.d.ts +41 -0
- package/dist/slow-ops.d.ts.map +1 -0
- package/dist/slow-ops.js +133 -0
- package/dist/store.d.ts +20 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +34 -0
- package/dist/stream.d.ts +29 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +92 -0
- package/dist/string-utils.d.ts +46 -0
- package/dist/string-utils.d.ts.map +1 -0
- package/dist/string-utils.js +69 -0
- package/dist/strip-bom.d.ts +8 -0
- package/dist/strip-bom.d.ts.map +1 -0
- package/dist/strip-bom.js +10 -0
- package/dist/subprocess-env.d.ts +25 -0
- package/dist/subprocess-env.d.ts.map +1 -0
- package/dist/subprocess-env.js +55 -0
- package/dist/temp-file.d.ts +18 -0
- package/dist/temp-file.d.ts.map +1 -0
- package/dist/temp-file.js +26 -0
- package/dist/tool-contract.d.ts +85 -0
- package/dist/tool-contract.d.ts.map +1 -0
- package/dist/tool-contract.js +101 -0
- package/dist/tools/read.d.ts +2 -10
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +662 -537
- package/dist/tools/write.d.ts +3 -2
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +329 -177
- package/dist/uuid.d.ts +20 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +28 -0
- package/dist/validation.d.ts +64 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +236 -0
- package/dist/with-resolvers.d.ts +12 -0
- package/dist/with-resolvers.d.ts.map +1 -0
- package/dist/with-resolvers.js +14 -0
- package/dist/xml-escape.d.ts +12 -0
- package/dist/xml-escape.d.ts.map +1 -0
- package/dist/xml-escape.js +15 -0
- package/package.json +1 -1
package/dist/tools/read.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { toUserMessage, toSafeTelemetryMessage } from "../errors.js";
|
|
3
|
+
import { validateInput } from "../validation.js";
|
|
4
|
+
import { analytics } from "../analytics.js";
|
|
5
|
+
import { buildTool, registerToolPool } from "../tool-contract.js";
|
|
2
6
|
// ─── Formatters ────────────────────────────────────────────────────
|
|
3
7
|
function formatEvents(events) {
|
|
4
8
|
if (events.length === 0)
|
|
@@ -88,547 +92,668 @@ function formatHybridResults(query, results, totalCandidates, pipeline) {
|
|
|
88
92
|
}
|
|
89
93
|
return lines.join("\n");
|
|
90
94
|
}
|
|
91
|
-
// ─── Tool
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
return {
|
|
179
|
-
content: [{ type: "text", text: `Error: ${error}` }],
|
|
180
|
-
isError: true,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
// ─── learnings ─────────────────────────────────────────────────
|
|
185
|
-
server.tool("learnings", "Browse institutional knowledge. Filter by category, status, " +
|
|
186
|
-
"confidence, or repo. Returns consolidated learnings with evidence counts.", {
|
|
187
|
-
category: z
|
|
188
|
-
.enum([
|
|
189
|
-
"architecture",
|
|
190
|
-
"bug_pattern",
|
|
191
|
-
"convention",
|
|
192
|
-
"decision",
|
|
193
|
-
"performance",
|
|
194
|
-
"security",
|
|
195
|
-
"workflow",
|
|
196
|
-
"domain_knowledge",
|
|
197
|
-
])
|
|
198
|
-
.optional()
|
|
199
|
-
.describe("Filter by learning category"),
|
|
200
|
-
status: z
|
|
201
|
-
.enum(["active", "reinforced", "contradicted", "obsolete"])
|
|
202
|
-
.optional()
|
|
203
|
-
.describe("Filter by learning status"),
|
|
204
|
-
min_confidence: z
|
|
205
|
-
.number()
|
|
206
|
-
.optional()
|
|
207
|
-
.describe("Minimum confidence (0.0-1.0)"),
|
|
208
|
-
limit: z.number().optional().default(30).describe("Max results"),
|
|
209
|
-
visibility: z
|
|
210
|
-
.enum(["user_repo", "account_wide", "user_private"])
|
|
211
|
-
.optional()
|
|
212
|
-
.describe("Visibility scope: user_repo (default), account_wide (cross-repo), user_private"),
|
|
213
|
-
}, async (params) => {
|
|
214
|
-
try {
|
|
215
|
-
const result = await client.getLearnings({
|
|
216
|
-
category: params.category,
|
|
217
|
-
status: params.status,
|
|
218
|
-
min_confidence: params.min_confidence,
|
|
219
|
-
limit: params.limit,
|
|
220
|
-
visibility: params.visibility,
|
|
221
|
-
});
|
|
222
|
-
if (!result.learnings || result.learnings.length === 0) {
|
|
223
|
-
return {
|
|
224
|
-
content: [
|
|
225
|
-
{
|
|
226
|
-
type: "text",
|
|
227
|
-
text: "No learnings found matching the filters. " +
|
|
228
|
-
"Learnings are generated when the consolidation engine processes reasoning events.",
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
const lines = [
|
|
234
|
-
`## Learnings (${result.total} total, showing ${result.learnings.length})`,
|
|
235
|
-
"",
|
|
236
|
-
];
|
|
237
|
-
for (const l of result.learnings) {
|
|
238
|
-
const confidence = ((l.confidence || 0) * 100).toFixed(0);
|
|
239
|
-
lines.push(`### ${l.summary}`);
|
|
240
|
-
lines.push(`**Confidence:** ${confidence}% | **Category:** ${l.category || "general"} | **Status:** ${l.status || "active"}`);
|
|
241
|
-
if (l.evidence_count > 1) {
|
|
242
|
-
lines.push(`**Evidence:** ${l.evidence_count} source(s)`);
|
|
95
|
+
// ─── Tool definitions ─────────────────────────────────────────────────
|
|
96
|
+
export function buildReadTools(client) {
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each tool has its own specific schema;
|
|
98
|
+
// the variance between ToolDef<SpecificShape> and ToolDef<ZodRawShape> is safe at runtime.
|
|
99
|
+
return [
|
|
100
|
+
// ─── context (THE DIFFERENTIATOR) ──────────────────────────────
|
|
101
|
+
buildTool({
|
|
102
|
+
name: "context",
|
|
103
|
+
description: "Get consolidated institutional knowledge about a topic. " +
|
|
104
|
+
"Returns ranked Learnings derived from cross-source consolidation " +
|
|
105
|
+
"(agent reasoning, code, docs, meetings, tickets, chat). " +
|
|
106
|
+
"Each learning has a relevance score, confidence, and evidence count. " +
|
|
107
|
+
"Falls back to raw events if no learnings exist.",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
topic: z
|
|
110
|
+
.string()
|
|
111
|
+
.describe("Topic to query (e.g., 'auth', 'billing', 'deployment')"),
|
|
112
|
+
file_patterns: z
|
|
113
|
+
.string()
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("Comma-separated file patterns to boost matching"),
|
|
116
|
+
limit: z.number().optional().default(10).describe("Max results"),
|
|
117
|
+
min_confidence: z
|
|
118
|
+
.number()
|
|
119
|
+
.optional()
|
|
120
|
+
.default(0.3)
|
|
121
|
+
.describe("Minimum confidence threshold"),
|
|
122
|
+
visibility: z
|
|
123
|
+
.enum(["user_repo", "account_wide", "user_private"])
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Visibility scope: user_repo (default), account_wide (cross-repo), user_private"),
|
|
126
|
+
},
|
|
127
|
+
isReadOnly: true,
|
|
128
|
+
call: async (params) => {
|
|
129
|
+
analytics.track("tool.invoked", { tool: "context" });
|
|
130
|
+
try {
|
|
131
|
+
validateInput(params.topic, 100_000);
|
|
132
|
+
// 1. Try hybrid search first (BM25 + vector + RRF)
|
|
133
|
+
const hybridResult = await client.hybridSearch(params.topic, {
|
|
134
|
+
file_patterns: params.file_patterns,
|
|
135
|
+
limit: params.limit,
|
|
136
|
+
min_confidence: params.min_confidence,
|
|
137
|
+
visibility: params.visibility,
|
|
138
|
+
});
|
|
139
|
+
if (hybridResult.results.length > 0) {
|
|
140
|
+
const text = formatHybridResults(params.topic, hybridResult.results, hybridResult.total_candidates, hybridResult.pipeline);
|
|
141
|
+
return { content: [{ type: "text", text }] };
|
|
142
|
+
}
|
|
143
|
+
// 2. Fall back to legacy context endpoint
|
|
144
|
+
const contextResult = await client.getContext(params.topic, {
|
|
145
|
+
file_patterns: params.file_patterns,
|
|
146
|
+
limit: params.limit,
|
|
147
|
+
min_confidence: params.min_confidence,
|
|
148
|
+
visibility: params.visibility,
|
|
149
|
+
});
|
|
150
|
+
if (contextResult.learnings.length > 0) {
|
|
151
|
+
const text = formatContextLearnings(params.topic, contextResult.learnings, contextResult.total_matched);
|
|
152
|
+
return { content: [{ type: "text", text }] };
|
|
153
|
+
}
|
|
154
|
+
// 3. Fall back to raw event query
|
|
155
|
+
const rawResult = await client.query({
|
|
156
|
+
area: params.topic,
|
|
157
|
+
});
|
|
158
|
+
if (rawResult.results.length > 0) {
|
|
159
|
+
const text = formatEvents(rawResult.results);
|
|
160
|
+
return {
|
|
161
|
+
content: [
|
|
162
|
+
{
|
|
163
|
+
type: "text",
|
|
164
|
+
text: `## Raw Events: ${params.topic}\n\n> No consolidated learnings yet. Showing ${rawResult.source_count} raw event(s).\n\n${text}`,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: `No knowledge found for topic "${params.topic}". This area hasn't been explored yet.`,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
243
177
|
}
|
|
244
|
-
|
|
245
|
-
|
|
178
|
+
catch (error) {
|
|
179
|
+
analytics.track("tool.error", {
|
|
180
|
+
tool: "context",
|
|
181
|
+
error: toSafeTelemetryMessage(error),
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
content: [{ type: "text", text: toUserMessage(error) }],
|
|
185
|
+
isError: true,
|
|
186
|
+
};
|
|
246
187
|
}
|
|
247
|
-
|
|
248
|
-
|
|
188
|
+
},
|
|
189
|
+
}),
|
|
190
|
+
// ─── learnings ─────────────────────────────────────────────────
|
|
191
|
+
buildTool({
|
|
192
|
+
name: "learnings",
|
|
193
|
+
description: "Browse institutional knowledge. Filter by category, status, " +
|
|
194
|
+
"confidence, or repo. Returns consolidated learnings with evidence counts.",
|
|
195
|
+
inputSchema: {
|
|
196
|
+
category: z
|
|
197
|
+
.enum([
|
|
198
|
+
"architecture",
|
|
199
|
+
"bug_pattern",
|
|
200
|
+
"convention",
|
|
201
|
+
"decision",
|
|
202
|
+
"performance",
|
|
203
|
+
"security",
|
|
204
|
+
"workflow",
|
|
205
|
+
"domain_knowledge",
|
|
206
|
+
])
|
|
207
|
+
.optional()
|
|
208
|
+
.describe("Filter by learning category"),
|
|
209
|
+
status: z
|
|
210
|
+
.enum(["active", "reinforced", "contradicted", "obsolete"])
|
|
211
|
+
.optional()
|
|
212
|
+
.describe("Filter by learning status"),
|
|
213
|
+
min_confidence: z
|
|
214
|
+
.number()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe("Minimum confidence (0.0-1.0)"),
|
|
217
|
+
limit: z.number().optional().default(30).describe("Max results"),
|
|
218
|
+
visibility: z
|
|
219
|
+
.enum(["user_repo", "account_wide", "user_private"])
|
|
220
|
+
.optional()
|
|
221
|
+
.describe("Visibility scope: user_repo (default), account_wide (cross-repo), user_private"),
|
|
222
|
+
},
|
|
223
|
+
isReadOnly: true,
|
|
224
|
+
call: async (params) => {
|
|
225
|
+
analytics.track("tool.invoked", { tool: "learnings" });
|
|
226
|
+
try {
|
|
227
|
+
const result = await client.getLearnings({
|
|
228
|
+
category: params.category,
|
|
229
|
+
status: params.status,
|
|
230
|
+
min_confidence: params.min_confidence,
|
|
231
|
+
limit: params.limit,
|
|
232
|
+
visibility: params.visibility,
|
|
233
|
+
});
|
|
234
|
+
if (!result.learnings || result.learnings.length === 0) {
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: "No learnings found matching the filters. " +
|
|
240
|
+
"Learnings are generated when the consolidation engine processes reasoning events.",
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const lines = [
|
|
246
|
+
`## Learnings (${result.total} total, showing ${result.learnings.length})`,
|
|
247
|
+
"",
|
|
248
|
+
];
|
|
249
|
+
for (const l of result.learnings) {
|
|
250
|
+
const confidence = ((l.confidence || 0) * 100).toFixed(0);
|
|
251
|
+
lines.push(`### ${l.summary}`);
|
|
252
|
+
lines.push(`**Confidence:** ${confidence}% | **Category:** ${l.category || "general"} | **Status:** ${l.status || "active"}`);
|
|
253
|
+
if (l.evidence_count > 1) {
|
|
254
|
+
lines.push(`**Evidence:** ${l.evidence_count} source(s)`);
|
|
255
|
+
}
|
|
256
|
+
if (l.suggested_action) {
|
|
257
|
+
lines.push(`**Action:** ${l.suggested_action}`);
|
|
258
|
+
}
|
|
259
|
+
if (l.file_patterns?.length > 0) {
|
|
260
|
+
lines.push(`**Files:** ${l.file_patterns.join(", ")}`);
|
|
261
|
+
}
|
|
262
|
+
lines.push("");
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
266
|
+
};
|
|
249
267
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
try {
|
|
331
|
-
// Fetch learnings to rank backlog items by relevance to institutional knowledge
|
|
332
|
-
const learningsResult = await client.getLearnings({
|
|
333
|
-
sort_by: "confidence",
|
|
334
|
-
sort_order: "desc",
|
|
335
|
-
});
|
|
336
|
-
const learnings = learningsResult.learnings || [];
|
|
337
|
-
// Score each backlog item by how many learnings mention related terms
|
|
338
|
-
const scored = params.backlog.map((task) => {
|
|
339
|
-
const taskLower = task.toLowerCase();
|
|
340
|
-
const keywords = taskLower.split(/\s+/).filter((w) => w.length > 3);
|
|
341
|
-
let score = 0;
|
|
342
|
-
const reasons = [];
|
|
343
|
-
for (const l of learnings) {
|
|
344
|
-
const summary = (l.summary || "").toLowerCase();
|
|
345
|
-
const tags = (l.tags || []).join(" ").toLowerCase();
|
|
346
|
-
const matched = keywords.filter((kw) => summary.includes(kw) || tags.includes(kw));
|
|
347
|
-
if (matched.length > 0) {
|
|
348
|
-
score += (l.confidence ?? 0.5) * matched.length;
|
|
349
|
-
reasons.push(`${((l.confidence ?? 0) * 100).toFixed(0)}% confidence learning: "${(l.summary || "").slice(0, 80)}..."`);
|
|
268
|
+
catch (error) {
|
|
269
|
+
analytics.track("tool.error", {
|
|
270
|
+
tool: "learnings",
|
|
271
|
+
error: toSafeTelemetryMessage(error),
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
content: [{ type: "text", text: toUserMessage(error) }],
|
|
275
|
+
isError: true,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
}),
|
|
280
|
+
// ─── decisions ─────────────────────────────────────────────────
|
|
281
|
+
buildTool({
|
|
282
|
+
name: "decisions",
|
|
283
|
+
description: "Get the decision log, optionally filtered by module and timeframe",
|
|
284
|
+
inputSchema: {
|
|
285
|
+
module: z.string().optional().describe("Filter by module/area"),
|
|
286
|
+
timeframe: z
|
|
287
|
+
.string()
|
|
288
|
+
.optional()
|
|
289
|
+
.describe("Time filter (e.g., '7d', '24h')"),
|
|
290
|
+
},
|
|
291
|
+
isReadOnly: true,
|
|
292
|
+
call: async (params) => {
|
|
293
|
+
analytics.track("tool.invoked", { tool: "decisions" });
|
|
294
|
+
try {
|
|
295
|
+
if (params.module)
|
|
296
|
+
validateInput(params.module, 1000);
|
|
297
|
+
const result = await client.query({
|
|
298
|
+
event_type: "decision",
|
|
299
|
+
module: params.module,
|
|
300
|
+
timeframe: params.timeframe,
|
|
301
|
+
});
|
|
302
|
+
const text = formatEvents(result.results);
|
|
303
|
+
return {
|
|
304
|
+
content: [
|
|
305
|
+
{
|
|
306
|
+
type: "text",
|
|
307
|
+
text: `## Decision log${params.module ? ` for "${params.module}"` : ""}\n\n${text}`,
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
analytics.track("tool.error", {
|
|
314
|
+
tool: "decisions",
|
|
315
|
+
error: toSafeTelemetryMessage(error),
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
content: [{ type: "text", text: toUserMessage(error) }],
|
|
319
|
+
isError: true,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
}),
|
|
324
|
+
// ─── patterns ──────────────────────────────────────────────────
|
|
325
|
+
buildTool({
|
|
326
|
+
name: "patterns",
|
|
327
|
+
description: "Get recurring insights and patterns identified across agent sessions",
|
|
328
|
+
inputSchema: {},
|
|
329
|
+
isReadOnly: true,
|
|
330
|
+
call: async () => {
|
|
331
|
+
analytics.track("tool.invoked", { tool: "patterns" });
|
|
332
|
+
try {
|
|
333
|
+
const result = await client.getLearnings({
|
|
334
|
+
min_confidence: 0.5,
|
|
335
|
+
sort_by: "confidence",
|
|
336
|
+
sort_order: "desc",
|
|
337
|
+
});
|
|
338
|
+
const learnings = result.learnings || [];
|
|
339
|
+
if (learnings.length === 0) {
|
|
340
|
+
return {
|
|
341
|
+
content: [
|
|
342
|
+
{
|
|
343
|
+
type: "text",
|
|
344
|
+
text: "No patterns identified yet. Patterns emerge after consolidation runs process reasoning events.",
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
};
|
|
350
348
|
}
|
|
349
|
+
const text = learnings
|
|
350
|
+
.map((l) => `[confidence: ${(l.confidence ?? 0).toFixed(2)}] ${l.summary}\n tags: ${(l.tags ?? []).join(", ") || "none"}\n files: ${(l.file_patterns ?? []).join(", ") || "none"}`)
|
|
351
|
+
.join("\n\n");
|
|
352
|
+
return {
|
|
353
|
+
content: [
|
|
354
|
+
{
|
|
355
|
+
type: "text",
|
|
356
|
+
text: `## Patterns\n\n${text}`,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
};
|
|
351
360
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.join("\n")
|
|
362
|
-
: ` _No matching learnings yet_`))
|
|
363
|
-
.join("\n\n");
|
|
364
|
-
return {
|
|
365
|
-
content: [
|
|
366
|
-
{
|
|
367
|
-
type: "text",
|
|
368
|
-
text: `## Suggested priorities\n\n${text}`,
|
|
369
|
-
},
|
|
370
|
-
],
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
catch (error) {
|
|
374
|
-
return {
|
|
375
|
-
content: [{ type: "text", text: `Error: ${error}` }],
|
|
376
|
-
isError: true,
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
// ─── backlog ───────────────────────────────────────────────────
|
|
381
|
-
server.tool("backlog", "Get the auto-generated agentic product backlog. " +
|
|
382
|
-
"PBIs are generated by the consolidation engine from high-confidence learnings. " +
|
|
383
|
-
"Each PBI includes evidence summary, suggested approach, and full context.", {
|
|
384
|
-
status: z
|
|
385
|
-
.string()
|
|
386
|
-
.optional()
|
|
387
|
-
.describe("Filter by status: draft, approved, assigned, in_progress, done, rejected"),
|
|
388
|
-
priority: z
|
|
389
|
-
.string()
|
|
390
|
-
.optional()
|
|
391
|
-
.describe("Filter by priority: CRITICAL, HIGH, MEDIUM, LOW"),
|
|
392
|
-
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
393
|
-
max_results: z.number().optional().default(20),
|
|
394
|
-
}, async (params) => {
|
|
395
|
-
try {
|
|
396
|
-
const backlog = await client.getBacklog({
|
|
397
|
-
status: params.status,
|
|
398
|
-
priority: params.priority,
|
|
399
|
-
tags: params.tags?.join(","),
|
|
400
|
-
limit: params.max_results,
|
|
401
|
-
});
|
|
402
|
-
if (!backlog || backlog.length === 0) {
|
|
403
|
-
return {
|
|
404
|
-
content: [
|
|
405
|
-
{
|
|
406
|
-
type: "text",
|
|
407
|
-
text: "Backlog is empty. PBIs are auto-generated when the consolidation engine " +
|
|
408
|
-
"runs and identifies actionable insights from reasoning events.",
|
|
409
|
-
},
|
|
410
|
-
],
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
const lines = [
|
|
414
|
-
`## Agentic Product Backlog`,
|
|
415
|
-
``,
|
|
416
|
-
`> ${backlog.length} item(s) | Auto-generated from consolidated learnings`,
|
|
417
|
-
``,
|
|
418
|
-
];
|
|
419
|
-
for (const item of backlog) {
|
|
420
|
-
lines.push(`### [${item.priority}] ${item.title}`);
|
|
421
|
-
lines.push(`**Status:** ${item.status} | **Scope:** ${item.scope}`);
|
|
422
|
-
if (item.description)
|
|
423
|
-
lines.push(item.description);
|
|
424
|
-
if (item.evidence_summary)
|
|
425
|
-
lines.push(`**Evidence:** ${item.evidence_summary}`);
|
|
426
|
-
if (item.suggested_approach)
|
|
427
|
-
lines.push(`**Approach:** ${item.suggested_approach}`);
|
|
428
|
-
lines.push("");
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
content: [{ type: "text", text: lines.join("\n") }],
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
catch (error) {
|
|
435
|
-
return {
|
|
436
|
-
content: [
|
|
437
|
-
{
|
|
438
|
-
type: "text",
|
|
439
|
-
text: `Failed to retrieve backlog: ${error}`,
|
|
440
|
-
},
|
|
441
|
-
],
|
|
442
|
-
isError: true,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
// ─── priorities ────────────────────────────────────────────────
|
|
447
|
-
server.tool("priorities", "Get evidence-ranked priorities for what to build next. " +
|
|
448
|
-
"Priorities are derived from consolidated learnings across agent sessions, " +
|
|
449
|
-
"code changes, meetings, tickets, and chat.", {
|
|
450
|
-
current_tasks: z
|
|
451
|
-
.array(z.string())
|
|
452
|
-
.optional()
|
|
453
|
-
.describe("Current task descriptions for context-aware ranking"),
|
|
454
|
-
max_results: z
|
|
455
|
-
.number()
|
|
456
|
-
.optional()
|
|
457
|
-
.default(10)
|
|
458
|
-
.describe("Maximum number of priorities to return"),
|
|
459
|
-
}, async (params) => {
|
|
460
|
-
try {
|
|
461
|
-
const backlog = await client.getBacklog({
|
|
462
|
-
status: "approved",
|
|
463
|
-
limit: params.max_results,
|
|
464
|
-
});
|
|
465
|
-
if (!backlog || backlog.length === 0) {
|
|
466
|
-
return {
|
|
467
|
-
content: [
|
|
468
|
-
{
|
|
469
|
-
type: "text",
|
|
470
|
-
text: "No approved priorities in the backlog. " +
|
|
471
|
-
"Run consolidation to generate PBIs from accumulated reasoning, " +
|
|
472
|
-
"then have a PM approve them for work.",
|
|
473
|
-
},
|
|
474
|
-
],
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
const lines = [
|
|
478
|
-
"## Evidence-Ranked Priorities",
|
|
479
|
-
"",
|
|
480
|
-
"> Derived from consolidated institutional knowledge, not guesswork.",
|
|
481
|
-
"",
|
|
482
|
-
];
|
|
483
|
-
for (let i = 0; i < backlog.length; i++) {
|
|
484
|
-
const item = backlog[i];
|
|
485
|
-
lines.push(`${i + 1}. **[${item.priority}] ${item.title}**`);
|
|
486
|
-
lines.push(` Status: ${item.status} | Scope: ${item.scope}`);
|
|
487
|
-
if (item.evidence_summary) {
|
|
488
|
-
lines.push(` Evidence: ${item.evidence_summary}`);
|
|
361
|
+
catch (error) {
|
|
362
|
+
analytics.track("tool.error", {
|
|
363
|
+
tool: "patterns",
|
|
364
|
+
error: toSafeTelemetryMessage(error),
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
content: [{ type: "text", text: toUserMessage(error) }],
|
|
368
|
+
isError: true,
|
|
369
|
+
};
|
|
489
370
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
"",
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
{
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
371
|
+
},
|
|
372
|
+
}),
|
|
373
|
+
// ─── suggest ───────────────────────────────────────────────────
|
|
374
|
+
buildTool({
|
|
375
|
+
name: "suggest",
|
|
376
|
+
description: "Get ranked task priorities based on accumulated reasoning",
|
|
377
|
+
inputSchema: {
|
|
378
|
+
backlog: z
|
|
379
|
+
.preprocess((v) => (typeof v === "string" ? JSON.parse(v) : v), z.array(z.string()).min(1))
|
|
380
|
+
.describe("List of backlog items to prioritize"),
|
|
381
|
+
},
|
|
382
|
+
isReadOnly: true,
|
|
383
|
+
call: async (params) => {
|
|
384
|
+
analytics.track("tool.invoked", { tool: "suggest" });
|
|
385
|
+
try {
|
|
386
|
+
for (const item of params.backlog) {
|
|
387
|
+
validateInput(item, 10_000);
|
|
388
|
+
}
|
|
389
|
+
const learningsResult = await client.getLearnings({
|
|
390
|
+
sort_by: "confidence",
|
|
391
|
+
sort_order: "desc",
|
|
392
|
+
});
|
|
393
|
+
const learnings = learningsResult.learnings || [];
|
|
394
|
+
const scored = params.backlog.map((task) => {
|
|
395
|
+
const taskLower = task.toLowerCase();
|
|
396
|
+
const keywords = taskLower
|
|
397
|
+
.split(/\s+/)
|
|
398
|
+
.filter((w) => w.length > 3);
|
|
399
|
+
let score = 0;
|
|
400
|
+
const reasons = [];
|
|
401
|
+
for (const l of learnings) {
|
|
402
|
+
const summary = (l.summary || "").toLowerCase();
|
|
403
|
+
const tags = (l.tags || []).join(" ").toLowerCase();
|
|
404
|
+
const matched = keywords.filter((kw) => summary.includes(kw) || tags.includes(kw));
|
|
405
|
+
if (matched.length > 0) {
|
|
406
|
+
score += (l.confidence ?? 0.5) * matched.length;
|
|
407
|
+
reasons.push(`${((l.confidence ?? 0) * 100).toFixed(0)}% confidence learning: "${(l.summary || "").slice(0, 80)}..."`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return { task, score: Math.round(score * 100) / 100, reasons };
|
|
411
|
+
});
|
|
412
|
+
scored.sort((a, b) => b.score - a.score);
|
|
413
|
+
const text = scored
|
|
414
|
+
.map((item, i) => `${i + 1}. **${item.task}** (evidence score: ${item.score})\n` +
|
|
415
|
+
(item.reasons.length > 0
|
|
416
|
+
? item.reasons
|
|
417
|
+
.slice(0, 3)
|
|
418
|
+
.map((r) => ` - ${r}`)
|
|
419
|
+
.join("\n")
|
|
420
|
+
: ` _No matching learnings yet_`))
|
|
421
|
+
.join("\n\n");
|
|
422
|
+
return {
|
|
423
|
+
content: [
|
|
424
|
+
{
|
|
425
|
+
type: "text",
|
|
426
|
+
text: `## Suggested priorities\n\n${text}`,
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
analytics.track("tool.error", {
|
|
433
|
+
tool: "suggest",
|
|
434
|
+
error: toSafeTelemetryMessage(error),
|
|
435
|
+
});
|
|
436
|
+
return {
|
|
437
|
+
content: [{ type: "text", text: toUserMessage(error) }],
|
|
438
|
+
isError: true,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
}),
|
|
443
|
+
// ─── backlog ───────────────────────────────────────────────────
|
|
444
|
+
buildTool({
|
|
445
|
+
name: "backlog",
|
|
446
|
+
description: "Get the auto-generated agentic product backlog. " +
|
|
447
|
+
"PBIs are generated by the consolidation engine from high-confidence learnings. " +
|
|
448
|
+
"Each PBI includes evidence summary, suggested approach, and full context.",
|
|
449
|
+
inputSchema: {
|
|
450
|
+
status: z
|
|
451
|
+
.string()
|
|
452
|
+
.optional()
|
|
453
|
+
.describe("Filter by status: draft, approved, assigned, in_progress, done, rejected"),
|
|
454
|
+
priority: z
|
|
455
|
+
.string()
|
|
456
|
+
.optional()
|
|
457
|
+
.describe("Filter by priority: CRITICAL, HIGH, MEDIUM, LOW"),
|
|
458
|
+
tags: z.array(z.string()).optional().describe("Filter by tags"),
|
|
459
|
+
max_results: z.number().optional().default(20),
|
|
460
|
+
},
|
|
461
|
+
isReadOnly: true,
|
|
462
|
+
call: async (params) => {
|
|
463
|
+
analytics.track("tool.invoked", { tool: "backlog" });
|
|
464
|
+
try {
|
|
465
|
+
const backlog = await client.getBacklog({
|
|
466
|
+
status: params.status,
|
|
467
|
+
priority: params.priority,
|
|
468
|
+
tags: params.tags?.join(","),
|
|
469
|
+
limit: params.max_results,
|
|
470
|
+
});
|
|
471
|
+
if (!backlog || backlog.length === 0) {
|
|
472
|
+
return {
|
|
473
|
+
content: [
|
|
474
|
+
{
|
|
475
|
+
type: "text",
|
|
476
|
+
text: "Backlog is empty. PBIs are auto-generated when the consolidation engine " +
|
|
477
|
+
"runs and identifies actionable insights from reasoning events.",
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
const lines = [
|
|
483
|
+
`## Agentic Product Backlog`,
|
|
484
|
+
``,
|
|
485
|
+
`> ${backlog.length} item(s) | Auto-generated from consolidated learnings`,
|
|
486
|
+
``,
|
|
487
|
+
];
|
|
488
|
+
for (const item of backlog) {
|
|
489
|
+
lines.push(`### [${item.priority}] ${item.title}`);
|
|
490
|
+
lines.push(`**Status:** ${item.status} | **Scope:** ${item.scope}`);
|
|
491
|
+
if (item.description)
|
|
492
|
+
lines.push(item.description);
|
|
493
|
+
if (item.evidence_summary)
|
|
494
|
+
lines.push(`**Evidence:** ${item.evidence_summary}`);
|
|
495
|
+
if (item.suggested_approach)
|
|
496
|
+
lines.push(`**Approach:** ${item.suggested_approach}`);
|
|
497
|
+
lines.push("");
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
analytics.track("tool.error", {
|
|
505
|
+
tool: "backlog",
|
|
506
|
+
error: toSafeTelemetryMessage(error),
|
|
507
|
+
});
|
|
508
|
+
return {
|
|
509
|
+
content: [
|
|
510
|
+
{
|
|
511
|
+
type: "text",
|
|
512
|
+
text: `Failed to retrieve backlog: ${toUserMessage(error)}`,
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
isError: true,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
}),
|
|
520
|
+
// ─── priorities ────────────────────────────────────────────────
|
|
521
|
+
buildTool({
|
|
522
|
+
name: "priorities",
|
|
523
|
+
description: "Get evidence-ranked priorities for what to build next. " +
|
|
524
|
+
"Priorities are derived from consolidated learnings across agent sessions, " +
|
|
525
|
+
"code changes, meetings, tickets, and chat.",
|
|
526
|
+
inputSchema: {
|
|
527
|
+
current_tasks: z
|
|
528
|
+
.array(z.string())
|
|
529
|
+
.optional()
|
|
530
|
+
.describe("Current task descriptions for context-aware ranking"),
|
|
531
|
+
max_results: z
|
|
532
|
+
.number()
|
|
533
|
+
.optional()
|
|
534
|
+
.default(10)
|
|
535
|
+
.describe("Maximum number of priorities to return"),
|
|
536
|
+
},
|
|
537
|
+
isReadOnly: true,
|
|
538
|
+
call: async (params) => {
|
|
539
|
+
analytics.track("tool.invoked", { tool: "priorities" });
|
|
540
|
+
try {
|
|
541
|
+
if (params.current_tasks) {
|
|
542
|
+
for (const task of params.current_tasks) {
|
|
543
|
+
validateInput(task, 10_000);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const backlog = await client.getBacklog({
|
|
547
|
+
status: "approved",
|
|
548
|
+
limit: params.max_results,
|
|
549
|
+
});
|
|
550
|
+
if (!backlog || backlog.length === 0) {
|
|
551
|
+
return {
|
|
552
|
+
content: [
|
|
553
|
+
{
|
|
554
|
+
type: "text",
|
|
555
|
+
text: "No approved priorities in the backlog. " +
|
|
556
|
+
"Run consolidation to generate PBIs from accumulated reasoning, " +
|
|
557
|
+
"then have a PM approve them for work.",
|
|
558
|
+
},
|
|
559
|
+
],
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
const lines = [
|
|
563
|
+
"## Evidence-Ranked Priorities",
|
|
564
|
+
"",
|
|
565
|
+
"> Derived from consolidated institutional knowledge, not guesswork.",
|
|
566
|
+
"",
|
|
567
|
+
];
|
|
568
|
+
for (let i = 0; i < backlog.length; i++) {
|
|
569
|
+
const item = backlog[i];
|
|
570
|
+
lines.push(`${i + 1}. **[${item.priority}] ${item.title}**`);
|
|
571
|
+
lines.push(` Status: ${item.status} | Scope: ${item.scope}`);
|
|
572
|
+
if (item.evidence_summary) {
|
|
573
|
+
lines.push(` Evidence: ${item.evidence_summary}`);
|
|
574
|
+
}
|
|
575
|
+
lines.push("");
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
catch (error) {
|
|
582
|
+
analytics.track("tool.error", {
|
|
583
|
+
tool: "priorities",
|
|
584
|
+
error: toSafeTelemetryMessage(error),
|
|
585
|
+
});
|
|
586
|
+
return {
|
|
587
|
+
content: [
|
|
588
|
+
{
|
|
589
|
+
type: "text",
|
|
590
|
+
text: `Failed to retrieve priorities: ${toUserMessage(error)}`,
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
isError: true,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
}),
|
|
598
|
+
// ─── semantic_search ─────────────────────────────────────────
|
|
599
|
+
buildTool({
|
|
600
|
+
name: "semantic_search",
|
|
601
|
+
description: "Search institutional knowledge using natural language. " +
|
|
602
|
+
"Uses vector embeddings for semantic similarity — finds learnings " +
|
|
603
|
+
"that are conceptually related to your query even if they don't share " +
|
|
604
|
+
"exact keywords. Requires the embedding provider to be configured.",
|
|
605
|
+
inputSchema: {
|
|
606
|
+
query: z
|
|
607
|
+
.string()
|
|
608
|
+
.describe("Natural language query (e.g., 'authentication token refresh')"),
|
|
609
|
+
limit: z.number().optional().default(10).describe("Max results"),
|
|
610
|
+
min_similarity: z
|
|
611
|
+
.number()
|
|
612
|
+
.optional()
|
|
613
|
+
.default(0.4)
|
|
614
|
+
.describe("Minimum cosine similarity threshold (0.0–1.0)"),
|
|
615
|
+
visibility: z
|
|
616
|
+
.enum(["user_repo", "account_wide", "user_private"])
|
|
617
|
+
.optional()
|
|
618
|
+
.describe("Visibility scope: user_repo (default, repo-scoped), " +
|
|
619
|
+
"account_wide (cross-repo within account), " +
|
|
620
|
+
"user_private (personal learnings only)"),
|
|
621
|
+
},
|
|
622
|
+
isReadOnly: true,
|
|
623
|
+
call: async (params) => {
|
|
624
|
+
analytics.track("tool.invoked", { tool: "semantic_search" });
|
|
625
|
+
try {
|
|
626
|
+
validateInput(params.query, 100_000);
|
|
627
|
+
// Try hybrid search first (BM25 + vector + RRF fusion)
|
|
628
|
+
const hybridResult = await client
|
|
629
|
+
.hybridSearch(params.query, {
|
|
630
|
+
limit: params.limit,
|
|
631
|
+
min_similarity: params.min_similarity,
|
|
632
|
+
visibility: params.visibility,
|
|
633
|
+
})
|
|
634
|
+
.catch(() => null);
|
|
635
|
+
if (hybridResult && hybridResult.results.length > 0) {
|
|
636
|
+
return {
|
|
637
|
+
content: [
|
|
638
|
+
{
|
|
639
|
+
type: "text",
|
|
640
|
+
text: formatHybridResults(params.query, hybridResult.results, hybridResult.total_candidates, hybridResult.pipeline),
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
// Fall back to legacy semantic search
|
|
646
|
+
const result = await client.semanticSearch(params.query, {
|
|
647
|
+
limit: params.limit,
|
|
648
|
+
min_similarity: params.min_similarity,
|
|
649
|
+
visibility: params.visibility,
|
|
650
|
+
});
|
|
651
|
+
if (result.results.length === 0) {
|
|
652
|
+
return {
|
|
653
|
+
content: [
|
|
654
|
+
{
|
|
655
|
+
type: "text",
|
|
656
|
+
text: `No semantically similar learnings found for "${params.query}". ` +
|
|
657
|
+
"The embedding provider may not be configured, or no learnings have been embedded yet.",
|
|
658
|
+
},
|
|
659
|
+
],
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
const lines = [
|
|
663
|
+
`## Semantic Search: ${params.query}`,
|
|
664
|
+
"",
|
|
665
|
+
`> ${result.total} result(s) found.`,
|
|
666
|
+
"",
|
|
667
|
+
];
|
|
668
|
+
for (const r of result.results) {
|
|
669
|
+
const sim = (r.similarity * 100).toFixed(0);
|
|
670
|
+
lines.push(`- **[${sim}%]** ${r.summary}`);
|
|
671
|
+
lines.push(` *ID: ${r.id}*`);
|
|
672
|
+
lines.push("");
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
catch (error) {
|
|
679
|
+
analytics.track("tool.error", {
|
|
680
|
+
tool: "semantic_search",
|
|
681
|
+
error: toSafeTelemetryMessage(error),
|
|
682
|
+
});
|
|
683
|
+
return {
|
|
684
|
+
content: [
|
|
685
|
+
{
|
|
686
|
+
type: "text",
|
|
687
|
+
text: `Semantic search failed: ${toUserMessage(error)}`,
|
|
688
|
+
},
|
|
689
|
+
],
|
|
690
|
+
isError: true,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
}),
|
|
695
|
+
// ─── consolidate ──────────────────────────────────────────────
|
|
696
|
+
buildTool({
|
|
697
|
+
name: "consolidate",
|
|
698
|
+
description: "Trigger a consolidation run to process recent reasoning events " +
|
|
699
|
+
"and derive new learnings and backlog items. Use this when you want " +
|
|
700
|
+
"to refresh the institutional knowledge base.",
|
|
701
|
+
inputSchema: {
|
|
702
|
+
max_events: z.number().optional().describe("Maximum events to process"),
|
|
703
|
+
},
|
|
704
|
+
isReadOnly: false,
|
|
705
|
+
isConcurrencySafe: false,
|
|
706
|
+
call: async (params) => {
|
|
707
|
+
analytics.track("tool.invoked", { tool: "consolidate" });
|
|
708
|
+
try {
|
|
709
|
+
const result = await client.triggerConsolidation({
|
|
710
|
+
repo: "",
|
|
711
|
+
trigger: "manual",
|
|
712
|
+
max_events: params.max_events,
|
|
713
|
+
});
|
|
714
|
+
const lines = [
|
|
715
|
+
"## Consolidation Complete",
|
|
716
|
+
"",
|
|
717
|
+
`**Run ID:** ${result.run_id}`,
|
|
718
|
+
`**Events processed:** ${result.events_processed}`,
|
|
719
|
+
`**New learnings:** ${result.new_learnings}`,
|
|
720
|
+
`**Reinforced:** ${result.reinforced}`,
|
|
721
|
+
`**Contradicted:** ${result.contradicted}`,
|
|
722
|
+
`**Backlog items generated:** ${result.backlog_items_generated}`,
|
|
723
|
+
];
|
|
724
|
+
if (result.model_id) {
|
|
725
|
+
lines.push(`**Model:** ${result.model_id}`);
|
|
726
|
+
}
|
|
727
|
+
if (result.input_tokens) {
|
|
728
|
+
lines.push(`**Tokens:** ${result.input_tokens} in / ${result.output_tokens} out`);
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
catch (error) {
|
|
735
|
+
analytics.track("tool.error", {
|
|
736
|
+
tool: "consolidate",
|
|
737
|
+
error: toSafeTelemetryMessage(error),
|
|
738
|
+
});
|
|
739
|
+
return {
|
|
740
|
+
content: [
|
|
741
|
+
{
|
|
742
|
+
type: "text",
|
|
743
|
+
text: `Consolidation failed: ${toUserMessage(error)}`,
|
|
744
|
+
},
|
|
745
|
+
],
|
|
746
|
+
isError: true,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
}),
|
|
751
|
+
];
|
|
752
|
+
}
|
|
753
|
+
// ─── Registration ─────────────────────────────────────────────────────
|
|
754
|
+
/**
|
|
755
|
+
* Register read tools on the MCP server.
|
|
756
|
+
*/
|
|
757
|
+
export function registerReadTools(server, client) {
|
|
758
|
+
registerToolPool(buildReadTools(client), server);
|
|
634
759
|
}
|