@contextq/mcp 2.0.0 → 2.1.1
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/README.md +8 -8
- package/dist/index.js +7 -209
- package/package.json +16 -16
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/tools.d.ts +0 -23
- package/dist/tools.js +0 -2543
- package/dist/tools.js.map +0 -1
- package/dist/tools.profile.test.d.ts +0 -14
- package/dist/tools.profile.test.js +0 -133
- package/dist/tools.profile.test.js.map +0 -1
- package/dist/tools.trace.test.d.ts +0 -11
- package/dist/tools.trace.test.js +0 -75
- package/dist/tools.trace.test.js.map +0 -1
- package/dist/trace-context.d.ts +0 -44
- package/dist/trace-context.js +0 -90
- package/dist/trace-context.js.map +0 -1
- package/dist/trace-context.test.d.ts +0 -6
- package/dist/trace-context.test.js +0 -86
- package/dist/trace-context.test.js.map +0 -1
package/dist/tools.js
DELETED
|
@@ -1,2543 +0,0 @@
|
|
|
1
|
-
import { extractTraceContext, traceContextStorage } from "./trace-context.js";
|
|
2
|
-
// ---------------------------------------------------------------------------
|
|
3
|
-
// Helpers
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
function apiUrl(path) {
|
|
6
|
-
const base = process.env.CONTEXT_API_URL ?? "http://localhost:38200";
|
|
7
|
-
return `${base.replace(/\/+$/, "")}${path}`;
|
|
8
|
-
}
|
|
9
|
-
function toQueryString(params) {
|
|
10
|
-
const qs = new URLSearchParams();
|
|
11
|
-
for (const [key, value] of Object.entries(params)) {
|
|
12
|
-
if (value === undefined || value === null)
|
|
13
|
-
continue;
|
|
14
|
-
if (Array.isArray(value)) {
|
|
15
|
-
for (const v of value)
|
|
16
|
-
qs.append(key, String(v));
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
qs.set(key, String(value));
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
const str = qs.toString();
|
|
23
|
-
return str ? `?${str}` : "";
|
|
24
|
-
}
|
|
25
|
-
async function request(method, path, body) {
|
|
26
|
-
const headers = { "Content-Type": "application/json" };
|
|
27
|
-
const apiKey = process.env.CONTEXT_API_KEY;
|
|
28
|
-
if (apiKey) {
|
|
29
|
-
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
30
|
-
}
|
|
31
|
-
// T558: forward the validated W3C trace context (when the calling tool's
|
|
32
|
-
// `_meta` carried one) as real HTTP headers on the outgoing API call --
|
|
33
|
-
// ambient via AsyncLocalStorage (see trace-context.ts) rather than a new
|
|
34
|
-
// parameter on all ~90 call sites of this function. Absent on every call
|
|
35
|
-
// today (no known MCP client sends these `_meta` keys yet) -- the store
|
|
36
|
-
// is null, these three lines never run, headers unchanged from before
|
|
37
|
-
// this task.
|
|
38
|
-
const traceContext = traceContextStorage.getStore();
|
|
39
|
-
if (traceContext) {
|
|
40
|
-
headers["traceparent"] = traceContext.traceparent;
|
|
41
|
-
if (traceContext.tracestate)
|
|
42
|
-
headers["tracestate"] = traceContext.tracestate;
|
|
43
|
-
if (traceContext.baggage)
|
|
44
|
-
headers["baggage"] = traceContext.baggage;
|
|
45
|
-
}
|
|
46
|
-
const options = {
|
|
47
|
-
method,
|
|
48
|
-
headers,
|
|
49
|
-
redirect: 'manual',
|
|
50
|
-
};
|
|
51
|
-
if (body !== undefined) {
|
|
52
|
-
options.body = JSON.stringify(body);
|
|
53
|
-
}
|
|
54
|
-
const res = await fetch(apiUrl(path), options);
|
|
55
|
-
if (res.status >= 300 && res.status < 400) {
|
|
56
|
-
throw new Error(`Unexpected redirect from API (${res.status}) for ${method} ${path}`);
|
|
57
|
-
}
|
|
58
|
-
const text = await res.text();
|
|
59
|
-
if (!res.ok) {
|
|
60
|
-
throw new Error(`API ${method} ${path} returned ${res.status}: ${text}`);
|
|
61
|
-
}
|
|
62
|
-
return text ? JSON.parse(text) : null;
|
|
63
|
-
}
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// Tool definitions
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
const CONTEXT_TYPE_ENUM = [
|
|
68
|
-
"reference",
|
|
69
|
-
"feedback",
|
|
70
|
-
"project",
|
|
71
|
-
"incident",
|
|
72
|
-
"lesson",
|
|
73
|
-
"user",
|
|
74
|
-
"synthesis",
|
|
75
|
-
"map",
|
|
76
|
-
"moc",
|
|
77
|
-
];
|
|
78
|
-
const CONTEXT_SCOPE_ENUM = ["personal", "workspace", "team"];
|
|
79
|
-
// T344: memory taxonomy axis — episodic/semantic/procedural. Orthogonal to
|
|
80
|
-
// memoryClass (action/knowledge). Defaults to a write-time heuristic
|
|
81
|
-
// classification (by context type) on the server when omitted.
|
|
82
|
-
const MEMORY_KIND_ENUM = ["episodic", "semantic", "procedural"];
|
|
83
|
-
const LIFECYCLE_ENUM = [
|
|
84
|
-
"fleeting",
|
|
85
|
-
"working",
|
|
86
|
-
"evergreen",
|
|
87
|
-
"archived",
|
|
88
|
-
];
|
|
89
|
-
const LINK_TYPE_ENUM = [
|
|
90
|
-
"supersedes",
|
|
91
|
-
"contradicts",
|
|
92
|
-
"implements",
|
|
93
|
-
"derived_from",
|
|
94
|
-
"related_to",
|
|
95
|
-
];
|
|
96
|
-
const LINK_DIRECTION_ENUM = ["outbound", "inbound", "both"];
|
|
97
|
-
export const tools = [
|
|
98
|
-
{
|
|
99
|
-
name: "ctx_save",
|
|
100
|
-
description: "Save a new context entry (reference doc, feedback, project note, incident report, lesson learned, or user profile). Use this when you want to persist knowledge for future retrieval. Optional lifecycle/valid_from/valid_to flag the note's maturity and bi-temporal validity. Response includes atomic, quality_score, and lifecycle once the backend judge has run. Trigger: user asks you to remember/save something (\"nhớ cái này\", \"lưu lại\", \"ghi nhớ giúp\", \"remember this\", \"save this\", \"note this down\") — call whenever work-relevant info should persist across sessions. Only call when the request is actually about tracked work/memory; ignore unrelated casual chat.",
|
|
101
|
-
inputSchema: {
|
|
102
|
-
type: "object",
|
|
103
|
-
properties: {
|
|
104
|
-
name: { type: "string", description: "Short, descriptive title" },
|
|
105
|
-
description: {
|
|
106
|
-
type: "string",
|
|
107
|
-
description: "One-line summary used for search ranking",
|
|
108
|
-
},
|
|
109
|
-
content: {
|
|
110
|
-
type: "string",
|
|
111
|
-
description: "Full content / body of the context entry",
|
|
112
|
-
},
|
|
113
|
-
type: {
|
|
114
|
-
type: "string",
|
|
115
|
-
enum: [...CONTEXT_TYPE_ENUM],
|
|
116
|
-
description: "Category of the context entry",
|
|
117
|
-
},
|
|
118
|
-
scope: {
|
|
119
|
-
type: "string",
|
|
120
|
-
enum: [...CONTEXT_SCOPE_ENUM],
|
|
121
|
-
description: "Visibility scope (defaults to personal)",
|
|
122
|
-
},
|
|
123
|
-
workspace: {
|
|
124
|
-
type: "string",
|
|
125
|
-
description: "Workspace identifier that owns this context",
|
|
126
|
-
},
|
|
127
|
-
project: {
|
|
128
|
-
type: "string",
|
|
129
|
-
description: "Optional project identifier within the workspace",
|
|
130
|
-
},
|
|
131
|
-
tags: {
|
|
132
|
-
type: "array",
|
|
133
|
-
items: { type: "string" },
|
|
134
|
-
description: "Tags for categorization and filtering",
|
|
135
|
-
},
|
|
136
|
-
metadata: {
|
|
137
|
-
type: "object",
|
|
138
|
-
description: "Arbitrary key-value metadata",
|
|
139
|
-
},
|
|
140
|
-
lifecycle: {
|
|
141
|
-
type: "string",
|
|
142
|
-
enum: [...LIFECYCLE_ENUM],
|
|
143
|
-
description: "Lifecycle state of the note. Omit to let the backend default to 'working'. Use 'fleeting' for transient captures, 'evergreen' for durable knowledge, 'archived' to retire from active surfacing.",
|
|
144
|
-
},
|
|
145
|
-
memoryKind: {
|
|
146
|
-
type: "string",
|
|
147
|
-
enum: [...MEMORY_KIND_ENUM],
|
|
148
|
-
description: "Taxonomy override: 'episodic' (an event/interaction happened), 'semantic' (durable factual/reference knowledge), or 'procedural' (how-to / lesson that changes future behavior). Omit to let the backend classify it from the context type (a cheap heuristic, optionally refined by the atomicity judge).",
|
|
149
|
-
},
|
|
150
|
-
valid_from: {
|
|
151
|
-
type: "string",
|
|
152
|
-
description: "Bi-temporal: ISO 8601 timestamp when the fact this context describes started being true. Optional.",
|
|
153
|
-
},
|
|
154
|
-
valid_to: {
|
|
155
|
-
type: "string",
|
|
156
|
-
description: "Bi-temporal: ISO 8601 timestamp when the fact stopped being true. Optional; null means still valid.",
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
required: ["name", "description", "content", "type", "workspace"],
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: "ctx_search",
|
|
164
|
-
description: "Search / recall saved knowledge and memories using hybrid full-text + semantic search ranked by relevance — the default tool for 'what do I know about X' or 'did I already save this'. Use this when you need to find, remember, or look up existing knowledge by keyword or phrase. Set chunk_search=false to disable per-chunk passage matching, lifecycle_boost=false for legacy ranking, or include_archived=true to surface retired notes. Results may include lifecycle, quality_score, and matched_chunk per hit.",
|
|
165
|
-
inputSchema: {
|
|
166
|
-
type: "object",
|
|
167
|
-
properties: {
|
|
168
|
-
query: {
|
|
169
|
-
type: "string",
|
|
170
|
-
description: "Search query (full-text + semantic)",
|
|
171
|
-
},
|
|
172
|
-
workspace: {
|
|
173
|
-
type: "string",
|
|
174
|
-
description: "Filter by workspace identifier",
|
|
175
|
-
},
|
|
176
|
-
project: {
|
|
177
|
-
type: "string",
|
|
178
|
-
description: "Filter by project identifier",
|
|
179
|
-
},
|
|
180
|
-
type: {
|
|
181
|
-
type: "string",
|
|
182
|
-
enum: [...CONTEXT_TYPE_ENUM],
|
|
183
|
-
description: "Filter by context type",
|
|
184
|
-
},
|
|
185
|
-
tags: {
|
|
186
|
-
type: "array",
|
|
187
|
-
items: { type: "string" },
|
|
188
|
-
description: "Filter by tags (all must match)",
|
|
189
|
-
},
|
|
190
|
-
scope: {
|
|
191
|
-
type: "string",
|
|
192
|
-
enum: [...CONTEXT_SCOPE_ENUM],
|
|
193
|
-
description: "Filter by visibility scope",
|
|
194
|
-
},
|
|
195
|
-
subjectId: {
|
|
196
|
-
type: "string",
|
|
197
|
-
description: "Filter to memories scoped to a single end-user (Mem0-parity user_id axis). Matches the subjectId used when the memory was created via ctx_remember / POST /api/memory. Omit to search across all subjects.",
|
|
198
|
-
},
|
|
199
|
-
memoryKind: {
|
|
200
|
-
type: "string",
|
|
201
|
-
enum: [...MEMORY_KIND_ENUM],
|
|
202
|
-
description: "Filter to a single taxonomy kind: 'episodic' (events/interactions), 'semantic' (durable reference knowledge), or 'procedural' (how-to / lessons). Omit to search across all kinds.",
|
|
203
|
-
},
|
|
204
|
-
epistemicMin: {
|
|
205
|
-
type: "string",
|
|
206
|
-
enum: ["observed", "told", "inferred", "assumed"],
|
|
207
|
-
description: "Epistemic floor (T358): only return contexts at or above this confidence tier (weakest->strongest: assumed < inferred < told < observed). E.g. 'told' excludes 'assumed'/'inferred' rows. Omit to search across all tiers.",
|
|
208
|
-
},
|
|
209
|
-
limit: {
|
|
210
|
-
type: "number",
|
|
211
|
-
description: "Max results to return (default 20)",
|
|
212
|
-
},
|
|
213
|
-
offset: {
|
|
214
|
-
type: "number",
|
|
215
|
-
description: "Offset for pagination",
|
|
216
|
-
},
|
|
217
|
-
chunk_search: {
|
|
218
|
-
type: "boolean",
|
|
219
|
-
description: "When true (default), search at the chunk level so individual passages can match. When false, only whole-context fields are scored.",
|
|
220
|
-
},
|
|
221
|
-
lifecycle_boost: {
|
|
222
|
-
type: "boolean",
|
|
223
|
-
description: "When true (default), apply the evergreen/fleeting lifecycle multipliers to the ranking. Set false for legacy ts_rank * tagBoost * recencyBoost only.",
|
|
224
|
-
},
|
|
225
|
-
include_archived: {
|
|
226
|
-
type: "boolean",
|
|
227
|
-
description: "When true, include lifecycle='archived' rows. Default false — archived notes are excluded from regular searches.",
|
|
228
|
-
},
|
|
229
|
-
level: {
|
|
230
|
-
type: "string",
|
|
231
|
-
enum: ["full", "paragraph", "sentence"],
|
|
232
|
-
description: "Layered representation level. 'full' (default) keeps the stored description; 'paragraph' replaces it with a ~120-word distill; 'sentence' replaces it with a ~25-word claim. Use 'sentence' for cheap high-density agent prompts where every token matters. Falls back to the stored description when the distill is not yet populated for a row.",
|
|
233
|
-
},
|
|
234
|
-
include_trace_events: {
|
|
235
|
-
type: "boolean",
|
|
236
|
-
description: "T375: when true, ALSO search episodic tool-call trace summaries ('what did I try before this worked?') and return them in a separate `traceEvents` field. Default false — this is fully additive and never changes `results` or its ranking. Combine with trace_session_id to scope to one session.",
|
|
237
|
-
},
|
|
238
|
-
trace_session_id: {
|
|
239
|
-
type: "number",
|
|
240
|
-
description: "Scope trace-event fusion to one agent session (omit to search across the tenant's trace events). Ignored unless include_trace_events is true.",
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
required: ["query"],
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
name: "ctx_explain_recall",
|
|
248
|
-
description: "Explain WHY a search result was recalled by reconstructing the RRF (Reciprocal Rank Fusion) math for a captured retrieval trace: per-signal score breakdown (PG lexical rank + raw ts_rank, Elasticsearch BM25 rank + raw score, chunk cosine similarity, recency/tag/lifecycle/salience/confidence boost multipliers), the RRF-fused score, and the final rank -- for every result of that search, plus a human-readable narrative per result. Requires the server to have RETRIEVAL_TRACE_ENABLED=true and that this particular search was sampled in by RETRIEVAL_TRACE_SAMPLE_RATE -- this is a V1 observability surface with no listing endpoint yet, so trace ids come from direct DB inspection during this phase. Each certificate is optionally signed with the tenant's Ed25519 export key (T370 MIF); `signatureVerified` in the response reports whether the signature checks out against the tenant's published public key, making the certificate usable as tamper-evident evidence for an auditor.",
|
|
249
|
-
inputSchema: {
|
|
250
|
-
type: "object",
|
|
251
|
-
properties: {
|
|
252
|
-
id: {
|
|
253
|
-
type: "number",
|
|
254
|
-
description: "The retrieval_traces row id to explain.",
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
required: ["id"],
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
name: "ctx_list",
|
|
262
|
-
description: "List context entries with optional filters. Use this to browse existing contexts by workspace, project, type, or tag without a search query.",
|
|
263
|
-
inputSchema: {
|
|
264
|
-
type: "object",
|
|
265
|
-
properties: {
|
|
266
|
-
workspace: {
|
|
267
|
-
type: "string",
|
|
268
|
-
description: "Filter by workspace identifier",
|
|
269
|
-
},
|
|
270
|
-
project: {
|
|
271
|
-
type: "string",
|
|
272
|
-
description: "Filter by project identifier",
|
|
273
|
-
},
|
|
274
|
-
type: {
|
|
275
|
-
type: "string",
|
|
276
|
-
enum: [...CONTEXT_TYPE_ENUM],
|
|
277
|
-
description: "Filter by context type",
|
|
278
|
-
},
|
|
279
|
-
tag: {
|
|
280
|
-
type: "string",
|
|
281
|
-
description: "Filter by a single tag",
|
|
282
|
-
},
|
|
283
|
-
scope: {
|
|
284
|
-
type: "string",
|
|
285
|
-
enum: [...CONTEXT_SCOPE_ENUM],
|
|
286
|
-
description: "Filter by visibility scope",
|
|
287
|
-
},
|
|
288
|
-
memoryKind: {
|
|
289
|
-
type: "string",
|
|
290
|
-
enum: [...MEMORY_KIND_ENUM],
|
|
291
|
-
description: "Filter to a single taxonomy kind: 'episodic', 'semantic', or 'procedural'.",
|
|
292
|
-
},
|
|
293
|
-
limit: {
|
|
294
|
-
type: "number",
|
|
295
|
-
description: "Max results to return (default 20)",
|
|
296
|
-
},
|
|
297
|
-
offset: {
|
|
298
|
-
type: "number",
|
|
299
|
-
description: "Offset for pagination",
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
required: [],
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
name: "ctx_get",
|
|
307
|
-
description: "Get a single context entry by its ID. Use this when you already know the exact context ID you want to read. Response includes lifecycle, atomic, quality_score, valid_from, and valid_to alongside the standard fields.",
|
|
308
|
-
inputSchema: {
|
|
309
|
-
type: "object",
|
|
310
|
-
properties: {
|
|
311
|
-
id: {
|
|
312
|
-
type: "number",
|
|
313
|
-
description: "The context entry ID",
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
required: ["id"],
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
name: "ctx_update",
|
|
321
|
-
description: "Update an existing context entry. Only the provided fields are changed; omitted fields remain unchanged. Use this to correct, append to, reclassify, or retire (archive) an existing entry. Optional lifecycle/valid_from/valid_to update note maturity and bi-temporal validity.",
|
|
322
|
-
inputSchema: {
|
|
323
|
-
type: "object",
|
|
324
|
-
properties: {
|
|
325
|
-
id: {
|
|
326
|
-
type: "number",
|
|
327
|
-
description: "The context entry ID to update",
|
|
328
|
-
},
|
|
329
|
-
name: { type: "string", description: "New title" },
|
|
330
|
-
description: { type: "string", description: "New description" },
|
|
331
|
-
content: { type: "string", description: "New content body" },
|
|
332
|
-
type: {
|
|
333
|
-
type: "string",
|
|
334
|
-
enum: [...CONTEXT_TYPE_ENUM],
|
|
335
|
-
description: "New context type",
|
|
336
|
-
},
|
|
337
|
-
scope: {
|
|
338
|
-
type: "string",
|
|
339
|
-
enum: [...CONTEXT_SCOPE_ENUM],
|
|
340
|
-
description: "New visibility scope",
|
|
341
|
-
},
|
|
342
|
-
tags: {
|
|
343
|
-
type: "array",
|
|
344
|
-
items: { type: "string" },
|
|
345
|
-
description: "Replacement set of tags",
|
|
346
|
-
},
|
|
347
|
-
metadata: {
|
|
348
|
-
type: "object",
|
|
349
|
-
description: "Replacement metadata object",
|
|
350
|
-
},
|
|
351
|
-
archivedAt: {
|
|
352
|
-
type: ["string", "null"],
|
|
353
|
-
description: "Set to null to unarchive, or ISO date string to archive",
|
|
354
|
-
},
|
|
355
|
-
lifecycle: {
|
|
356
|
-
type: "string",
|
|
357
|
-
enum: [...LIFECYCLE_ENUM],
|
|
358
|
-
description: "New lifecycle state (fleeting/working/evergreen/archived).",
|
|
359
|
-
},
|
|
360
|
-
memoryKind: {
|
|
361
|
-
type: "string",
|
|
362
|
-
enum: [...MEMORY_KIND_ENUM],
|
|
363
|
-
description: "Taxonomy override: 'episodic', 'semantic', or 'procedural'. Setting this locks the value against the fire-and-forget atomicity-judge LLM refinement on this write.",
|
|
364
|
-
},
|
|
365
|
-
valid_from: {
|
|
366
|
-
type: "string",
|
|
367
|
-
description: "Bi-temporal: ISO 8601 timestamp when the fact started being true.",
|
|
368
|
-
},
|
|
369
|
-
valid_to: {
|
|
370
|
-
type: "string",
|
|
371
|
-
description: "Bi-temporal: ISO 8601 timestamp when the fact stopped being true; null to keep open.",
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
required: ["id"],
|
|
375
|
-
},
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
name: "ctx_delete",
|
|
379
|
-
description: "Permanently delete a context entry by ID. This action cannot be undone. Use this only when you are sure the entry should be removed.",
|
|
380
|
-
inputSchema: {
|
|
381
|
-
type: "object",
|
|
382
|
-
properties: {
|
|
383
|
-
id: {
|
|
384
|
-
type: "number",
|
|
385
|
-
description: "The context entry ID to delete",
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
required: ["id"],
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
name: "ctx_bulk_update",
|
|
393
|
-
description: "Apply the same lifecycle move and/or archive flag to many contexts in a single call. Use this when you need to retire, mature, or reclassify a batch (capped at 200 ids per call). Tenant-scoped on the backend — ids belonging to other tenants are silently dropped and reported in the failed list. Returns { updated: number[], failed: { id, error }[] }.",
|
|
394
|
-
inputSchema: {
|
|
395
|
-
type: "object",
|
|
396
|
-
properties: {
|
|
397
|
-
ids: {
|
|
398
|
-
type: "array",
|
|
399
|
-
items: { type: "number" },
|
|
400
|
-
description: "Context IDs to update (max 200 per call)",
|
|
401
|
-
},
|
|
402
|
-
patch: {
|
|
403
|
-
type: "object",
|
|
404
|
-
description: "Fields to apply uniformly. Supply lifecycle, archive, or both. archive=true stamps archived_at to NOW().",
|
|
405
|
-
properties: {
|
|
406
|
-
lifecycle: {
|
|
407
|
-
type: "string",
|
|
408
|
-
enum: [...LIFECYCLE_ENUM],
|
|
409
|
-
description: "New lifecycle state for every supplied id",
|
|
410
|
-
},
|
|
411
|
-
archive: {
|
|
412
|
-
type: "boolean",
|
|
413
|
-
description: "When true, stamp archived_at = NOW() on every supplied id (use to retire a batch).",
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
required: ["ids", "patch"],
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
name: "ctx_undo_archive",
|
|
423
|
-
description: "Undo a memory-evolution auto-archive within the configurable window (default 72h, env EVOLUTION_ARCHIVE_UNDO_WINDOW_HOURS). Only contexts archived by the memory-evolution feature qualify - manual PATCH lifecycle archives don't. Returns the restored context. Companion endpoint: ctx_undo_archive_info for eligibility check.",
|
|
424
|
-
inputSchema: {
|
|
425
|
-
type: "object",
|
|
426
|
-
properties: {
|
|
427
|
-
id: {
|
|
428
|
-
type: "number",
|
|
429
|
-
description: "Context ID to undo archive on.",
|
|
430
|
-
},
|
|
431
|
-
},
|
|
432
|
-
required: ["id"],
|
|
433
|
-
},
|
|
434
|
-
},
|
|
435
|
-
{
|
|
436
|
-
name: "ctx_undo_archive_info",
|
|
437
|
-
description: "Check whether a context's auto-archive is still undo-able. Returns `{eligible: bool, archivedAt, expiresAt, archivedByFocalId, confidence, withinWindow}`. Use before ctx_undo_archive to surface a meaningful UX (button enabled/disabled, countdown to expiry) instead of round-tripping the undo attempt.",
|
|
438
|
-
inputSchema: {
|
|
439
|
-
type: "object",
|
|
440
|
-
properties: {
|
|
441
|
-
id: {
|
|
442
|
-
type: "number",
|
|
443
|
-
description: "Context ID to inspect.",
|
|
444
|
-
},
|
|
445
|
-
},
|
|
446
|
-
required: ["id"],
|
|
447
|
-
},
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
name: "ctx_import",
|
|
451
|
-
description: "Bulk import context entries from an external source. Optionally filter by workspace and preview with a dry run before committing.",
|
|
452
|
-
inputSchema: {
|
|
453
|
-
type: "object",
|
|
454
|
-
properties: {
|
|
455
|
-
workspace: {
|
|
456
|
-
type: "string",
|
|
457
|
-
description: "Filter import to a specific workspace",
|
|
458
|
-
},
|
|
459
|
-
dryRun: {
|
|
460
|
-
type: "boolean",
|
|
461
|
-
description: "If true, validate and preview the import without persisting changes",
|
|
462
|
-
},
|
|
463
|
-
},
|
|
464
|
-
required: [],
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
name: "ctx_stats",
|
|
469
|
-
description: "Get aggregate statistics: total context count, breakdown by workspace, type, tag, recently updated entries, and orphan_rate (notes with no tags and no inbound references). Use this for an overview of what is stored and to spot disconnected knowledge.",
|
|
470
|
-
inputSchema: {
|
|
471
|
-
type: "object",
|
|
472
|
-
properties: {},
|
|
473
|
-
required: [],
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
name: "ctx_dream",
|
|
478
|
-
description: "Run memory consolidation (dream) on a workspace's context entries. Clusters related entries using vector similarity, synthesizes each cluster into a 'synthesis' entry via LLM, and writes a dream log. Use dryRun to preview clusters without persisting.\n\nT497: dream on a large workspace (more than DREAM_ASYNC_THRESHOLD contexts, default 100) runs async — the response is { jobId, statusUrl } and you must poll ctx_ingest_status (or GET /api/ingest-jobs/:id) until status='succeeded' or 'failed' to read the full result. Small workspaces still return the full result inline. Pass async=true/false to force a path explicitly.",
|
|
479
|
-
inputSchema: {
|
|
480
|
-
type: "object",
|
|
481
|
-
properties: {
|
|
482
|
-
workspace: {
|
|
483
|
-
type: "string",
|
|
484
|
-
description: "Workspace identifier to consolidate knowledge for",
|
|
485
|
-
},
|
|
486
|
-
project: {
|
|
487
|
-
type: "string",
|
|
488
|
-
description: "Optional project filter — consolidate only entries for this project",
|
|
489
|
-
},
|
|
490
|
-
dryRun: {
|
|
491
|
-
type: "boolean",
|
|
492
|
-
description: "If true, preview clusters without creating synthesis entries (default false)",
|
|
493
|
-
},
|
|
494
|
-
async: {
|
|
495
|
-
type: "boolean",
|
|
496
|
-
description: "Force the async path (true) or sync path (false). Omit to let the server auto-pick based on workspace size — async responses are { jobId, statusUrl }; sync responses are the full dream result.",
|
|
497
|
-
},
|
|
498
|
-
},
|
|
499
|
-
required: ["workspace"],
|
|
500
|
-
},
|
|
501
|
-
},
|
|
502
|
-
{
|
|
503
|
-
name: "ctx_link",
|
|
504
|
-
description: "Manually create a typed link from one context to another (supersedes/contradicts/implements/derived_from/related_to). The backend already auto-creates links during memory evolution; only call this tool when you want to assert a relationship the system missed or override it explicitly. The 'reason' arg is recorded as the link's created_by annotation for audit.",
|
|
505
|
-
inputSchema: {
|
|
506
|
-
type: "object",
|
|
507
|
-
properties: {
|
|
508
|
-
source_id: {
|
|
509
|
-
type: "number",
|
|
510
|
-
description: "ID of the source context (the link's tail)",
|
|
511
|
-
},
|
|
512
|
-
target_id: {
|
|
513
|
-
type: "number",
|
|
514
|
-
description: "ID of the target context (the link's head)",
|
|
515
|
-
},
|
|
516
|
-
type: {
|
|
517
|
-
type: "string",
|
|
518
|
-
enum: [...LINK_TYPE_ENUM],
|
|
519
|
-
description: "Link semantics: supersedes (replaces), contradicts (disagrees), implements (concrete impl of target spec), derived_from (extracted from target), related_to (generic association).",
|
|
520
|
-
},
|
|
521
|
-
confidence: {
|
|
522
|
-
type: "number",
|
|
523
|
-
description: "Optional confidence score in [0, 1]. Omit if not meaningful.",
|
|
524
|
-
},
|
|
525
|
-
reason: {
|
|
526
|
-
type: "string",
|
|
527
|
-
description: "Optional human-readable rationale; recorded as created_by annotation for audit.",
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
required: ["source_id", "target_id", "type"],
|
|
531
|
-
},
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
name: "ctx_links",
|
|
535
|
-
description: "List typed links for a context — incoming, outgoing, or both. Use this to discover how a context is connected (what it supersedes, what supersedes it, what implements it, etc.) before making decisions about updates or archival. The `created_by` field on each link indicates origin: `wikilink:<raw title>` marks edges auto-created from `[[Wiki Title]]` tokens in the source content, `memory-evolution` marks LLM-judged edges, and other values are manual.",
|
|
536
|
-
inputSchema: {
|
|
537
|
-
type: "object",
|
|
538
|
-
properties: {
|
|
539
|
-
id: {
|
|
540
|
-
type: "number",
|
|
541
|
-
description: "The context entry ID whose links you want to list",
|
|
542
|
-
},
|
|
543
|
-
direction: {
|
|
544
|
-
type: "string",
|
|
545
|
-
enum: [...LINK_DIRECTION_ENUM],
|
|
546
|
-
description: "Which links to return: 'outbound' (this context links to others), 'inbound' (others link to this), or 'both' (default).",
|
|
547
|
-
},
|
|
548
|
-
},
|
|
549
|
-
required: ["id"],
|
|
550
|
-
},
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
name: "ctx_evolve",
|
|
554
|
-
description: "Re-run memory evolution for an existing context. Examines K=5 nearest neighbors and creates typed links / archives superseded notes via LLM judging. Use when you've updated a context substantially or want to recompute relationships. Pair with dry_run=true first to preview.",
|
|
555
|
-
inputSchema: {
|
|
556
|
-
type: "object",
|
|
557
|
-
properties: {
|
|
558
|
-
id: {
|
|
559
|
-
type: "number",
|
|
560
|
-
description: "The context entry ID to re-run memory evolution for",
|
|
561
|
-
},
|
|
562
|
-
k_neighbors: {
|
|
563
|
-
type: "number",
|
|
564
|
-
description: "Override the default neighbor count (env EVOLUTION_K_NEIGHBORS or 5). Capped at 20.",
|
|
565
|
-
},
|
|
566
|
-
dry_run: {
|
|
567
|
-
type: "boolean",
|
|
568
|
-
description: "When true, run the LLM judgments but skip link creation, archival, and activity logging. Returns the proposals so you can preview before committing. Default false.",
|
|
569
|
-
},
|
|
570
|
-
},
|
|
571
|
-
required: ["id"],
|
|
572
|
-
},
|
|
573
|
-
},
|
|
574
|
-
{
|
|
575
|
-
name: "ctx_chunks",
|
|
576
|
-
description: "List the block-level chunks for a context, ordered by chunk_index. Each chunk includes its heading_path, content, and has_embedding flag (false while the embedding job is still pending). Use this to inspect how a context was split for chunked retrieval, debug per-passage matches surfaced by ctx_search, or check embedding coverage before relying on semantic ranking.",
|
|
577
|
-
inputSchema: {
|
|
578
|
-
type: "object",
|
|
579
|
-
properties: {
|
|
580
|
-
id: {
|
|
581
|
-
type: "number",
|
|
582
|
-
description: "The context entry ID whose chunks you want to list",
|
|
583
|
-
},
|
|
584
|
-
},
|
|
585
|
-
required: ["id"],
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
// -------------------------------------------------------------------------
|
|
589
|
-
// Code graph navigation (T315). Requires the repo to have been indexed with
|
|
590
|
-
// the contextq indexer (T312/T314) so that code edges exist in context_links.
|
|
591
|
-
// These tools answer questions from the stored graph — not grep, not file-read.
|
|
592
|
-
// -------------------------------------------------------------------------
|
|
593
|
-
{
|
|
594
|
-
name: "ctx_code_refs",
|
|
595
|
-
description: "Find every file that imports or calls a given source file. Returns the set of referencing files (with link type and callee-symbol hint) from the code graph stored in context_links. Use this instead of grep to answer 'what references X' — it reads from the pre-built graph rather than scanning the repo. `ref` can be a repo-relative file path (e.g. 'src/services/search.service.ts') or a numeric context id. Returns 404 when the file has not been indexed.",
|
|
596
|
-
inputSchema: {
|
|
597
|
-
type: "object",
|
|
598
|
-
properties: {
|
|
599
|
-
ref: {
|
|
600
|
-
type: "string",
|
|
601
|
-
description: "Repo-relative file path (e.g. 'src/services/context.service.ts') or numeric context id. The path is matched against the indexed external_id suffix, so partial trailing paths work as long as they are unambiguous within the tenant.",
|
|
602
|
-
},
|
|
603
|
-
},
|
|
604
|
-
required: ["ref"],
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
name: "ctx_code_trace",
|
|
609
|
-
description: "Trace the directional call chain FROM a given source file via BFS over CALLS edges in the code graph. Returns nodes and edges so you can reconstruct the call path. Use this to answer 'what does X call, and what do those files call' up to N levels deep — reads from the pre-built graph, not file content. `ref` can be a file path or numeric context id. `depth` controls BFS hops (default 2, max 3). Returns 404 when the file has not been indexed.",
|
|
610
|
-
inputSchema: {
|
|
611
|
-
type: "object",
|
|
612
|
-
properties: {
|
|
613
|
-
ref: {
|
|
614
|
-
type: "string",
|
|
615
|
-
description: "Repo-relative file path (e.g. 'src/services/search.service.ts') or numeric context id to start the trace from.",
|
|
616
|
-
},
|
|
617
|
-
depth: {
|
|
618
|
-
type: "number",
|
|
619
|
-
description: "How many CALLS hops to follow. Default 2, max 3. The service clamps the value — passing 4 is treated as 3.",
|
|
620
|
-
},
|
|
621
|
-
},
|
|
622
|
-
required: ["ref"],
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
name: "ctx_provenance_get",
|
|
627
|
-
description: "Walk the belief provenance chain backward from a context (T358): who/what produced it, and what it was derived from. BFS over context_provenance.source_context_ids, same traversal shape as ctx_code_trace. Returns { root: {id, name, epistemicStatus}, nodes: [{id, name, epistemicStatus}], edges: [{contextId, sourceContextId, transformType, producerId, modelId, promptHash, createdAt}], depth, truncated }. transformType is one of manual/agent_write/ingest/dream_synthesis/evolve. epistemicStatus is one of observed/told/inferred/assumed (weakest->strongest: assumed < inferred < told < observed) — use this to judge how much to trust a fact and its ancestors. Use this to answer 'where did this claim come from' or 'what fed into this dream synthesis'. Returns 404 when the context does not exist or isn't visible to the caller.",
|
|
628
|
-
inputSchema: {
|
|
629
|
-
type: "object",
|
|
630
|
-
properties: {
|
|
631
|
-
id: {
|
|
632
|
-
type: "number",
|
|
633
|
-
description: "The context entry ID to walk provenance backward from",
|
|
634
|
-
},
|
|
635
|
-
depth: {
|
|
636
|
-
type: "number",
|
|
637
|
-
description: "How many hops to follow back through source_context_ids. Default 5, max 5.",
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
required: ["id"],
|
|
641
|
-
},
|
|
642
|
-
},
|
|
643
|
-
{
|
|
644
|
-
name: "ctx_confidence_get",
|
|
645
|
-
description: "Get a context's current belief confidence (T359): a continuously-recomputed [0,1] score, distinct from epistemicStatus (a coarse write-time tag) and context_links.confidence (a static per-edge score). This one moves over time — corroborating derived_from links raise it, contradicts/supersedes links against it lower it, weighted by the linking source context's own confidence, and it slowly decays toward the neutral 0.5 prior if the context goes stale without new evidence. Returns { contextId, confidence, confidenceUpdatedAt, evidenceCount }. Use this to judge how much to trust a specific fact right now, as opposed to ctx_belief_history which shows how that trust got here. Returns 404 when the context does not exist or isn't visible to the caller.",
|
|
646
|
-
inputSchema: {
|
|
647
|
-
type: "object",
|
|
648
|
-
properties: {
|
|
649
|
-
id: {
|
|
650
|
-
type: "number",
|
|
651
|
-
description: "The context entry ID to look up confidence for",
|
|
652
|
-
},
|
|
653
|
-
},
|
|
654
|
-
required: ["id"],
|
|
655
|
-
},
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
name: "ctx_belief_history",
|
|
659
|
-
description: "Get the append-only ledger of every confidence change applied to a context (T359): each entry records the triggering link, a human-readable reason, and the old/new confidence values. Use this to audit WHY a context's belief confidence is what it is right now (see ctx_confidence_get for the current value) — e.g. 'why do we no longer trust this note' or 'what corroborated this claim'. Returns { results: [{id, contextId, triggeringLinkId, reason, oldConfidence, newConfidence, createdAt}] }, newest first. Returns an empty results array when the context isn't visible to the caller or has no recorded events yet.",
|
|
660
|
-
inputSchema: {
|
|
661
|
-
type: "object",
|
|
662
|
-
properties: {
|
|
663
|
-
id: {
|
|
664
|
-
type: "number",
|
|
665
|
-
description: "The context entry ID to fetch belief history for",
|
|
666
|
-
},
|
|
667
|
-
limit: {
|
|
668
|
-
type: "number",
|
|
669
|
-
description: "Max events to return, newest first. Default 50, max 200.",
|
|
670
|
-
},
|
|
671
|
-
},
|
|
672
|
-
required: ["id"],
|
|
673
|
-
},
|
|
674
|
-
},
|
|
675
|
-
{
|
|
676
|
-
name: "ctx_blast_radius",
|
|
677
|
-
description: "Compute the blast radius of a context BEFORE trusting a correction to it (T362): 'what breaks if this context turns out to be wrong'. Bounded BFS over context_links following derived_from/supersedes/implements edges downstream (same traversal shape as ctx_code_trace/ctx_provenance_get), plus a structural join against goal_nodes and a best-effort match against agent_lessons that reference the context. The exact same fan-out count gates whether the server's own automated supersede/archive/contradict decisions (memory-evolution, T352 contradicts links) auto-apply or are held for manual review — call this first when you're about to believe or act on a correction to a context with many dependents. Returns { contextId, contextName, totalFanOut, threshold, exceedsThreshold, depthReached, truncated, contextLinkCount, goalNodeCount, agentLessonCount, topNodes: [{id, name, kind, linkType, depth, fanOutScore}] }. `topNodes` is ranked highest-impact first. Returns 404 when the context does not exist or isn't visible to the caller.",
|
|
678
|
-
inputSchema: {
|
|
679
|
-
type: "object",
|
|
680
|
-
properties: {
|
|
681
|
-
id: {
|
|
682
|
-
type: "number",
|
|
683
|
-
description: "The context entry ID to compute blast radius for",
|
|
684
|
-
},
|
|
685
|
-
depth: {
|
|
686
|
-
type: "number",
|
|
687
|
-
description: "Max BFS hops to follow downstream. Default/max 3.",
|
|
688
|
-
},
|
|
689
|
-
top: {
|
|
690
|
-
type: "number",
|
|
691
|
-
description: "Max number of top-impact nodes to return. Default 10, max 50.",
|
|
692
|
-
},
|
|
693
|
-
},
|
|
694
|
-
required: ["id"],
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
// -------------------------------------------------------------------------
|
|
698
|
-
// Entity knowledge graph (T339-T342). Requires ENTITY_GRAPH_ENABLED to have
|
|
699
|
-
// been on for a while so entities/entity_edges are populated — otherwise
|
|
700
|
-
// these return empty results, not errors.
|
|
701
|
-
// -------------------------------------------------------------------------
|
|
702
|
-
{
|
|
703
|
-
name: "ctx_graph_search",
|
|
704
|
-
description: "Entity-anchored fact search over the temporal knowledge graph. Resolves entities mentioned in the query (name/alias match, no LLM call), walks entity_edges 1-2 hops, and returns ranked facts with provenance (which context each fact came from) and validity (current vs. historical/invalidated). Use this to answer relationship questions ('who does X work for', 'what depends on Y') that plain text search can't reliably surface. Pass `as_of` for a point-in-time view of what was believed true at that date. Returns empty arrays (not an error) when no entities in the query resolve or the graph has no data yet.",
|
|
705
|
-
inputSchema: {
|
|
706
|
-
type: "object",
|
|
707
|
-
properties: {
|
|
708
|
-
query: {
|
|
709
|
-
type: "string",
|
|
710
|
-
description: "Free-text query naming one or more entities and/or a relationship, e.g. 'who does Carol Vu work for'.",
|
|
711
|
-
},
|
|
712
|
-
as_of: {
|
|
713
|
-
type: "string",
|
|
714
|
-
description: "Optional ISO 8601 timestamp. Returns facts as they were believed true at this point in time, including facts since invalidated. Omit for the current state.",
|
|
715
|
-
},
|
|
716
|
-
limit: {
|
|
717
|
-
type: "number",
|
|
718
|
-
description: "Max provenance context ids to return, default 20, max 50.",
|
|
719
|
-
},
|
|
720
|
-
},
|
|
721
|
-
required: ["query"],
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
{
|
|
725
|
-
name: "ctx_entity_get",
|
|
726
|
-
description: "Fetch an entity card from the knowledge graph: the entity's identity (name, aliases, type, summary), its current edges (facts currently believed true), and its full timeline (every fact ever extracted about it, including ones since invalidated/superseded, newest first). Use this to look up everything the graph knows about a specific entity id. Returns 404 when the entity does not exist under the authenticated tenant.",
|
|
727
|
-
inputSchema: {
|
|
728
|
-
type: "object",
|
|
729
|
-
properties: {
|
|
730
|
-
id: {
|
|
731
|
-
type: "number",
|
|
732
|
-
description: "Numeric entity id (from an entities row, or the sourceEntityId/targetEntityId on a fact returned by ctx_graph_search).",
|
|
733
|
-
},
|
|
734
|
-
as_of: {
|
|
735
|
-
type: "string",
|
|
736
|
-
description: "Optional ISO 8601 timestamp. Scopes the 'edges' field (not the timeline, which always shows full history) to what was valid at this point in time.",
|
|
737
|
-
},
|
|
738
|
-
},
|
|
739
|
-
required: ["id"],
|
|
740
|
-
},
|
|
741
|
-
},
|
|
742
|
-
{
|
|
743
|
-
name: "ctx_mocs",
|
|
744
|
-
description: "List Maps of Content (MOCs) for the tenant. MOCs are curated index notes that summarize and link to clusters of related contexts, providing a navigation layer over the knowledge graph. Use this to discover existing high-level views before creating new synthesis notes.",
|
|
745
|
-
inputSchema: {
|
|
746
|
-
type: "object",
|
|
747
|
-
properties: {
|
|
748
|
-
limit: {
|
|
749
|
-
type: "number",
|
|
750
|
-
description: "Optional max results to return.",
|
|
751
|
-
},
|
|
752
|
-
},
|
|
753
|
-
required: [],
|
|
754
|
-
},
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
name: "ctx_regenerate_mocs",
|
|
758
|
-
description: "Regenerate Maps of Content by re-clustering the tenant's contexts and synthesizing one MOC per cluster via LLM. Expensive (runs an LLM call per cluster) — call sparingly, typically after a substantial batch of new contexts has been added. Requires admin scope.",
|
|
759
|
-
inputSchema: {
|
|
760
|
-
type: "object",
|
|
761
|
-
properties: {},
|
|
762
|
-
required: [],
|
|
763
|
-
},
|
|
764
|
-
},
|
|
765
|
-
{
|
|
766
|
-
name: "ctx_maps",
|
|
767
|
-
description: "List memory maps (Dream-generated multi-shape navigation TOCs) for the caller's tenant. Optionally filter by workspace / project slug. Each row carries `{id, shape, version, generatedAt, userEdited, scope}` - fetch the full map_payload via /api/maps/:id (not wrapped here yet to keep this surface small). Useful when listing or summarising available maps to an AI agent.",
|
|
768
|
-
inputSchema: {
|
|
769
|
-
type: "object",
|
|
770
|
-
properties: {
|
|
771
|
-
workspace: {
|
|
772
|
-
type: "string",
|
|
773
|
-
description: "Filter by workspace slug (optional).",
|
|
774
|
-
},
|
|
775
|
-
project: {
|
|
776
|
-
type: "string",
|
|
777
|
-
description: "Filter by project slug (optional).",
|
|
778
|
-
},
|
|
779
|
-
},
|
|
780
|
-
required: [],
|
|
781
|
-
},
|
|
782
|
-
},
|
|
783
|
-
{
|
|
784
|
-
name: "ctx_saved_searches_list",
|
|
785
|
-
description: "List all saved searches visible to the caller (their own + tenant-shared). Saved searches are named, reusable retrieval views (Dataview-style) that bundle a query string with filters so agents/sessions can rerun them without re-typing the criteria.",
|
|
786
|
-
inputSchema: {
|
|
787
|
-
type: "object",
|
|
788
|
-
properties: {},
|
|
789
|
-
required: [],
|
|
790
|
-
},
|
|
791
|
-
},
|
|
792
|
-
{
|
|
793
|
-
name: "ctx_saved_searches_create",
|
|
794
|
-
description: "Create a new saved search. Supply a unique name (per owner within the tenant), an optional description, the search query string, and the filter blob. Set shared_with_tenant=true to let other users in the tenant see and run it.",
|
|
795
|
-
inputSchema: {
|
|
796
|
-
type: "object",
|
|
797
|
-
properties: {
|
|
798
|
-
name: {
|
|
799
|
-
type: "string",
|
|
800
|
-
description: "Unique name for the saved search (per owner within the tenant)",
|
|
801
|
-
},
|
|
802
|
-
description: {
|
|
803
|
-
type: "string",
|
|
804
|
-
description: "Optional one-line summary",
|
|
805
|
-
},
|
|
806
|
-
query: {
|
|
807
|
-
type: "string",
|
|
808
|
-
description: "Search query (full-text + semantic). May be empty for filter-only views.",
|
|
809
|
-
},
|
|
810
|
-
filters: {
|
|
811
|
-
type: "object",
|
|
812
|
-
description: "Filter blob — accepts the same fields as ctx_search (workspace, project, type, tags, scope, lifecycle, useMap, includeMaps, includeArchived, lifecycleBoost, chunkSearch, limit, offset). Unknown keys are stripped server-side.",
|
|
813
|
-
},
|
|
814
|
-
shared_with_tenant: {
|
|
815
|
-
type: "boolean",
|
|
816
|
-
description: "When true, other users in the tenant can read and run (but not delete) this saved search. Default false.",
|
|
817
|
-
},
|
|
818
|
-
},
|
|
819
|
-
required: ["name"],
|
|
820
|
-
},
|
|
821
|
-
},
|
|
822
|
-
{
|
|
823
|
-
name: "ctx_saved_searches_get",
|
|
824
|
-
description: "Fetch a single saved search by id. Returns 404 if it does not exist or the caller cannot see it (different tenant, or owned by another user and not shared).",
|
|
825
|
-
inputSchema: {
|
|
826
|
-
type: "object",
|
|
827
|
-
properties: {
|
|
828
|
-
id: {
|
|
829
|
-
type: "number",
|
|
830
|
-
description: "The saved search id",
|
|
831
|
-
},
|
|
832
|
-
},
|
|
833
|
-
required: ["id"],
|
|
834
|
-
},
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
name: "ctx_saved_searches_update",
|
|
838
|
-
description: "Update an existing saved search. Owner can update their own searches; admins/owners can also update tenant-shared searches owned by others. Only the provided fields are changed.",
|
|
839
|
-
inputSchema: {
|
|
840
|
-
type: "object",
|
|
841
|
-
properties: {
|
|
842
|
-
id: {
|
|
843
|
-
type: "number",
|
|
844
|
-
description: "The saved search id",
|
|
845
|
-
},
|
|
846
|
-
name: { type: "string", description: "New name" },
|
|
847
|
-
description: { type: "string", description: "New description" },
|
|
848
|
-
query: { type: "string", description: "New query string" },
|
|
849
|
-
filters: {
|
|
850
|
-
type: "object",
|
|
851
|
-
description: "Replacement filter blob (same shape as create)",
|
|
852
|
-
},
|
|
853
|
-
shared_with_tenant: {
|
|
854
|
-
type: "boolean",
|
|
855
|
-
description: "Toggle tenant-wide visibility",
|
|
856
|
-
},
|
|
857
|
-
},
|
|
858
|
-
required: ["id"],
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
{
|
|
862
|
-
name: "ctx_saved_searches_delete",
|
|
863
|
-
description: "Delete a saved search by id. Owner only — even tenant-shared searches can only be deleted by their original owner.",
|
|
864
|
-
inputSchema: {
|
|
865
|
-
type: "object",
|
|
866
|
-
properties: {
|
|
867
|
-
id: {
|
|
868
|
-
type: "number",
|
|
869
|
-
description: "The saved search id to delete",
|
|
870
|
-
},
|
|
871
|
-
},
|
|
872
|
-
required: ["id"],
|
|
873
|
-
},
|
|
874
|
-
},
|
|
875
|
-
{
|
|
876
|
-
name: "ctx_saved_searches_run",
|
|
877
|
-
description: "Execute a saved search by id and return the same shape as ctx_search. Optional limit/offset/query overrides apply at run time without modifying the persisted view. Use this when you already know the saved search id.",
|
|
878
|
-
inputSchema: {
|
|
879
|
-
type: "object",
|
|
880
|
-
properties: {
|
|
881
|
-
id: {
|
|
882
|
-
type: "number",
|
|
883
|
-
description: "The saved search id",
|
|
884
|
-
},
|
|
885
|
-
query: {
|
|
886
|
-
type: "string",
|
|
887
|
-
description: "Optional query override. If omitted, the persisted query is used.",
|
|
888
|
-
},
|
|
889
|
-
limit: {
|
|
890
|
-
type: "number",
|
|
891
|
-
description: "Optional limit override",
|
|
892
|
-
},
|
|
893
|
-
offset: {
|
|
894
|
-
type: "number",
|
|
895
|
-
description: "Optional offset override",
|
|
896
|
-
},
|
|
897
|
-
},
|
|
898
|
-
required: ["id"],
|
|
899
|
-
},
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
name: "ctx_run_saved_search",
|
|
903
|
-
description: "Convenience: resolve a saved search by name (caller's own row preferred, falling back to a tenant-shared row with the same name) and execute it. Use this when an agent has the human-readable name but not the id.",
|
|
904
|
-
inputSchema: {
|
|
905
|
-
type: "object",
|
|
906
|
-
properties: {
|
|
907
|
-
name: {
|
|
908
|
-
type: "string",
|
|
909
|
-
description: "The saved search name to resolve and run",
|
|
910
|
-
},
|
|
911
|
-
query: {
|
|
912
|
-
type: "string",
|
|
913
|
-
description: "Optional query override",
|
|
914
|
-
},
|
|
915
|
-
limit: {
|
|
916
|
-
type: "number",
|
|
917
|
-
description: "Optional limit override",
|
|
918
|
-
},
|
|
919
|
-
offset: {
|
|
920
|
-
type: "number",
|
|
921
|
-
description: "Optional offset override",
|
|
922
|
-
},
|
|
923
|
-
},
|
|
924
|
-
required: ["name"],
|
|
925
|
-
},
|
|
926
|
-
},
|
|
927
|
-
{
|
|
928
|
-
name: "ctx_ingest",
|
|
929
|
-
description: "Ingest raw material (URL or inline content) and turn it into atomic claims that get diff'd against the existing knowledge base. The server fetches/parses the source, extracts claims via LLM, runs kNN against the tenant + workspace scope, and decides create / update / archive per claim. Set dry_run=true to preview the plan without persisting any changes — recommended for the first call against a new source.\n\nWave 13b: large or URL-sourced ingests run async — the response is { jobId, statusUrl } and you must poll ctx_ingest_status (or GET /api/ingest-jobs/:id) until status='succeeded' or 'failed' to read the breakdown. Small inline ingests still return the full result inline. Pass async=true to force the async path explicitly.",
|
|
930
|
-
inputSchema: {
|
|
931
|
-
type: "object",
|
|
932
|
-
properties: {
|
|
933
|
-
source: {
|
|
934
|
-
type: "object",
|
|
935
|
-
description: "Raw material to ingest. Provide either url (fetched server-side, capped at 2 MB / 15 s) OR content (inline text). 'label' is a short human-readable identifier recorded on the activity log (defaults to the URL or 'inline').",
|
|
936
|
-
properties: {
|
|
937
|
-
url: {
|
|
938
|
-
type: "string",
|
|
939
|
-
description: "HTTP(S) URL to fetch and parse. HTML is stripped to plain text; markdown / plain text / JSON are passed through.",
|
|
940
|
-
},
|
|
941
|
-
content: {
|
|
942
|
-
type: "string",
|
|
943
|
-
description: "Inline source content (markdown, plain text, transcript). Use when the agent already has the document in memory.",
|
|
944
|
-
},
|
|
945
|
-
label: {
|
|
946
|
-
type: "string",
|
|
947
|
-
description: "Optional short label (filename, doc title, ticket ID) recorded on the audit log.",
|
|
948
|
-
},
|
|
949
|
-
},
|
|
950
|
-
},
|
|
951
|
-
workspace: {
|
|
952
|
-
type: "string",
|
|
953
|
-
description: "Workspace identifier the ingested claims belong to. Required for tenant + workspace scoped diff.",
|
|
954
|
-
},
|
|
955
|
-
project: {
|
|
956
|
-
type: "string",
|
|
957
|
-
description: "Optional project identifier within the workspace.",
|
|
958
|
-
},
|
|
959
|
-
dry_run: {
|
|
960
|
-
type: "boolean",
|
|
961
|
-
description: "When true, run the full extract + diff pipeline but skip every DB write (no create / update / archive / activity log). The response still lists what *would* happen so the agent can preview before committing. Default false.",
|
|
962
|
-
},
|
|
963
|
-
max_claims: {
|
|
964
|
-
type: "number",
|
|
965
|
-
description: "Cap on claims extracted from the source. Default 10, hard max 25. Lower this for noisy sources where you only want the top few facts.",
|
|
966
|
-
},
|
|
967
|
-
async: {
|
|
968
|
-
type: "boolean",
|
|
969
|
-
description: "Force the async path (true) or sync path (false). Omit to let the server auto-pick — URL sources and inputs with max_claims > INGEST_ASYNC_THRESHOLD (default 5) run async; everything else runs inline. Async responses are { jobId, statusUrl }; sync responses are the full IngestResult.",
|
|
970
|
-
},
|
|
971
|
-
},
|
|
972
|
-
required: ["source", "workspace"],
|
|
973
|
-
},
|
|
974
|
-
},
|
|
975
|
-
{
|
|
976
|
-
name: "ctx_ingest_status",
|
|
977
|
-
description: "Fetch the current status of an async background job. Generic poller (T497) — use this for the job id returned by ctx_ingest, ctx_remember, OR ctx_dream when any of them took the async path, until status='succeeded' or 'failed'. Returns counters (total_items, processed_items, failed_items), timestamps (created_at, started_at, completed_at), and on success the full result_summary (shape depends on which tool started the job: ctx_ingest/ctx_remember's created/updated/archived/skipped arrays, or ctx_dream's dream result).",
|
|
978
|
-
inputSchema: {
|
|
979
|
-
type: "object",
|
|
980
|
-
properties: {
|
|
981
|
-
job_id: {
|
|
982
|
-
type: "number",
|
|
983
|
-
description: "The job id returned by ctx_ingest, ctx_remember, or ctx_dream when the async path was taken.",
|
|
984
|
-
},
|
|
985
|
-
},
|
|
986
|
-
required: ["job_id"],
|
|
987
|
-
},
|
|
988
|
-
},
|
|
989
|
-
{
|
|
990
|
-
name: "ctx_remember",
|
|
991
|
-
description: "Extract durable memories from a raw multi-turn conversation and save them as deduped atomic contexts. Turn-aware sibling of ctx_ingest: the server builds a speaker-attributed transcript, extracts only durable facts/preferences via LLM (skipping chit-chat), and runs the claims through the SAME kNN-dedup + diff + create/update/archive pipeline ctx_ingest uses. Pass subjectId to scope memories to a single end-user of your application (Mem0-parity user_id) — dedup then only considers that subject's own prior memories, and every created context is tagged with that subjectId so ctx_search (subjectId param) and GET /api/memory can retrieve it later. Set dryRun=true to preview without persisting.\n\nLong conversations run async — the response is { jobId, statusUrl } and you must poll ctx_ingest_status (or GET /api/ingest-jobs/:id) until status='succeeded' or 'failed'. Short conversations return the full result inline. Pass async=true/false to force a path explicitly.",
|
|
992
|
-
inputSchema: {
|
|
993
|
-
type: "object",
|
|
994
|
-
properties: {
|
|
995
|
-
messages: {
|
|
996
|
-
type: "array",
|
|
997
|
-
items: {
|
|
998
|
-
type: "object",
|
|
999
|
-
properties: {
|
|
1000
|
-
role: {
|
|
1001
|
-
type: "string",
|
|
1002
|
-
description: "Speaker role, e.g. 'user', 'assistant', 'system'.",
|
|
1003
|
-
},
|
|
1004
|
-
content: { type: "string", description: "Turn content." },
|
|
1005
|
-
name: {
|
|
1006
|
-
type: "string",
|
|
1007
|
-
description: "Optional speaker identifier (e.g. a specific agent name in a multi-agent transcript).",
|
|
1008
|
-
},
|
|
1009
|
-
},
|
|
1010
|
-
required: ["role", "content"],
|
|
1011
|
-
},
|
|
1012
|
-
description: "Conversation turns in chronological order.",
|
|
1013
|
-
},
|
|
1014
|
-
subjectId: {
|
|
1015
|
-
type: "string",
|
|
1016
|
-
description: "End-user identity this conversation belongs to (Mem0-parity user_id). Scopes dedup and tags every created context so it can be retrieved later via ctx_search subjectId or GET /api/memory.",
|
|
1017
|
-
},
|
|
1018
|
-
agentSlug: {
|
|
1019
|
-
type: "string",
|
|
1020
|
-
description: "Optional identifier of the agent that produced/consumed this conversation. Recorded as metadata only.",
|
|
1021
|
-
},
|
|
1022
|
-
workspace: {
|
|
1023
|
-
type: "string",
|
|
1024
|
-
description: "Workspace identifier the extracted memories belong to. Falls back to the request's active scope when omitted.",
|
|
1025
|
-
},
|
|
1026
|
-
project: {
|
|
1027
|
-
type: "string",
|
|
1028
|
-
description: "Optional project identifier within the workspace.",
|
|
1029
|
-
},
|
|
1030
|
-
sessionId: {
|
|
1031
|
-
type: "string",
|
|
1032
|
-
description: "Optional conversation/session identifier. Recorded as metadata and on the audit row only.",
|
|
1033
|
-
},
|
|
1034
|
-
dryRun: {
|
|
1035
|
-
type: "boolean",
|
|
1036
|
-
description: "When true, run the full extract + diff pipeline but skip every DB write. Default false.",
|
|
1037
|
-
},
|
|
1038
|
-
maxClaims: {
|
|
1039
|
-
type: "number",
|
|
1040
|
-
description: "Cap on claims extracted from the conversation. Default 10, hard max 25.",
|
|
1041
|
-
},
|
|
1042
|
-
async: {
|
|
1043
|
-
type: "boolean",
|
|
1044
|
-
description: "Force the async path (true) or sync path (false). Omit to let the server auto-pick — conversations longer than MEMORY_ASYNC_THRESHOLD messages (default 8) run async.",
|
|
1045
|
-
},
|
|
1046
|
-
},
|
|
1047
|
-
required: ["messages"],
|
|
1048
|
-
},
|
|
1049
|
-
},
|
|
1050
|
-
{
|
|
1051
|
-
name: "ctx_events_recent",
|
|
1052
|
-
description: "List the most recent context events (created / updated / archived / linked) from the in-memory ring buffer. Tenant-scoped — only events for the caller's tenant are returned. Useful for downstream agents that want to react to writes; the ring is capped at 200 envelopes server-side. Note: streaming subscriptions are HTTP-only via GET /api/events (Server-Sent Events) — MCP does not expose a streaming variant.",
|
|
1053
|
-
inputSchema: {
|
|
1054
|
-
type: "object",
|
|
1055
|
-
properties: {
|
|
1056
|
-
topic: {
|
|
1057
|
-
type: "string",
|
|
1058
|
-
enum: [
|
|
1059
|
-
"context.created",
|
|
1060
|
-
"context.updated",
|
|
1061
|
-
"context.archived",
|
|
1062
|
-
"context.linked",
|
|
1063
|
-
],
|
|
1064
|
-
description: "Optional single-topic filter. Omit to receive every topic.",
|
|
1065
|
-
},
|
|
1066
|
-
limit: {
|
|
1067
|
-
type: "number",
|
|
1068
|
-
description: "Max events to return (default 50, max 200).",
|
|
1069
|
-
},
|
|
1070
|
-
},
|
|
1071
|
-
required: [],
|
|
1072
|
-
},
|
|
1073
|
-
},
|
|
1074
|
-
{
|
|
1075
|
-
name: "ctx_memory_review_logs",
|
|
1076
|
-
description: "List recent memory-review verdicts (keep / retag / merge / archive / contradict / error). Each row records one LLM-judged review of a context during the periodic review-for-correctness ritual; verdicts are advisory unless 'archive' was auto-applied. Use this to audit what the reviewer has flagged and triage retag/merge/contradict candidates by hand. Superadmin scope only.",
|
|
1077
|
-
inputSchema: {
|
|
1078
|
-
type: "object",
|
|
1079
|
-
properties: {
|
|
1080
|
-
tenant_id: {
|
|
1081
|
-
type: "number",
|
|
1082
|
-
description: "Optional tenant ID filter. Omit to list across all tenants.",
|
|
1083
|
-
},
|
|
1084
|
-
limit: {
|
|
1085
|
-
type: "number",
|
|
1086
|
-
description: "Max rows to return (default 100, max 500).",
|
|
1087
|
-
},
|
|
1088
|
-
},
|
|
1089
|
-
required: [],
|
|
1090
|
-
},
|
|
1091
|
-
},
|
|
1092
|
-
{
|
|
1093
|
-
name: "ctx_contradictions_list",
|
|
1094
|
-
description: "List contradictions detected by the proactive contradiction hunter (T360) for the caller's tenant. Each row is a high-similarity context pair the LLM classifier judged as disagreeing, with severity, rationale, whether the T352 dispute flip actually applied or was held by the T362 blast-radius gate, and (when applied) the context_links id. Complements ctx_link (which disputes explicitly) -- this surfaces disagreements the hunter found on its own. Resolve via the existing POST /api/contexts/:id/resolve-contradiction route (ctx_get the disputed context id first if unclear which side is 'target').",
|
|
1095
|
-
inputSchema: {
|
|
1096
|
-
type: "object",
|
|
1097
|
-
properties: {
|
|
1098
|
-
workspace: {
|
|
1099
|
-
type: "string",
|
|
1100
|
-
description: "Optional workspace slug filter. Omit to use the active scope, or list across all workspaces if none is set.",
|
|
1101
|
-
},
|
|
1102
|
-
status: {
|
|
1103
|
-
type: "string",
|
|
1104
|
-
enum: ["open", "resolved", "dismissed"],
|
|
1105
|
-
description: "Filter by queue status (default 'open').",
|
|
1106
|
-
},
|
|
1107
|
-
limit: {
|
|
1108
|
-
type: "number",
|
|
1109
|
-
description: "Max rows to return (default 50, max 200).",
|
|
1110
|
-
},
|
|
1111
|
-
},
|
|
1112
|
-
required: [],
|
|
1113
|
-
},
|
|
1114
|
-
},
|
|
1115
|
-
{
|
|
1116
|
-
name: "ctx_admin_rate_limit_get",
|
|
1117
|
-
description: "Inspect the in-memory per-tenant rate-limit buckets. Returns one row per tenant currently tracked by the limiter (tenant_id, tenant_slug, tokens remaining, capacity, refill_per_min). The bucket map lives in process memory, so the snapshot reflects only the API instance that handled the request. Useful for ops triage when a tenant is reporting 429s. Superadmin scope only.",
|
|
1118
|
-
inputSchema: {
|
|
1119
|
-
type: "object",
|
|
1120
|
-
properties: {},
|
|
1121
|
-
required: [],
|
|
1122
|
-
},
|
|
1123
|
-
},
|
|
1124
|
-
{
|
|
1125
|
-
name: "ctx_admin_rate_limit_set",
|
|
1126
|
-
description: "Override the per-tenant rate limit. Patches `tenants.settings.rate_limit` with the provided fields and invalidates the cached bucket so the new config takes effect on the tenant's next request. Defaults are 600 capacity / 600 refill per minute. At least one of capacity or refill_per_min must be provided. Superadmin scope only.",
|
|
1127
|
-
inputSchema: {
|
|
1128
|
-
type: "object",
|
|
1129
|
-
properties: {
|
|
1130
|
-
tenant_id: {
|
|
1131
|
-
type: "number",
|
|
1132
|
-
description: "Tenant ID to override.",
|
|
1133
|
-
},
|
|
1134
|
-
capacity: {
|
|
1135
|
-
type: "number",
|
|
1136
|
-
description: "Maximum tokens the bucket can hold (positive integer).",
|
|
1137
|
-
},
|
|
1138
|
-
refill_per_min: {
|
|
1139
|
-
type: "number",
|
|
1140
|
-
description: "Tokens added back per minute (positive number).",
|
|
1141
|
-
},
|
|
1142
|
-
},
|
|
1143
|
-
required: ["tenant_id"],
|
|
1144
|
-
},
|
|
1145
|
-
},
|
|
1146
|
-
{
|
|
1147
|
-
name: "ctx_memory_review_run",
|
|
1148
|
-
description: "Manually trigger a one-shot memory review pass for a tenant. Samples a handful of older + well-connected contexts (default 5, env MEMORY_REVIEW_SAMPLE_SIZE), asks the LLM to verdict each, and persists results to memory_review_logs. Returns a summary with per-verdict counters and how many archives were auto-applied (only when LLM confidence > 0.8). Useful for ad-hoc audits without waiting for the scheduler. Superadmin scope only.",
|
|
1149
|
-
inputSchema: {
|
|
1150
|
-
type: "object",
|
|
1151
|
-
properties: {
|
|
1152
|
-
tenant_id: {
|
|
1153
|
-
type: ["number", "null"],
|
|
1154
|
-
description: "Tenant to review. Pass null to review tenant-less contexts. Omit to default to the caller's tenant.",
|
|
1155
|
-
},
|
|
1156
|
-
},
|
|
1157
|
-
required: [],
|
|
1158
|
-
},
|
|
1159
|
-
},
|
|
1160
|
-
{
|
|
1161
|
-
name: "ctx_audit_cleanup_run",
|
|
1162
|
-
description: "Manually trigger one pass of the audit log retention cleaner. Deletes rows from activity_logs whose created_at is older than retention_days (capped at the AUDIT_LOG_RETENTION_DAYS env default when omitted; floor of 7 days is always enforced server-side to prevent nuking recent history). Single call deletes at most 5000 rows; rerun if more remain. Returns { deleted, retention_days }. Superadmin scope only.",
|
|
1163
|
-
inputSchema: {
|
|
1164
|
-
type: "object",
|
|
1165
|
-
properties: {
|
|
1166
|
-
retention_days: {
|
|
1167
|
-
type: "number",
|
|
1168
|
-
description: "Override the env-configured retention window for this single run. Server enforces a minimum of 7 days. Omit to use AUDIT_LOG_RETENTION_DAYS (default 90).",
|
|
1169
|
-
},
|
|
1170
|
-
},
|
|
1171
|
-
required: [],
|
|
1172
|
-
},
|
|
1173
|
-
},
|
|
1174
|
-
{
|
|
1175
|
-
name: "ctx_health",
|
|
1176
|
-
description: "Run the deep health probe and return the full report. Probes DB, Elasticsearch, embedding provider, LLM provider, and scheduler states. 30-second in-memory cache on the server. Returns `{status, components, schedulers, queue}` - status is `ok|degraded|fail`. Useful for ad-hoc prod health checks from MCP clients.",
|
|
1177
|
-
inputSchema: {
|
|
1178
|
-
type: "object",
|
|
1179
|
-
properties: {},
|
|
1180
|
-
required: [],
|
|
1181
|
-
},
|
|
1182
|
-
},
|
|
1183
|
-
{
|
|
1184
|
-
name: "ctx_audit_chain_status",
|
|
1185
|
-
description: "Verify the per-row sha256 chain of activity_logs. Returns `{ok: true, total_checked}` when intact, or `{ok: false, broken_at_id, total_checked}` when a row's prev_hash mismatches the previous row's row_hash. Accepts `prev_hash=''` as valid chain-restart genesis (see migration 0029 + PR #157). Superadmin scope only.",
|
|
1186
|
-
inputSchema: {
|
|
1187
|
-
type: "object",
|
|
1188
|
-
properties: {},
|
|
1189
|
-
required: [],
|
|
1190
|
-
},
|
|
1191
|
-
},
|
|
1192
|
-
{
|
|
1193
|
-
name: "ctx_admin_queue_stats",
|
|
1194
|
-
description: "Inspect the in-process LLM job queue. Returns `{pending, running, completed, failed, rejected, capacity:{concurrency, maxPending}, enabled}`. Counters reflect a single API instance because the queue is in-process. Use after a burst of writes or 429s from a provider to confirm the queue is not saturated. Superadmin scope only.",
|
|
1195
|
-
inputSchema: {
|
|
1196
|
-
type: "object",
|
|
1197
|
-
properties: {},
|
|
1198
|
-
required: [],
|
|
1199
|
-
},
|
|
1200
|
-
},
|
|
1201
|
-
// -------------------------------------------------------------------------
|
|
1202
|
-
// Agent Memory Layer (runtime continuity). Call agent_boot at startup, then
|
|
1203
|
-
// checkpoint/tick/lesson as you work, then agent_handoff at the end. This is
|
|
1204
|
-
// the "A-Z non-stop, never forget" loop. See docs/design/agent-memory-layer.md
|
|
1205
|
-
// -------------------------------------------------------------------------
|
|
1206
|
-
{
|
|
1207
|
-
name: "agent_boot",
|
|
1208
|
-
description: "Boot an autonomous agent: ONE call that returns everything needed to start or resume work, token-budgeted. Returns {agent, session:{...,role}, resume:{checkpoint_summary, open_tasks}, handoff:{tldr, source: wrap|dream|safety-net|unknown — T825 ranks a wrap SEED above a dream synthesis or a SessionEnd safety-net stub written moments later}, lessons:[], facts:[], brief, skills:[], skills_full, repo_map, siblings:[], budget:{limit, used, dropped}, client:{recommended_wiring_version, min_wiring_version}}. Call this FIRST in any agent run. If a non-terminal session exists for this agent (or session_id is given) the resume slot tells you exactly where you left off. `goal` drives the relevant-facts, lessons, AND skills retrieval. `skills` (T376) offers already-distilled, parametrized procedures ({context_id, name, description, success_count, similarity}) mined from repeated, independently-verified past runs matching this goal — check it BEFORE re-deriving a solution from scratch. T506: on a session's SECOND and later boots, `skills` is diffed down to only entries that are new or changed (re-distilled) since that session's OWN previous boot — `skills_full` tells you whether the array is the full current match set (true) or this diffed subset (false); an empty `skills` with `skills_full:false` means nothing new matched, not that there are no skills at all. Pass `full:true` to always get the complete current match set (first boots always get the full set regardless). When `project_id` is given you ALSO get a goal-graph situation `brief` {north_star, role (inferred from the work), lane, next (ready-frontier), blocked_on, blocking, done} and the session's role is inferred from the matched goal node. When `include_repo_map:true` and the workspace has code-indexed contexts (from contextq index), you get a `repo_map` {entries:[{path, signatures, rank}], truncated, tokenBudget, omitted} with the top-ranked file signatures from the codebase — use this to answer \"where is X handled\" without greping files. `siblings` (T616) lists OTHER currently-active sessions in the same workspace (tenant-isolated, freshness-filtered to the last ~60min, self-excluded, capped at 5) so you immediately know who else is working here and can coordinate via relay_* or a native SendMessage ping before touching shared resources. Slots fill priority-first (resume > handoff > lessons > facts > brief; skills, repo_map, and siblings use their OWN budget/read, never displace other slots). Overflow reported in budget.dropped. The `client` block carries server-side wiring version advisories (wiring_version 2+ supports repo_map, 3+ supports skills, 4+ supports the skills diff/skills_full/full, 5+ supports siblings). Trigger: user asks to resume/catch up on ongoing work (\"hôm trước tới đâu\", \"tiếp gì\", \"tóm lại đang làm gì\", \"what's next\", \"resume\", \"where did we leave off\", \"catch me up\") — call at the START of a work session. Only call when the request is about tracked work/memory; skip entirely for unrelated casual questions.",
|
|
1209
|
-
inputSchema: {
|
|
1210
|
-
type: "object",
|
|
1211
|
-
properties: {
|
|
1212
|
-
agent: { type: "string", description: "Stable agent slug (handle the agent boots with every run, e.g. 'claude-code')" },
|
|
1213
|
-
agent_name: { type: "string", description: "Human-readable name; used only when the agent is first created" },
|
|
1214
|
-
goal: { type: "string", description: "The objective for this run — drives relevant-facts + lessons retrieval AND goal-node role inference" },
|
|
1215
|
-
workspace: { type: "string", description: "Workspace slug to scope handoff + facts to" },
|
|
1216
|
-
project_id: { type: "number", description: "Project id to scope the goal-graph situation brief + role inference to (omit = no brief, classic pack)" },
|
|
1217
|
-
token_budget: { type: "number", description: "Max tokens for the assembled pack (default 4000)" },
|
|
1218
|
-
session_id: { type: "number", description: "Resume a specific session by id (otherwise the latest active/paused session for this agent)" },
|
|
1219
|
-
include_repo_map: { type: "boolean", description: "When true, include a token-budgeted repo map (entries with path+signatures) from code-indexed contexts. Only useful for workspaces indexed with contextq index. Default false." },
|
|
1220
|
-
repo_map_token_budget: { type: "number", description: "Token cap for the repo map slot (default ~2000, range 100-16000). Ignored when include_repo_map is false." },
|
|
1221
|
-
epistemic_min: { type: "string", enum: ["observed", "told", "inferred", "assumed"], description: "Epistemic floor (T358) for the FACTS slot: only surface facts at or above this confidence tier (weakest->strongest: assumed < inferred < told < observed). Omit for no floor." },
|
|
1222
|
-
full: { type: "boolean", description: "T506: bypass the skills diff-since-last-boot behavior and always return the full current skills match set. Default false (repeat boots of the same session return only new/changed skills)." },
|
|
1223
|
-
},
|
|
1224
|
-
required: ["agent"],
|
|
1225
|
-
},
|
|
1226
|
-
},
|
|
1227
|
-
{
|
|
1228
|
-
name: "agent_session_start",
|
|
1229
|
-
description: "Start a new agent session (a run with a goal). Returns the created session including its id. Use when beginning a fresh task that you want to track and resume. Pass parent_session_id to chain a resumed run to its predecessor.",
|
|
1230
|
-
inputSchema: {
|
|
1231
|
-
type: "object",
|
|
1232
|
-
properties: {
|
|
1233
|
-
agent: { type: "string", description: "Stable agent slug" },
|
|
1234
|
-
agent_name: { type: "string", description: "Human-readable name (used only on first creation)" },
|
|
1235
|
-
goal: { type: "string", description: "The A-Z objective for this run" },
|
|
1236
|
-
workspace: { type: "string", description: "Workspace slug this run operates in" },
|
|
1237
|
-
project: { type: "string", description: "Optional project slug within the workspace" },
|
|
1238
|
-
role: { type: "string", description: "Optional role this session plays (frontend, backend, design, ...). Usually inferred at boot from the matched goal node instead." },
|
|
1239
|
-
parent_session_id: { type: "number", description: "Id of the session this one resumes/continues" },
|
|
1240
|
-
metadata: { type: "object", description: "Arbitrary run metadata" },
|
|
1241
|
-
},
|
|
1242
|
-
required: ["agent"],
|
|
1243
|
-
},
|
|
1244
|
-
},
|
|
1245
|
-
{
|
|
1246
|
-
name: "agent_session_end",
|
|
1247
|
-
description: "End or update an agent session's status. Use status='completed' when the goal is met, 'paused' to suspend (resume later from the checkpoint), 'stalled' when the vibe-loop stall detector trips, or 'abandoned' to drop the run. Setting completed/abandoned stamps ended_at.",
|
|
1248
|
-
inputSchema: {
|
|
1249
|
-
type: "object",
|
|
1250
|
-
properties: {
|
|
1251
|
-
session_id: { type: "number", description: "Session id to update" },
|
|
1252
|
-
status: { type: "string", enum: ["active", "paused", "completed", "stalled", "abandoned"], description: "New session status" },
|
|
1253
|
-
goal: { type: "string", description: "Optionally revise the goal" },
|
|
1254
|
-
metadata: { type: "object", description: "Metadata to merge into the session" },
|
|
1255
|
-
},
|
|
1256
|
-
required: ["session_id", "status"],
|
|
1257
|
-
},
|
|
1258
|
-
},
|
|
1259
|
-
{
|
|
1260
|
-
name: "agent_checkpoint",
|
|
1261
|
-
description: "Snapshot the agent's working state so a restart/crash can resume from exactly here. `state` is an arbitrary JSON scratchpad (cursor, partial results, plan, open files). `summary` is a 1-line 'where I am'. Returns the checkpoint with its monotonic seq. Call periodically after each chunk of progress.",
|
|
1262
|
-
inputSchema: {
|
|
1263
|
-
type: "object",
|
|
1264
|
-
properties: {
|
|
1265
|
-
session_id: { type: "number", description: "Session id to checkpoint" },
|
|
1266
|
-
state: { type: "object", description: "Working-state scratchpad (arbitrary JSON)" },
|
|
1267
|
-
summary: { type: "string", description: "One-line human-readable 'where I am'" },
|
|
1268
|
-
token_estimate: { type: "number", description: "Optional explicit token size of the state (auto-estimated if omitted)" },
|
|
1269
|
-
},
|
|
1270
|
-
required: ["session_id"],
|
|
1271
|
-
},
|
|
1272
|
-
},
|
|
1273
|
-
{
|
|
1274
|
-
name: "agent_context_pressure",
|
|
1275
|
-
description: "Advisory check: compares your estimated_tokens (and optionally turn_count) for this session against a server-side threshold and returns {should_checkpoint, reason, threshold}. Purely advisory — no server-side token accounting is kept, nothing is persisted. When should_checkpoint is true, call agent_checkpoint next to snapshot your working state before continuing.",
|
|
1276
|
-
inputSchema: {
|
|
1277
|
-
type: "object",
|
|
1278
|
-
properties: {
|
|
1279
|
-
session_id: { type: "number", description: "Session id to evaluate" },
|
|
1280
|
-
estimated_tokens: { type: "number", description: "Your own estimate of accumulated context tokens for this run" },
|
|
1281
|
-
turn_count: { type: "number", description: "Optional: number of turns/steps taken so far this run" },
|
|
1282
|
-
},
|
|
1283
|
-
required: ["session_id", "estimated_tokens"],
|
|
1284
|
-
},
|
|
1285
|
-
},
|
|
1286
|
-
{
|
|
1287
|
-
name: "agent_resume",
|
|
1288
|
-
description: "Read the resume bundle for a session WITHOUT booting fresh: latest checkpoint, open tasks (pending/in_progress/blocked), and goal-relevant lessons. Use when you already know the session_id and just need to reload where you left off.",
|
|
1289
|
-
inputSchema: {
|
|
1290
|
-
type: "object",
|
|
1291
|
-
properties: {
|
|
1292
|
-
session_id: { type: "number", description: "Session id to resume" },
|
|
1293
|
-
},
|
|
1294
|
-
required: ["session_id"],
|
|
1295
|
-
},
|
|
1296
|
-
},
|
|
1297
|
-
{
|
|
1298
|
-
name: "agent_task_upsert",
|
|
1299
|
-
description: "Create or update one checklist item in a session's task tree. Omit task_id to create; pass task_id to update. `verify_cmd` names HOW the item is proven done (the agent must run it before ticking). Use parent_task_id for subtasks. This productizes the vibe goal-file checklist. Trigger: call at the START of a tracked piece of work to record a checklist item (session-scoped — for a task meant to persist across sessions use goal_add on the board instead). Only call when the work is actually being tracked; ignore unrelated casual chat.",
|
|
1300
|
-
inputSchema: {
|
|
1301
|
-
type: "object",
|
|
1302
|
-
properties: {
|
|
1303
|
-
session_id: { type: "number", description: "Session that owns this task" },
|
|
1304
|
-
task_id: { type: "number", description: "Existing task id to update (omit to create)" },
|
|
1305
|
-
parent_task_id: { type: "number", description: "Parent task id for a subtask" },
|
|
1306
|
-
title: { type: "string", description: "Task title (required when creating)" },
|
|
1307
|
-
status: { type: "string", enum: ["pending", "in_progress", "verified", "blocked", "skipped"], description: "Task status" },
|
|
1308
|
-
verify_cmd: { type: "string", description: "Command/observation that proves this task done" },
|
|
1309
|
-
order_index: { type: "number", description: "Ordering within the session" },
|
|
1310
|
-
context_id: { type: "number", description: "Optional id of the durable context this task produced" },
|
|
1311
|
-
goal_node_id: { type: "number", description: "Optional goal-graph node this task rolls up to (links session work to the project goal)" },
|
|
1312
|
-
},
|
|
1313
|
-
required: ["session_id"],
|
|
1314
|
-
},
|
|
1315
|
-
},
|
|
1316
|
-
{
|
|
1317
|
-
name: "agent_task_tick",
|
|
1318
|
-
description: "Flip a task's status. Setting status='verified' REQUIRES non-empty `evidence` (real observed output: test result, HTTP status, exit code) — the no-self-certification rule. Returns 400 if you try to verify without evidence. Use this as each checklist item is proven. Trigger: user reports finishing a piece of tracked work (\"xong rồi\", \"xong X\", \"done X\", \"done\", \"mark done\", \"finished X\") — tick the matching session-scoped checklist item here (use goal_advance instead for a board-level task). Only call when it maps to a tracked item; ignore unrelated casual chatter.",
|
|
1319
|
-
inputSchema: {
|
|
1320
|
-
type: "object",
|
|
1321
|
-
properties: {
|
|
1322
|
-
task_id: { type: "number", description: "Task id to tick" },
|
|
1323
|
-
status: { type: "string", enum: ["pending", "in_progress", "verified", "blocked", "skipped"], description: "New status" },
|
|
1324
|
-
evidence: { type: "string", description: "Real observed output proving the task (required to set 'verified')" },
|
|
1325
|
-
},
|
|
1326
|
-
required: ["task_id", "status"],
|
|
1327
|
-
},
|
|
1328
|
-
},
|
|
1329
|
-
{
|
|
1330
|
-
name: "agent_lesson_add",
|
|
1331
|
-
description: "Record a lesson learned during a run so the agent doesn't repeat the failure. Embedded for goal-relevant recall at the next agent_boot. Mirrors the vibe-loop '## Lessons' log. scope controls breadth: 'session' (this run), 'agent' (this agent always), or 'workspace'.",
|
|
1332
|
-
inputSchema: {
|
|
1333
|
-
type: "object",
|
|
1334
|
-
properties: {
|
|
1335
|
-
session_id: { type: "number", description: "Session this lesson came from" },
|
|
1336
|
-
what_failed: { type: "string", description: "What was attempted that failed" },
|
|
1337
|
-
why: { type: "string", description: "Why it failed" },
|
|
1338
|
-
try_instead: { type: "string", description: "What to do differently next time" },
|
|
1339
|
-
scope: { type: "string", enum: ["session", "agent", "workspace"], description: "How broadly the lesson applies (default 'session')" },
|
|
1340
|
-
},
|
|
1341
|
-
required: ["session_id", "what_failed"],
|
|
1342
|
-
},
|
|
1343
|
-
},
|
|
1344
|
-
{
|
|
1345
|
-
name: "agent_handoff",
|
|
1346
|
-
description: "Generate a handoff document for the session's workspace at run end (wraps the dream handoff generator — LLM-synthesized TL;DR + in-progress + next-steps + open-questions). Links the handoff context back to the session. Pass complete=true to also mark the session completed. Requires an LLM provider configured on the server.",
|
|
1347
|
-
inputSchema: {
|
|
1348
|
-
type: "object",
|
|
1349
|
-
properties: {
|
|
1350
|
-
session_id: { type: "number", description: "Session to generate a handoff for" },
|
|
1351
|
-
workspace: { type: "string", description: "Workspace slug (falls back to the session's workspace)" },
|
|
1352
|
-
project: { type: "string", description: "Optional project slug to narrow the handoff" },
|
|
1353
|
-
since_days: { type: "number", description: "Look-back window in days (default 7)" },
|
|
1354
|
-
dry_run: { type: "boolean", description: "Generate without persisting the handoff context" },
|
|
1355
|
-
complete: { type: "boolean", description: "Also set the session status to 'completed'" },
|
|
1356
|
-
},
|
|
1357
|
-
required: ["session_id"],
|
|
1358
|
-
},
|
|
1359
|
-
},
|
|
1360
|
-
{
|
|
1361
|
-
name: "agent_replay",
|
|
1362
|
-
description: "Reconstruct the ordered tool-call sequence for a session (T375 episodic trace). Every tool call this MCP bridge makes is logged fire-and-forget with a one-line LLM-compressed summary; agent_replay returns them in order (seq, tool_name, args, result, duration_ms, summary), optionally bounded to [from_seq, to_seq]. Use this to answer 'what did I actually do in this session' — checkpoints and lessons are lossy summaries, this is the full record. Also feeds ctx_search when include_trace_events is set on a search request ('what did I try before this worked?').",
|
|
1363
|
-
inputSchema: {
|
|
1364
|
-
type: "object",
|
|
1365
|
-
properties: {
|
|
1366
|
-
session_id: { type: "number", description: "Session id to replay" },
|
|
1367
|
-
from_seq: { type: "number", description: "First seq to include (default 1)" },
|
|
1368
|
-
to_seq: { type: "number", description: "Last seq to include (default: latest)" },
|
|
1369
|
-
},
|
|
1370
|
-
required: ["session_id"],
|
|
1371
|
-
},
|
|
1372
|
-
},
|
|
1373
|
-
{
|
|
1374
|
-
name: "agent_skill_suggest",
|
|
1375
|
-
description: "Find already-distilled, parametrized procedures matching a goal (T376). A scheduled job clusters repeated, independently-verified agent_task successes (by goal similarity + tool-call-sequence shingling) into type='procedure' contexts with literal args lifted to {{placeholders}} and lineage back to their source sessions/tasks. Returns {suggestions:[{context_id, name, description, success_count, similarity}]} — fetch the full procedure with ctx_get(context_id) before starting work on a matching goal, instead of re-deriving a solution from scratch. `similarity` is null when no embedding provider is configured (fallback ranks by success_count/recency instead of semantic match). Also surfaced proactively in agent_boot's `skills` slot — call this directly when you want to re-check mid-run or narrow to a specific workspace.",
|
|
1376
|
-
inputSchema: {
|
|
1377
|
-
type: "object",
|
|
1378
|
-
properties: {
|
|
1379
|
-
goal: { type: "string", description: "The objective to find a matching distilled procedure for" },
|
|
1380
|
-
workspace: { type: "string", description: "Optional workspace slug to scope suggestions to" },
|
|
1381
|
-
limit: { type: "number", description: "Max suggestions to return (default 3, max 10)" },
|
|
1382
|
-
},
|
|
1383
|
-
required: ["goal"],
|
|
1384
|
-
},
|
|
1385
|
-
},
|
|
1386
|
-
{
|
|
1387
|
-
name: "agent_identity_attest",
|
|
1388
|
-
description: "Record a model-upgrade for an agent's durable identity (T373). Call this whenever the model backing an agent is swapped (e.g. moving to a newer Claude release) so continuity is provable: appends one hash-chained transition to identity_transitions, linked to the previous transition's row_hash (or '' for the first-ever transition on this identity). Also snapshots a sha256 hash of the REAL agent_boot payload immediately before and after the swap (bootpack_hash_before/after) — an unchanged hash proves memory survived the swap intact. Returns {identity, transition}.",
|
|
1389
|
-
inputSchema: {
|
|
1390
|
-
type: "object",
|
|
1391
|
-
properties: {
|
|
1392
|
-
agent: { type: "string", description: "Stable agent slug whose identity is being attested" },
|
|
1393
|
-
agent_name: { type: "string", description: "Human-readable name; used only when the agent is first created" },
|
|
1394
|
-
workspace: { type: "string", description: "Workspace slug used to assemble the before/after boot-pack snapshot" },
|
|
1395
|
-
new_model_id: { type: "string", description: "The model id now running this agent (e.g. 'claude-sonnet-5-...')" },
|
|
1396
|
-
},
|
|
1397
|
-
required: ["agent", "new_model_id"],
|
|
1398
|
-
},
|
|
1399
|
-
},
|
|
1400
|
-
{
|
|
1401
|
-
name: "agent_identity_verify",
|
|
1402
|
-
description: "Walk an agent's identity_transitions chain from genesis (seq=1) to the latest transition, verifying every prev_hash links to the previous row's row_hash AND every row_hash matches a fresh recompute from current content. Returns {ok:true, total_checked, last_seq} when intact, or {ok:false, broken_at_seq, total_checked, last_seq, reason:'chain_broken'|'row_hash_mismatch'} on the first failure. Use this to prove an agent's identity continuity across any number of model swaps.",
|
|
1403
|
-
inputSchema: {
|
|
1404
|
-
type: "object",
|
|
1405
|
-
properties: {
|
|
1406
|
-
agent: { type: "string", description: "Stable agent slug whose identity chain to verify" },
|
|
1407
|
-
},
|
|
1408
|
-
required: ["agent"],
|
|
1409
|
-
},
|
|
1410
|
-
},
|
|
1411
|
-
{
|
|
1412
|
-
name: "agent_intention_set",
|
|
1413
|
-
description: "Set a prospective memory intention (T374) — 'remember to remember'. Stores a future trigger paired with a payload context to resurface once it fires. trigger_type='event' fires when a matching action/resource is logged (synchronously, off every activity_logs write); 'condition' is evaluated on a periodic poll (currently supports kind='context_count_gte'); 'time' fires at/after a timestamp. When the trigger matches, the payload context surfaces in the NEXT matching agent_boot's push slot with reason='prospective_trigger' — a guaranteed delivery, not a relevance-gated suggestion. Pass agent to scope delivery to one agent's boots only (omit for any agent in the tenant). Pass expires_at so a stale intention never fires past its window. Returns the created intention row.",
|
|
1414
|
-
inputSchema: {
|
|
1415
|
-
type: "object",
|
|
1416
|
-
properties: {
|
|
1417
|
-
agent: { type: "string", description: "Scope delivery to one agent's boots only (omit = any agent in the tenant)" },
|
|
1418
|
-
trigger_type: { type: "string", enum: ["event", "condition", "time"], description: "How this intention fires" },
|
|
1419
|
-
trigger_spec: {
|
|
1420
|
-
type: "object",
|
|
1421
|
-
description: "Trigger definition; shape depends on trigger_type. event: {action?, actionPrefix?, resourceContains?} (at least one of action/actionPrefix required) — matched (AND semantics) against every logged activity's action/resource. condition: {kind:'context_count_gte', workspace?, count} — polled periodically. time: {fireAt: ISO datetime string} — fires at/after this timestamp.",
|
|
1422
|
-
},
|
|
1423
|
-
payload_context_id: { type: "number", description: "The context id to resurface once the trigger fires" },
|
|
1424
|
-
reason: { type: "string", description: "Why this intention exists (human-readable, shown in the audit trail)" },
|
|
1425
|
-
expires_at: { type: "string", description: "ISO datetime after which this intention never fires (optional)" },
|
|
1426
|
-
},
|
|
1427
|
-
required: ["trigger_type", "trigger_spec", "payload_context_id"],
|
|
1428
|
-
},
|
|
1429
|
-
},
|
|
1430
|
-
{
|
|
1431
|
-
name: "agent_intention_list",
|
|
1432
|
-
description: "List prospective memory intentions for this tenant, optionally filtered by agent slug (scope) or status (active|fired|delivered|expired|cancelled).",
|
|
1433
|
-
inputSchema: {
|
|
1434
|
-
type: "object",
|
|
1435
|
-
properties: {
|
|
1436
|
-
agent: { type: "string", description: "Filter to intentions scoped to this agent slug" },
|
|
1437
|
-
status: { type: "string", enum: ["active", "fired", "delivered", "expired", "cancelled"], description: "Filter by status" },
|
|
1438
|
-
},
|
|
1439
|
-
required: [],
|
|
1440
|
-
},
|
|
1441
|
-
},
|
|
1442
|
-
{
|
|
1443
|
-
name: "agent_intention_cancel",
|
|
1444
|
-
description: "Cancel a prospective memory intention before it fires. Only succeeds while the intention is still 'active' — returns 404 if it doesn't exist or has already fired/delivered/expired/been cancelled.",
|
|
1445
|
-
inputSchema: {
|
|
1446
|
-
type: "object",
|
|
1447
|
-
properties: {
|
|
1448
|
-
intention_id: { type: "number", description: "Intention id to cancel" },
|
|
1449
|
-
},
|
|
1450
|
-
required: ["intention_id"],
|
|
1451
|
-
},
|
|
1452
|
-
},
|
|
1453
|
-
// -------------------------------------------------------------------------
|
|
1454
|
-
// Session working scope (team-mode Phase 1). Select the active workspace +
|
|
1455
|
-
// project once, then subsequent reads default to it and writes resolve their
|
|
1456
|
-
// target from it. Clearing scope (workspace=null) returns to ALL workspaces.
|
|
1457
|
-
// -------------------------------------------------------------------------
|
|
1458
|
-
{
|
|
1459
|
-
name: "ctx_use",
|
|
1460
|
-
description: "Select the active working scope (workspace + optional project) for this session/key. Subsequent reads default to this scope; writes resolve their target from it. Pass workspace=null (or omit) to clear back to ALL workspaces. Returns the resolved {workspace, project, persisted}. This is 'select the project/context to work with'.",
|
|
1461
|
-
inputSchema: {
|
|
1462
|
-
type: "object",
|
|
1463
|
-
properties: {
|
|
1464
|
-
workspace: { type: "string", description: "Workspace slug to scope to. Omit or null = ALL workspaces." },
|
|
1465
|
-
project: { type: "string", description: "Optional project slug within the workspace." },
|
|
1466
|
-
},
|
|
1467
|
-
required: [],
|
|
1468
|
-
},
|
|
1469
|
-
},
|
|
1470
|
-
{
|
|
1471
|
-
name: "ctx_scope",
|
|
1472
|
-
description: "Show the current active working scope (workspace + project) for this session/key, or 'ALL workspaces' when nothing is selected. Returns {workspace, project}.",
|
|
1473
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
1474
|
-
},
|
|
1475
|
-
// --- Goal graph (team-mode Phase 2) ---
|
|
1476
|
-
{
|
|
1477
|
-
name: "goal_set_objective",
|
|
1478
|
-
description: "Set (or fetch) the north-star objective for a project — the root of the goal graph everything hangs off. Returns the kind='objective' node, which carries an `objective_run_id` — a run id lazily assigned once and then shared by every subsequent goal_add/goal_advance/goal_link_dep/goal_decompose/goal_import/goal_sync mutation under this objective. Query activity_logs (or goal_list/goal_frontier with objective_run_id) by that value to reconstruct this objective's whole activity story, the same way Claude Code's workflow.run_id reconstructs one workflow run.",
|
|
1479
|
-
inputSchema: {
|
|
1480
|
-
type: "object",
|
|
1481
|
-
properties: {
|
|
1482
|
-
project_id: { type: "number", description: "Project id the goal graph belongs to (optional — omit for a workspace-level graph)" },
|
|
1483
|
-
title: { type: "string", description: "The objective / north-star statement" },
|
|
1484
|
-
why: { type: "string", description: "Optional rationale (stored as the node's context)" },
|
|
1485
|
-
},
|
|
1486
|
-
required: ["title"],
|
|
1487
|
-
},
|
|
1488
|
-
},
|
|
1489
|
-
{
|
|
1490
|
-
name: "goal_add",
|
|
1491
|
-
description: "Add a node to the goal graph. Progressive elaboration: only title is required -- omit parent_id to create a root node (a vague node is created status='draft'); fill the rest as reality reveals it. kind: objective|milestone|goal|work_item|relay. owner_role/status/origin are free strings. Trigger: when a user (including a non-technical one) asks you to remember or hand off a piece of work for later (\"thêm việc\", \"thêm task\", \"todo\", \"add a task\", \"add to the board\"), create a node here with a valid status (draft is fine if details are vague) — this is how a casual request becomes a durable tracked task. Only call when the request is genuinely about work to track; ignore unrelated casual questions.",
|
|
1492
|
-
inputSchema: {
|
|
1493
|
-
type: "object",
|
|
1494
|
-
properties: {
|
|
1495
|
-
project_id: { type: "number", description: "Project id (optional)" },
|
|
1496
|
-
parent_id: { type: "number", description: "Parent node id (containment tree; null/omit for a root)" },
|
|
1497
|
-
title: { type: "string", description: "Node title (the only hard requirement)" },
|
|
1498
|
-
kind: { type: "string", description: "objective|milestone|goal|work_item|relay (default work_item)" },
|
|
1499
|
-
owner_role: { type: "string", description: "Role that owns this node (e.g. frontend, backend, design)" },
|
|
1500
|
-
status: { type: "string", description: "draft|not_started|ready|in_progress|blocked|done|superseded" },
|
|
1501
|
-
origin: { type: "string", description: "greenfield|leverage|migrate|unknown" },
|
|
1502
|
-
size: { type: "string", description: "S|M|L|XL" },
|
|
1503
|
-
effort_weeks: { type: "number", description: "Estimated effort in weeks" },
|
|
1504
|
-
target_weeks: { type: "number", description: "Milestone target (weeks)" },
|
|
1505
|
-
verify_cmd: { type: "string", description: "How a leaf is proven done" },
|
|
1506
|
-
external_ref: { type: "object", description: "External tracker ref, e.g. {jira: 'FIP-123'}" },
|
|
1507
|
-
content: { type: "string", description: "Long description (creates a searchable contexts row)" },
|
|
1508
|
-
},
|
|
1509
|
-
required: ["title"],
|
|
1510
|
-
},
|
|
1511
|
-
},
|
|
1512
|
-
{
|
|
1513
|
-
name: "goal_link_dep",
|
|
1514
|
-
description: "Create a dependency edge between two goal nodes (the DAG that cuts across the containment tree). kind='blocks' (from blocks to) or 'informs'. Rejected with a cycle error if it would make the graph cyclic.",
|
|
1515
|
-
inputSchema: {
|
|
1516
|
-
type: "object",
|
|
1517
|
-
properties: {
|
|
1518
|
-
from_node_id: { type: "number", description: "Source node id" },
|
|
1519
|
-
to_node_id: { type: "number", description: "Target node id" },
|
|
1520
|
-
kind: { type: "string", description: "blocks|informs (default blocks)" },
|
|
1521
|
-
},
|
|
1522
|
-
required: ["from_node_id", "to_node_id"],
|
|
1523
|
-
},
|
|
1524
|
-
},
|
|
1525
|
-
{
|
|
1526
|
-
name: "goal_advance",
|
|
1527
|
-
description: "Advance a node's status. A leaf moving to 'done' REQUIRES non-empty evidence (real observed output) — the no-self-certification rule. Parent status rolls up automatically from children. Trigger: when the user reports finishing a tracked piece of work (\"xong rồi\", \"xong X\", \"done X\", \"done\", \"mark done\", \"finished X\"), advance the matching board node's status here. Only call when the report maps to a tracked board item; ignore unrelated casual chatter.",
|
|
1528
|
-
inputSchema: {
|
|
1529
|
-
type: "object",
|
|
1530
|
-
properties: {
|
|
1531
|
-
node_id: { type: "number", description: "Node id to advance" },
|
|
1532
|
-
status: { type: "string", description: "New status (draft|not_started|ready|in_progress|blocked|done|superseded)" },
|
|
1533
|
-
evidence: { type: "string", description: "Real observed output proving the node (required for a leaf -> done)" },
|
|
1534
|
-
},
|
|
1535
|
-
required: ["node_id", "status"],
|
|
1536
|
-
},
|
|
1537
|
-
},
|
|
1538
|
-
{
|
|
1539
|
-
name: "goal_import",
|
|
1540
|
-
description: "Bulk-import an existing backlog (CSV, e.g. a Jira export) into a living goal tree: parses to nodes, builds the containment tree from epic/phase columns, maps roles from issue type/assignee, and INFERS implicit cross-role dependencies (e.g. an FE task that needs a BE task). Tolerates messy data. Returns {created, deps, nodes}.",
|
|
1541
|
-
inputSchema: {
|
|
1542
|
-
type: "object",
|
|
1543
|
-
properties: {
|
|
1544
|
-
project_id: { type: "number", description: "Project id to import into (optional)" },
|
|
1545
|
-
csv: { type: "string", description: "Raw CSV content" },
|
|
1546
|
-
role_map: { type: "object", description: "Assignee -> role map, e.g. {Nghia:'frontend', Nguyen:'backend'}" },
|
|
1547
|
-
},
|
|
1548
|
-
required: ["csv"],
|
|
1549
|
-
},
|
|
1550
|
-
},
|
|
1551
|
-
{
|
|
1552
|
-
name: "goal_decompose",
|
|
1553
|
-
description: "LLM-expand a goal node into child nodes + dependencies, self-checked by a judge before writing (no garbage on fail). `size` is an ADVISORY granularity hint for the LLM only (small=coarse/fewer children, medium=default/today's behavior, large=fine-grained/more children) — never a hard count enforced by the judge, since decomposition quality beats an exact node count. Returns {proposed, applied, reason, objective_run_id} — applied children share the parent objective's run id (lazily assigned on first decompose if the objective predates run-id tagging). If no LLM is configured returns applied=false, reason='llm_not_configured' (never errors).",
|
|
1554
|
-
inputSchema: {
|
|
1555
|
-
type: "object",
|
|
1556
|
-
properties: {
|
|
1557
|
-
node_id: { type: "number", description: "Node to decompose" },
|
|
1558
|
-
available_roles: { type: "array", items: { type: "string" }, description: "Roles the plan may assign work to" },
|
|
1559
|
-
size: { type: "string", description: "Advisory decomposition granularity: small|medium|large (default medium)" },
|
|
1560
|
-
},
|
|
1561
|
-
required: ["node_id"],
|
|
1562
|
-
},
|
|
1563
|
-
},
|
|
1564
|
-
{
|
|
1565
|
-
name: "goal_list",
|
|
1566
|
-
description: "List goal nodes, filtered. Use to read the graph (your lane, a status column, all milestones, or one objective's whole run via objective_run_id). Returns GoalNode[].",
|
|
1567
|
-
inputSchema: {
|
|
1568
|
-
type: "object",
|
|
1569
|
-
properties: {
|
|
1570
|
-
project_id: { type: "number", description: "Filter by project" },
|
|
1571
|
-
owner_role: { type: "string", description: "Filter by owning role" },
|
|
1572
|
-
status: { type: "string", description: "Filter by status" },
|
|
1573
|
-
kind: { type: "string", description: "Filter by kind" },
|
|
1574
|
-
objective_run_id: { type: "string", description: "Filter to nodes stamped with one objective's run id (from goal_set_objective/goal_get/goal_decompose)" },
|
|
1575
|
-
},
|
|
1576
|
-
required: [],
|
|
1577
|
-
},
|
|
1578
|
-
},
|
|
1579
|
-
{
|
|
1580
|
-
name: "goal_get",
|
|
1581
|
-
description: "Get one goal node with its why-path (walk up to the objective) and its in/out dependency edges. Returns {node, why_path, deps_in, deps_out} — `node.objective_run_id` (and the objective's own entry in why_path) identifies the run whose activity_logs tell this node's whole objective story.",
|
|
1582
|
-
inputSchema: {
|
|
1583
|
-
type: "object",
|
|
1584
|
-
properties: { node_id: { type: "number", description: "Node id" } },
|
|
1585
|
-
required: ["node_id"],
|
|
1586
|
-
},
|
|
1587
|
-
},
|
|
1588
|
-
{
|
|
1589
|
-
name: "goal_frontier",
|
|
1590
|
-
description: "The ready-frontier: nodes (optionally for a role, or scoped to one objective_run_id) whose ALL blocking dependencies are done and that aren't done yet — i.e. 'what can I start NOW'. Returns GoalNode[].",
|
|
1591
|
-
inputSchema: {
|
|
1592
|
-
type: "object",
|
|
1593
|
-
properties: {
|
|
1594
|
-
project_id: { type: "number", description: "Filter by project" },
|
|
1595
|
-
objective_run_id: { type: "string", description: "Scope to one objective's run id" },
|
|
1596
|
-
owner_role: { type: "string", description: "Filter to a role's ready work" },
|
|
1597
|
-
},
|
|
1598
|
-
required: [],
|
|
1599
|
-
},
|
|
1600
|
-
},
|
|
1601
|
-
// --- Directed relays (team-mode Phase 2) ---
|
|
1602
|
-
{
|
|
1603
|
-
name: "relay_open",
|
|
1604
|
-
description: "Open a directed relay (a baton from one role to another, e.g. FE -> BE for an API contract). Built-in kinds: api_contract (frontend->backend), design_handoff (design->frontend); or pass from_role/to_role explicitly. Any role/agent name works immediately, no prior registration or setup call needed -- an unseen role is addressable on its very first relay_open. Born status='open', unclaimed. If your runtime supports cross-session messaging (e.g. Claude Code's ListAgents/SendMessage), consider pinging a plausibly-matching local session right after opening -- relay_inbox stays the source of truth, the ping is just a real-time nudge.",
|
|
1605
|
-
inputSchema: {
|
|
1606
|
-
type: "object",
|
|
1607
|
-
properties: {
|
|
1608
|
-
relay_kind: { type: "string", description: "api_contract | design_handoff (fills from/to role)" },
|
|
1609
|
-
from_role: { type: "string", description: "Originating role (overrides relay_kind)" },
|
|
1610
|
-
to_role: { type: "string", description: "Addressed role (overrides relay_kind)" },
|
|
1611
|
-
title: { type: "string", description: "What's being handed off" },
|
|
1612
|
-
project_id: { type: "number", description: "Project id (optional)" },
|
|
1613
|
-
parent_id: { type: "number", description: "The work item this relay hangs off (optional)" },
|
|
1614
|
-
payload: { type: "object", description: "Structured handoff payload per the kind's schema" },
|
|
1615
|
-
},
|
|
1616
|
-
required: ["title"],
|
|
1617
|
-
},
|
|
1618
|
-
},
|
|
1619
|
-
{
|
|
1620
|
-
name: "relay_inbox",
|
|
1621
|
-
description: "List relays addressed to a role (your inbox). status='open' by default; include_all=true also shows claimed relays (in progress by another session). Returns GoalNode[] (kind='relay').",
|
|
1622
|
-
inputSchema: {
|
|
1623
|
-
type: "object",
|
|
1624
|
-
properties: {
|
|
1625
|
-
role: { type: "string", description: "The to_role whose inbox to read" },
|
|
1626
|
-
project_id: { type: "number", description: "Filter by project" },
|
|
1627
|
-
include_all: { type: "boolean", description: "Also include claimed relays (default false)" },
|
|
1628
|
-
},
|
|
1629
|
-
required: ["role"],
|
|
1630
|
-
},
|
|
1631
|
-
},
|
|
1632
|
-
{
|
|
1633
|
-
name: "relay_get",
|
|
1634
|
-
description: "Fetch one directed cross-role relay/handoff (a baton passed between roles, e.g. frontend to backend) by id. Returns the kind='relay' GoalNode with its status, payload, and claim state. Use once you already have a relay id from relay_open or relay_inbox and want its full detail.",
|
|
1635
|
-
inputSchema: {
|
|
1636
|
-
type: "object",
|
|
1637
|
-
properties: { id: { type: "number", description: "Relay id" } },
|
|
1638
|
-
required: ["id"],
|
|
1639
|
-
},
|
|
1640
|
-
},
|
|
1641
|
-
{
|
|
1642
|
-
name: "relay_claim",
|
|
1643
|
-
description: "Claim a relay (atomic, first-write-wins). If already claimed, returns claimed=false with heldBy=<session id> so you skip it (no double-work). Returns {relay, claimed, heldBy}.",
|
|
1644
|
-
inputSchema: {
|
|
1645
|
-
type: "object",
|
|
1646
|
-
properties: {
|
|
1647
|
-
id: { type: "number", description: "Relay id to claim" },
|
|
1648
|
-
session_id: { type: "number", description: "Your session id (the current baton holder)" },
|
|
1649
|
-
},
|
|
1650
|
-
required: ["id", "session_id"],
|
|
1651
|
-
},
|
|
1652
|
-
},
|
|
1653
|
-
{
|
|
1654
|
-
name: "relay_advance",
|
|
1655
|
-
description: "Advance a relay's lifecycle: open -> claimed -> delivered -> verified -> closed (delivered -> claimed = kickback). 'verified' REQUIRES evidence. Illegal transitions are rejected. Pass session_id to enable the identity-reuse guard: if a DIFFERENT attested agent identity now holds this session than the one that claimed the relay, the advance is flagged (default, response + audit log) or rejected with a retarget error (RELAY_IDENTITY_GUARD_MODE=strict). Omit session_id to skip the guard entirely.",
|
|
1656
|
-
inputSchema: {
|
|
1657
|
-
type: "object",
|
|
1658
|
-
properties: {
|
|
1659
|
-
id: { type: "number", description: "Relay id" },
|
|
1660
|
-
status: { type: "string", description: "Target status" },
|
|
1661
|
-
evidence: { type: "string", description: "Real observed output (required for -> verified)" },
|
|
1662
|
-
session_id: {
|
|
1663
|
-
type: "number",
|
|
1664
|
-
description: "Your session id -- enables the identity-reuse guard against the identity that claimed this relay",
|
|
1665
|
-
},
|
|
1666
|
-
},
|
|
1667
|
-
required: ["id", "status"],
|
|
1668
|
-
},
|
|
1669
|
-
},
|
|
1670
|
-
{
|
|
1671
|
-
name: "relay_nudge",
|
|
1672
|
-
description: "Nudge a stuck 'claimed' relay to wake the holder (only 'claimed' relays can be nudged). Increments a per-relay nudge count; once it reaches the tenant's threshold (default 3), the same call auto-releases the claim back to status='open' so another session can pick it up. Returns {relay, released, nudgeCount, threshold}. If your runtime supports cross-session messaging and the holder's session is locally reachable, send it a native ping alongside this call for a real-time wake instead of waiting on its next poll.",
|
|
1673
|
-
inputSchema: {
|
|
1674
|
-
type: "object",
|
|
1675
|
-
properties: {
|
|
1676
|
-
id: { type: "number", description: "Relay id to nudge" },
|
|
1677
|
-
session_id: { type: "number", description: "Your session id (recorded as the nudger, optional)" },
|
|
1678
|
-
reason: { type: "string", description: "Why you're nudging it (optional, for the audit trail)" },
|
|
1679
|
-
},
|
|
1680
|
-
required: ["id"],
|
|
1681
|
-
},
|
|
1682
|
-
},
|
|
1683
|
-
{
|
|
1684
|
-
name: "ctx_web_search",
|
|
1685
|
-
description: "Search the web through a configured provider (Tavily, Brave, or Serper). Use this when you need to look up current information online, find documentation for a live service, or search the internet for facts not in your training data. Tenant-scoped and gated behind WEB_SEARCH_ENABLED plus an active search credential. Optionally pipe results through ctx_ingest to persist them as knowledge base entries.",
|
|
1686
|
-
inputSchema: {
|
|
1687
|
-
type: "object",
|
|
1688
|
-
properties: {
|
|
1689
|
-
query: { type: "string", description: "Search query string (1-2000 chars)" },
|
|
1690
|
-
maxResults: {
|
|
1691
|
-
type: "number",
|
|
1692
|
-
description: "Max results to return (1-20, default 5)",
|
|
1693
|
-
},
|
|
1694
|
-
ingest: {
|
|
1695
|
-
type: "boolean",
|
|
1696
|
-
description: "If true, pipe each result through the ingest pipeline to persist as knowledge base entries (best-effort, default false)",
|
|
1697
|
-
},
|
|
1698
|
-
},
|
|
1699
|
-
required: ["query"],
|
|
1700
|
-
},
|
|
1701
|
-
},
|
|
1702
|
-
{
|
|
1703
|
-
name: "ctx_feedback",
|
|
1704
|
-
description: "Record a helpful/unhelpful verdict on a context that was returned by ctx_search (or POST /api/retrieve), closing the recall loop back into ranking. Pass the retrieval trace id from ctx_explain_recall (or from a search response's trace metadata) to join the verdict to the exact score breakdown it is about -- omit it if you don't have one, the verdict still records. Verdicts accumulate into a per-context, per-tenant adjustment (bounded by a small cap so no single client can reshape ranking) that folds into the T345 salience/reinforcement term the next time this context is retrieved. Tenant-scoped: a contextId or retrievalTraceId belonging to another tenant is silently dropped rather than accepted.",
|
|
1705
|
-
inputSchema: {
|
|
1706
|
-
type: "object",
|
|
1707
|
-
properties: {
|
|
1708
|
-
contextId: { type: "number", description: "The context id this verdict is about" },
|
|
1709
|
-
used: { type: "boolean", description: "true = the recalled context was actually useful/relevant; false = it was returned but not helpful" },
|
|
1710
|
-
retrievalTraceId: {
|
|
1711
|
-
type: "number",
|
|
1712
|
-
description: "Optional -- the retrieval_traces row id (from ctx_explain_recall or a search trace, NOT the 32-hex trace_id sidecar) this verdict is joined to.",
|
|
1713
|
-
},
|
|
1714
|
-
sessionId: { type: "number", description: "Optional agent session id, for forensic correlation" },
|
|
1715
|
-
source: { type: "string", description: "Optional harness tag (e.g. 'agent-boot', 'manual', 'eval'), max 32 chars" },
|
|
1716
|
-
},
|
|
1717
|
-
required: ["contextId", "used"],
|
|
1718
|
-
},
|
|
1719
|
-
},
|
|
1720
|
-
// --- World-model snapshots + forkable sandboxes (T378) ---
|
|
1721
|
-
{
|
|
1722
|
-
name: "ctx_snapshot_create",
|
|
1723
|
-
description: "Take a content-addressed snapshot of a workspace's memory state (contexts, context_links, and its goal graph) at this moment. Returns {id, manifestHash, sourceWorkspaceId, ...} -- the snapshot id feeds ctx_fork_world for counterfactual replay or safe memory-surgery testing.",
|
|
1724
|
-
inputSchema: {
|
|
1725
|
-
type: "object",
|
|
1726
|
-
properties: {
|
|
1727
|
-
workspace: { type: "string", description: "Workspace slug to snapshot" },
|
|
1728
|
-
},
|
|
1729
|
-
required: ["workspace"],
|
|
1730
|
-
},
|
|
1731
|
-
},
|
|
1732
|
-
{
|
|
1733
|
-
name: "ctx_fork_world",
|
|
1734
|
-
description: "Fork a throwaway SANDBOX workspace cloned from a snapshot (or a fresh snapshot of `workspace` taken on the fly). Mutate memory inside the sandbox freely -- production rows are never touched. Sandboxes are quota-capped per tenant and auto-expire after ttl_minutes (default from WORLD_SANDBOX_TTL_MINUTES). Returns {sandboxWorkspaceSlug, snapshotId, expiresAt, clonedCounts}.",
|
|
1735
|
-
inputSchema: {
|
|
1736
|
-
type: "object",
|
|
1737
|
-
properties: {
|
|
1738
|
-
snapshot_id: { type: "number", description: "Fork from an existing snapshot id" },
|
|
1739
|
-
workspace: { type: "string", description: "Or: take a fresh snapshot of this workspace slug and fork it" },
|
|
1740
|
-
ttl_minutes: { type: "number", description: "Sandbox lifetime in minutes (0 = no expiry; default from WORLD_SANDBOX_TTL_MINUTES)" },
|
|
1741
|
-
},
|
|
1742
|
-
required: [],
|
|
1743
|
-
},
|
|
1744
|
-
},
|
|
1745
|
-
{
|
|
1746
|
-
name: "ctx_diff_world",
|
|
1747
|
-
description: "Diff a sandbox workspace's CURRENT rows against the snapshot it was forked from -- added/modified/removed/unchanged contexts, plus coarse counts for context_links and the goal graph. Use before deciding whether to apply a sandbox experiment's changes back to production (there is no auto-merge -- replay the accepted changes yourself, then ctx_discard_sandbox).",
|
|
1748
|
-
inputSchema: {
|
|
1749
|
-
type: "object",
|
|
1750
|
-
properties: {
|
|
1751
|
-
sandbox_workspace: { type: "string", description: "Sandbox workspace slug (returned by ctx_fork_world)" },
|
|
1752
|
-
},
|
|
1753
|
-
required: ["sandbox_workspace"],
|
|
1754
|
-
},
|
|
1755
|
-
},
|
|
1756
|
-
{
|
|
1757
|
-
name: "ctx_discard_sandbox",
|
|
1758
|
-
description: "Hard-delete a sandbox workspace and everything in it (contexts, links, cloned goal nodes/deps). Irreversible. Sandboxes past their TTL are also swept automatically by a background job.",
|
|
1759
|
-
inputSchema: {
|
|
1760
|
-
type: "object",
|
|
1761
|
-
properties: {
|
|
1762
|
-
sandbox_workspace: { type: "string", description: "Sandbox workspace slug to discard" },
|
|
1763
|
-
},
|
|
1764
|
-
required: ["sandbox_workspace"],
|
|
1765
|
-
},
|
|
1766
|
-
},
|
|
1767
|
-
// --- T668: tool-profile discovery (always present, in every profile) ---
|
|
1768
|
-
{
|
|
1769
|
-
name: "ctx_tool_groups",
|
|
1770
|
-
description: "List additional groups of ContextQ tools not loaded in this session by default -- code-graph lookup, admin/audit, relay handoff, world-model snapshots, saved searches, knowledge-graph traversal, bulk import/ingest, and more. Search here first if a ContextQ tool you expect (a saved search, a relay, a snapshot, a code reference) is missing from your current tool list. Returns each group's name, one-line purpose, member tool names, and how many of them are already loaded, plus how to load a group with ctx_load_tool_group.",
|
|
1771
|
-
inputSchema: {
|
|
1772
|
-
type: "object",
|
|
1773
|
-
properties: {},
|
|
1774
|
-
additionalProperties: false,
|
|
1775
|
-
},
|
|
1776
|
-
},
|
|
1777
|
-
{
|
|
1778
|
-
name: "ctx_load_tool_group",
|
|
1779
|
-
description: "Load one additional group of ContextQ tools into this session (group names come from ctx_tool_groups) so they become callable without reconnecting. Pass \"all\" to load every remaining ContextQ tool at once. Some MCP clients need to refresh their tool list to actually see newly loaded tools in the model's context -- if a loaded tool still doesn't show up, call it directly by name anyway (ContextQ accepts a tool call for any known tool name regardless of what tools/list currently returns), or restart this server with the environment variable CONTEXT_MCP_TOOL_PROFILE=full to get every tool from the start.",
|
|
1780
|
-
inputSchema: {
|
|
1781
|
-
type: "object",
|
|
1782
|
-
properties: {
|
|
1783
|
-
group: {
|
|
1784
|
-
type: "string",
|
|
1785
|
-
description: "Group name from ctx_tool_groups (e.g. \"pkm\", \"admin\", \"relay\", \"knowledge-graph\"), or \"all\" to load every remaining ContextQ tool.",
|
|
1786
|
-
},
|
|
1787
|
-
},
|
|
1788
|
-
required: ["group"],
|
|
1789
|
-
additionalProperties: false,
|
|
1790
|
-
},
|
|
1791
|
-
},
|
|
1792
|
-
];
|
|
1793
|
-
// Every tool NOT in DEFAULT_PROFILE_TOOL_NAMES must appear in exactly one
|
|
1794
|
-
// group here -- enforced by an exhaustiveness check in
|
|
1795
|
-
// tools.profile.test.ts, so a newly added tool can't silently fall out of
|
|
1796
|
-
// both the default profile and every group's reach.
|
|
1797
|
-
export const TOOL_GROUPS = {
|
|
1798
|
-
"search-advanced": {
|
|
1799
|
-
description: "Explainable-recall score breakdowns, live web search, and recall-loop feedback beyond the default ctx_search.",
|
|
1800
|
-
tools: ["ctx_explain_recall", "ctx_web_search", "ctx_feedback"],
|
|
1801
|
-
},
|
|
1802
|
-
"crud-advanced": {
|
|
1803
|
-
description: "Bulk updates, archive-undo, and per-entry usage/scope tracking beyond the default single-entry CRUD.",
|
|
1804
|
-
tools: ["ctx_bulk_update", "ctx_undo_archive", "ctx_undo_archive_info", "ctx_use", "ctx_scope"],
|
|
1805
|
-
},
|
|
1806
|
-
ingest: {
|
|
1807
|
-
description: "Bulk document/URL import and raw ingest-job status.",
|
|
1808
|
-
tools: ["ctx_import", "ctx_ingest", "ctx_ingest_status"],
|
|
1809
|
-
},
|
|
1810
|
-
pkm: {
|
|
1811
|
-
description: "Personal-knowledge-management synthesis: dream runs, context linking, evolution, chunk inspection, and maps-of-content.",
|
|
1812
|
-
tools: ["ctx_dream", "ctx_link", "ctx_links", "ctx_evolve", "ctx_chunks", "ctx_mocs", "ctx_regenerate_mocs", "ctx_maps"],
|
|
1813
|
-
},
|
|
1814
|
-
code: {
|
|
1815
|
-
description: "Code-graph reference lookup and call-trace queries for code-indexed contexts.",
|
|
1816
|
-
tools: ["ctx_code_refs", "ctx_code_trace"],
|
|
1817
|
-
},
|
|
1818
|
-
"knowledge-graph": {
|
|
1819
|
-
description: "Provenance, confidence, belief history, blast-radius, entity, and contradiction traversal over the knowledge graph.",
|
|
1820
|
-
tools: [
|
|
1821
|
-
"ctx_provenance_get",
|
|
1822
|
-
"ctx_confidence_get",
|
|
1823
|
-
"ctx_belief_history",
|
|
1824
|
-
"ctx_blast_radius",
|
|
1825
|
-
"ctx_graph_search",
|
|
1826
|
-
"ctx_entity_get",
|
|
1827
|
-
"ctx_contradictions_list",
|
|
1828
|
-
],
|
|
1829
|
-
},
|
|
1830
|
-
"saved-searches": {
|
|
1831
|
-
description: "CRUD and manual/scheduled run of saved/recurring searches.",
|
|
1832
|
-
tools: [
|
|
1833
|
-
"ctx_saved_searches_list",
|
|
1834
|
-
"ctx_saved_searches_create",
|
|
1835
|
-
"ctx_saved_searches_get",
|
|
1836
|
-
"ctx_saved_searches_update",
|
|
1837
|
-
"ctx_saved_searches_delete",
|
|
1838
|
-
"ctx_saved_searches_run",
|
|
1839
|
-
"ctx_run_saved_search",
|
|
1840
|
-
],
|
|
1841
|
-
},
|
|
1842
|
-
admin: {
|
|
1843
|
-
description: "Operational/admin tools: recent-events feed, memory-review logs and runs, rate limits, audit cleanup/chain status, queue stats.",
|
|
1844
|
-
tools: [
|
|
1845
|
-
"ctx_events_recent",
|
|
1846
|
-
"ctx_memory_review_logs",
|
|
1847
|
-
"ctx_admin_rate_limit_get",
|
|
1848
|
-
"ctx_admin_rate_limit_set",
|
|
1849
|
-
"ctx_memory_review_run",
|
|
1850
|
-
"ctx_audit_cleanup_run",
|
|
1851
|
-
"ctx_audit_chain_status",
|
|
1852
|
-
"ctx_admin_queue_stats",
|
|
1853
|
-
],
|
|
1854
|
-
},
|
|
1855
|
-
"agent-advanced": {
|
|
1856
|
-
description: "Context-pressure checks, episodic tool-call replay, procedural skill suggestions, identity attestation, and intention tracking beyond the default agent session loop.",
|
|
1857
|
-
tools: [
|
|
1858
|
-
"agent_context_pressure",
|
|
1859
|
-
"agent_replay",
|
|
1860
|
-
"agent_skill_suggest",
|
|
1861
|
-
"agent_identity_attest",
|
|
1862
|
-
"agent_identity_verify",
|
|
1863
|
-
"agent_intention_set",
|
|
1864
|
-
"agent_intention_list",
|
|
1865
|
-
"agent_intention_cancel",
|
|
1866
|
-
],
|
|
1867
|
-
},
|
|
1868
|
-
"goal-advanced": {
|
|
1869
|
-
description: "Objective setup, dependency links, import, decomposition, and single-node reads on the goal graph beyond the default add/advance/list/frontier.",
|
|
1870
|
-
tools: ["goal_set_objective", "goal_link_dep", "goal_import", "goal_decompose", "goal_get"],
|
|
1871
|
-
},
|
|
1872
|
-
relay: {
|
|
1873
|
-
description: "Directed cross-role handoff: open, inbox, get, claim, advance, nudge.",
|
|
1874
|
-
tools: ["relay_open", "relay_inbox", "relay_get", "relay_claim", "relay_advance", "relay_nudge"],
|
|
1875
|
-
},
|
|
1876
|
-
"world-model": {
|
|
1877
|
-
description: "Content-addressed workspace snapshots and forkable sandboxes for counterfactual memory experiments.",
|
|
1878
|
-
tools: ["ctx_snapshot_create", "ctx_fork_world", "ctx_diff_world", "ctx_discard_sandbox"],
|
|
1879
|
-
},
|
|
1880
|
-
};
|
|
1881
|
-
// The default profile: session lifecycle (boot/start/end/checkpoint/resume),
|
|
1882
|
-
// the core search+CRUD loop, task + lesson tracking, goal-graph basics, and
|
|
1883
|
-
// the two tool-discovery tools themselves. Deliberately excludes every tool
|
|
1884
|
-
// listed in TOOL_GROUPS above.
|
|
1885
|
-
export const DEFAULT_PROFILE_TOOL_NAMES = new Set([
|
|
1886
|
-
"agent_boot",
|
|
1887
|
-
"agent_session_start",
|
|
1888
|
-
"agent_session_end",
|
|
1889
|
-
"agent_checkpoint",
|
|
1890
|
-
"agent_resume",
|
|
1891
|
-
"agent_task_upsert",
|
|
1892
|
-
"agent_task_tick",
|
|
1893
|
-
"agent_lesson_add",
|
|
1894
|
-
"agent_handoff",
|
|
1895
|
-
"ctx_search",
|
|
1896
|
-
"ctx_save",
|
|
1897
|
-
"ctx_get",
|
|
1898
|
-
"ctx_list",
|
|
1899
|
-
"ctx_update",
|
|
1900
|
-
"ctx_delete",
|
|
1901
|
-
"ctx_remember",
|
|
1902
|
-
"ctx_stats",
|
|
1903
|
-
"ctx_health",
|
|
1904
|
-
"goal_add",
|
|
1905
|
-
"goal_advance",
|
|
1906
|
-
"goal_list",
|
|
1907
|
-
"goal_frontier",
|
|
1908
|
-
"ctx_tool_groups",
|
|
1909
|
-
"ctx_load_tool_group",
|
|
1910
|
-
]);
|
|
1911
|
-
function resolveInitialToolProfile() {
|
|
1912
|
-
const raw = (process.env.CONTEXT_MCP_TOOL_PROFILE ?? "").trim().toLowerCase();
|
|
1913
|
-
return raw === "full" ? "full" : "default";
|
|
1914
|
-
}
|
|
1915
|
-
// Mutable -- ctx_load_tool_group grows this at runtime so a session that
|
|
1916
|
-
// started on the default profile can still reach the full surface without
|
|
1917
|
-
// reconnecting. One MCP stdio process = one client session, same lifetime
|
|
1918
|
-
// assumption `currentSessionId`/`brainStateInjected` below already rely on.
|
|
1919
|
-
const activeToolNames = resolveInitialToolProfile() === "full" ? new Set(tools.map((t) => t.name)) : new Set(DEFAULT_PROFILE_TOOL_NAMES);
|
|
1920
|
-
export function getActiveTools() {
|
|
1921
|
-
return tools.filter((t) => activeToolNames.has(t.name));
|
|
1922
|
-
}
|
|
1923
|
-
// Promotes a group's tools into the active set. Returns which names were
|
|
1924
|
-
// newly added (already-active ones are skipped) and whether `group` matched
|
|
1925
|
-
// nothing, so the caller can report both cases without duplicating the
|
|
1926
|
-
// lookup. index.ts uses the return value to decide whether to emit
|
|
1927
|
-
// notifications/tools/list_changed.
|
|
1928
|
-
export function loadToolGroup(group) {
|
|
1929
|
-
const key = group.trim().toLowerCase();
|
|
1930
|
-
const names = key === "all" ? tools.map((t) => t.name) : TOOL_GROUPS[key]?.tools;
|
|
1931
|
-
if (!names)
|
|
1932
|
-
return { added: [], unknownGroup: true };
|
|
1933
|
-
const added = [];
|
|
1934
|
-
for (const name of names) {
|
|
1935
|
-
if (!activeToolNames.has(name)) {
|
|
1936
|
-
activeToolNames.add(name);
|
|
1937
|
-
added.push(name);
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
return { added, unknownGroup: false };
|
|
1941
|
-
}
|
|
1942
|
-
const handlers = {
|
|
1943
|
-
async ctx_save(input) {
|
|
1944
|
-
return request("POST", "/api/contexts", input);
|
|
1945
|
-
},
|
|
1946
|
-
async ctx_search(input) {
|
|
1947
|
-
return request("POST", "/api/search", input);
|
|
1948
|
-
},
|
|
1949
|
-
async ctx_explain_recall(input) {
|
|
1950
|
-
const { id } = input;
|
|
1951
|
-
return request("GET", `/api/search/traces/${id}/explain`);
|
|
1952
|
-
},
|
|
1953
|
-
async ctx_web_search(input) {
|
|
1954
|
-
return request("POST", "/api/search/web", input);
|
|
1955
|
-
},
|
|
1956
|
-
async ctx_feedback(input) {
|
|
1957
|
-
return request("POST", "/api/memory-push/feedback", input);
|
|
1958
|
-
},
|
|
1959
|
-
async ctx_list(input) {
|
|
1960
|
-
const qs = toQueryString(input);
|
|
1961
|
-
return request("GET", `/api/contexts${qs}`);
|
|
1962
|
-
},
|
|
1963
|
-
async ctx_get(input) {
|
|
1964
|
-
return request("GET", `/api/contexts/${input.id}`);
|
|
1965
|
-
},
|
|
1966
|
-
async ctx_update(input) {
|
|
1967
|
-
const { id, ...body } = input;
|
|
1968
|
-
return request("PUT", `/api/contexts/${id}`, body);
|
|
1969
|
-
},
|
|
1970
|
-
async ctx_delete(input) {
|
|
1971
|
-
return request("DELETE", `/api/contexts/${input.id}`);
|
|
1972
|
-
},
|
|
1973
|
-
async ctx_bulk_update(input) {
|
|
1974
|
-
return request("POST", "/api/contexts/bulk", input);
|
|
1975
|
-
},
|
|
1976
|
-
async ctx_undo_archive(input) {
|
|
1977
|
-
const { id } = input;
|
|
1978
|
-
return request("POST", `/api/contexts/${id}/undo-archive`, {});
|
|
1979
|
-
},
|
|
1980
|
-
async ctx_undo_archive_info(input) {
|
|
1981
|
-
const { id } = input;
|
|
1982
|
-
return request("GET", `/api/contexts/${id}/undo-archive-info`);
|
|
1983
|
-
},
|
|
1984
|
-
async ctx_import(input) {
|
|
1985
|
-
return request("POST", "/api/import", input);
|
|
1986
|
-
},
|
|
1987
|
-
async ctx_stats() {
|
|
1988
|
-
return request("GET", "/api/stats");
|
|
1989
|
-
},
|
|
1990
|
-
async ctx_dream(input) {
|
|
1991
|
-
return request("POST", "/api/dream", input);
|
|
1992
|
-
},
|
|
1993
|
-
async ctx_link(input) {
|
|
1994
|
-
const { source_id, target_id, type, confidence, reason } = input;
|
|
1995
|
-
const body = { target_id, type };
|
|
1996
|
-
if (confidence !== undefined)
|
|
1997
|
-
body.confidence = confidence;
|
|
1998
|
-
// The API persists the rationale via the `created_by` annotation column.
|
|
1999
|
-
if (reason !== undefined)
|
|
2000
|
-
body.created_by = reason;
|
|
2001
|
-
return request("POST", `/api/contexts/${source_id}/links`, body);
|
|
2002
|
-
},
|
|
2003
|
-
async ctx_links(input) {
|
|
2004
|
-
const { id, direction } = input;
|
|
2005
|
-
const qs = toQueryString({ direction: direction ?? "both" });
|
|
2006
|
-
return request("GET", `/api/contexts/${id}/links${qs}`);
|
|
2007
|
-
},
|
|
2008
|
-
async ctx_evolve(input) {
|
|
2009
|
-
const { id, k_neighbors, dry_run } = input;
|
|
2010
|
-
const body = {};
|
|
2011
|
-
if (k_neighbors !== undefined)
|
|
2012
|
-
body.k_neighbors = k_neighbors;
|
|
2013
|
-
if (dry_run !== undefined)
|
|
2014
|
-
body.dry_run = dry_run;
|
|
2015
|
-
return request("POST", `/api/contexts/${id}/evolve`, body);
|
|
2016
|
-
},
|
|
2017
|
-
async ctx_chunks(input) {
|
|
2018
|
-
return request("GET", `/api/contexts/${input.id}/chunks`);
|
|
2019
|
-
},
|
|
2020
|
-
async ctx_code_refs(input) {
|
|
2021
|
-
const { ref } = input;
|
|
2022
|
-
return request("GET", `/api/code/refs${toQueryString({ ref })}`);
|
|
2023
|
-
},
|
|
2024
|
-
async ctx_code_trace(input) {
|
|
2025
|
-
const { ref, depth } = input;
|
|
2026
|
-
return request("GET", `/api/code/trace${toQueryString({ ref, depth })}`);
|
|
2027
|
-
},
|
|
2028
|
-
async ctx_provenance_get(input) {
|
|
2029
|
-
const { id, depth } = input;
|
|
2030
|
-
return request("GET", `/api/contexts/${id}/provenance${toQueryString({ depth })}`);
|
|
2031
|
-
},
|
|
2032
|
-
async ctx_confidence_get(input) {
|
|
2033
|
-
const { id } = input;
|
|
2034
|
-
return request("GET", `/api/contexts/${id}/confidence`);
|
|
2035
|
-
},
|
|
2036
|
-
async ctx_belief_history(input) {
|
|
2037
|
-
const { id, limit } = input;
|
|
2038
|
-
return request("GET", `/api/contexts/${id}/belief-events${toQueryString({ limit })}`);
|
|
2039
|
-
},
|
|
2040
|
-
async ctx_blast_radius(input) {
|
|
2041
|
-
const { id, depth, top } = input;
|
|
2042
|
-
return request("GET", `/api/contexts/${id}/blast-radius${toQueryString({ depth, top })}`);
|
|
2043
|
-
},
|
|
2044
|
-
async ctx_graph_search(input) {
|
|
2045
|
-
const { query, as_of, limit } = input;
|
|
2046
|
-
const body = { query };
|
|
2047
|
-
if (as_of !== undefined)
|
|
2048
|
-
body.asOf = as_of;
|
|
2049
|
-
if (limit !== undefined)
|
|
2050
|
-
body.limit = limit;
|
|
2051
|
-
return request("POST", "/api/graph/search", body);
|
|
2052
|
-
},
|
|
2053
|
-
async ctx_entity_get(input) {
|
|
2054
|
-
const { id, as_of } = input;
|
|
2055
|
-
return request("GET", `/api/graph/entities/${id}${toQueryString({ asOf: as_of })}`);
|
|
2056
|
-
},
|
|
2057
|
-
async ctx_mocs(input) {
|
|
2058
|
-
const qs = toQueryString(input);
|
|
2059
|
-
return request("GET", `/api/mocs${qs}`);
|
|
2060
|
-
},
|
|
2061
|
-
async ctx_regenerate_mocs() {
|
|
2062
|
-
return request("POST", "/api/mocs/regenerate", {});
|
|
2063
|
-
},
|
|
2064
|
-
async ctx_maps(input) {
|
|
2065
|
-
const qs = toQueryString(input);
|
|
2066
|
-
return request("GET", `/api/maps${qs}`);
|
|
2067
|
-
},
|
|
2068
|
-
async ctx_saved_searches_list() {
|
|
2069
|
-
return request("GET", "/api/saved-searches");
|
|
2070
|
-
},
|
|
2071
|
-
async ctx_saved_searches_create(input) {
|
|
2072
|
-
return request("POST", "/api/saved-searches", input);
|
|
2073
|
-
},
|
|
2074
|
-
async ctx_saved_searches_get(input) {
|
|
2075
|
-
return request("GET", `/api/saved-searches/${input.id}`);
|
|
2076
|
-
},
|
|
2077
|
-
async ctx_saved_searches_update(input) {
|
|
2078
|
-
const { id, ...body } = input;
|
|
2079
|
-
return request("PUT", `/api/saved-searches/${id}`, body);
|
|
2080
|
-
},
|
|
2081
|
-
async ctx_saved_searches_delete(input) {
|
|
2082
|
-
return request("DELETE", `/api/saved-searches/${input.id}`);
|
|
2083
|
-
},
|
|
2084
|
-
async ctx_saved_searches_run(input) {
|
|
2085
|
-
const { id, ...body } = input;
|
|
2086
|
-
return request("POST", `/api/saved-searches/${id}/run`, body);
|
|
2087
|
-
},
|
|
2088
|
-
async ctx_run_saved_search(input) {
|
|
2089
|
-
return request("POST", "/api/saved-searches/run-by-name", input);
|
|
2090
|
-
},
|
|
2091
|
-
async ctx_ingest(input) {
|
|
2092
|
-
return request("POST", "/api/ingest", input);
|
|
2093
|
-
},
|
|
2094
|
-
async ctx_ingest_status(input) {
|
|
2095
|
-
const { job_id } = input;
|
|
2096
|
-
return request("GET", `/api/ingest-jobs/${job_id}`);
|
|
2097
|
-
},
|
|
2098
|
-
async ctx_remember(input) {
|
|
2099
|
-
return request("POST", "/api/memory", input);
|
|
2100
|
-
},
|
|
2101
|
-
async ctx_events_recent(input) {
|
|
2102
|
-
const qs = toQueryString(input);
|
|
2103
|
-
return request("GET", `/api/events/recent${qs}`);
|
|
2104
|
-
},
|
|
2105
|
-
async ctx_memory_review_logs(input) {
|
|
2106
|
-
const qs = toQueryString(input);
|
|
2107
|
-
return request("GET", `/api/admin/memory-review-logs${qs}`);
|
|
2108
|
-
},
|
|
2109
|
-
async ctx_memory_review_run(input) {
|
|
2110
|
-
const body = {};
|
|
2111
|
-
if ("tenant_id" in input)
|
|
2112
|
-
body.tenant_id = input.tenant_id;
|
|
2113
|
-
return request("POST", "/api/admin/memory-review/run", body);
|
|
2114
|
-
},
|
|
2115
|
-
async ctx_contradictions_list(input) {
|
|
2116
|
-
const qs = toQueryString(input);
|
|
2117
|
-
return request("GET", `/api/contradictions${qs}`);
|
|
2118
|
-
},
|
|
2119
|
-
async ctx_audit_cleanup_run(input) {
|
|
2120
|
-
const body = {};
|
|
2121
|
-
if (input.retention_days !== undefined) {
|
|
2122
|
-
body.retention_days = input.retention_days;
|
|
2123
|
-
}
|
|
2124
|
-
return request("POST", "/api/admin/audit-cleanup/run", body);
|
|
2125
|
-
},
|
|
2126
|
-
async ctx_admin_rate_limit_get() {
|
|
2127
|
-
return request("GET", "/api/admin/rate-limit/buckets");
|
|
2128
|
-
},
|
|
2129
|
-
async ctx_admin_rate_limit_set(input) {
|
|
2130
|
-
const { tenant_id, ...body } = input;
|
|
2131
|
-
return request("PUT", `/api/admin/tenants/${tenant_id}/rate-limit`, body);
|
|
2132
|
-
},
|
|
2133
|
-
async ctx_health() {
|
|
2134
|
-
return request("GET", "/health/deep");
|
|
2135
|
-
},
|
|
2136
|
-
async ctx_audit_chain_status() {
|
|
2137
|
-
return request("GET", "/api/admin/audit-logs/verify-chain");
|
|
2138
|
-
},
|
|
2139
|
-
async ctx_admin_queue_stats() {
|
|
2140
|
-
return request("GET", "/api/admin/queue-stats");
|
|
2141
|
-
},
|
|
2142
|
-
// --- Agent Memory Layer ---
|
|
2143
|
-
async agent_boot(input) {
|
|
2144
|
-
return request("POST", "/api/agent/boot", input);
|
|
2145
|
-
},
|
|
2146
|
-
async agent_session_start(input) {
|
|
2147
|
-
return request("POST", "/api/agent/sessions", input);
|
|
2148
|
-
},
|
|
2149
|
-
async agent_session_end(input) {
|
|
2150
|
-
const { session_id, ...body } = input;
|
|
2151
|
-
return request("PATCH", `/api/agent/sessions/${session_id}`, body);
|
|
2152
|
-
},
|
|
2153
|
-
async agent_checkpoint(input) {
|
|
2154
|
-
const { session_id, ...body } = input;
|
|
2155
|
-
return request("POST", `/api/agent/sessions/${session_id}/checkpoint`, body);
|
|
2156
|
-
},
|
|
2157
|
-
async agent_context_pressure(input) {
|
|
2158
|
-
const { session_id, ...body } = input;
|
|
2159
|
-
return request("POST", `/api/agent/sessions/${session_id}/context-pressure`, body);
|
|
2160
|
-
},
|
|
2161
|
-
async agent_resume(input) {
|
|
2162
|
-
const { session_id } = input;
|
|
2163
|
-
return request("GET", `/api/agent/sessions/${session_id}/resume`);
|
|
2164
|
-
},
|
|
2165
|
-
async agent_task_upsert(input) {
|
|
2166
|
-
const { session_id, ...body } = input;
|
|
2167
|
-
return request("POST", `/api/agent/sessions/${session_id}/tasks`, body);
|
|
2168
|
-
},
|
|
2169
|
-
async agent_task_tick(input) {
|
|
2170
|
-
const { task_id, ...body } = input;
|
|
2171
|
-
return request("PATCH", `/api/agent/tasks/${task_id}`, body);
|
|
2172
|
-
},
|
|
2173
|
-
async agent_lesson_add(input) {
|
|
2174
|
-
const { session_id, ...body } = input;
|
|
2175
|
-
return request("POST", `/api/agent/sessions/${session_id}/lessons`, body);
|
|
2176
|
-
},
|
|
2177
|
-
async agent_handoff(input) {
|
|
2178
|
-
const { session_id, ...body } = input;
|
|
2179
|
-
return request("POST", `/api/agent/sessions/${session_id}/handoff`, body);
|
|
2180
|
-
},
|
|
2181
|
-
async agent_replay(input) {
|
|
2182
|
-
const { session_id, ...qs } = input;
|
|
2183
|
-
return request("GET", `/api/agent/sessions/${session_id}/trace-events${toQueryString(qs)}`);
|
|
2184
|
-
},
|
|
2185
|
-
async agent_skill_suggest(input) {
|
|
2186
|
-
return request("GET", `/api/agent/skills/suggest${toQueryString(input)}`);
|
|
2187
|
-
},
|
|
2188
|
-
async agent_identity_attest(input) {
|
|
2189
|
-
return request("POST", "/api/agent/identity/attest", input);
|
|
2190
|
-
},
|
|
2191
|
-
async agent_identity_verify(input) {
|
|
2192
|
-
const { agent } = input;
|
|
2193
|
-
return request("GET", `/api/agent/identity/verify${toQueryString({ agent })}`);
|
|
2194
|
-
},
|
|
2195
|
-
async agent_intention_set(input) {
|
|
2196
|
-
return request("POST", "/api/agent/intentions", input);
|
|
2197
|
-
},
|
|
2198
|
-
async agent_intention_list(input) {
|
|
2199
|
-
const { agent, status } = input;
|
|
2200
|
-
return request("GET", `/api/agent/intentions${toQueryString({ agent, status })}`);
|
|
2201
|
-
},
|
|
2202
|
-
async agent_intention_cancel(input) {
|
|
2203
|
-
const { intention_id } = input;
|
|
2204
|
-
return request("POST", `/api/agent/intentions/${intention_id}/cancel`, {});
|
|
2205
|
-
},
|
|
2206
|
-
// --- Session working scope ---
|
|
2207
|
-
async ctx_use(input) {
|
|
2208
|
-
return request("POST", "/api/session/scope", input);
|
|
2209
|
-
},
|
|
2210
|
-
async ctx_scope() {
|
|
2211
|
-
return request("GET", "/api/session/scope");
|
|
2212
|
-
},
|
|
2213
|
-
// --- Goal graph (team-mode Phase 2) ---
|
|
2214
|
-
async goal_set_objective(input) {
|
|
2215
|
-
return request("POST", "/api/goal/objective", input);
|
|
2216
|
-
},
|
|
2217
|
-
async goal_add(input) {
|
|
2218
|
-
return request("POST", "/api/goal/nodes", input);
|
|
2219
|
-
},
|
|
2220
|
-
async goal_link_dep(input) {
|
|
2221
|
-
return request("POST", "/api/goal/deps", input);
|
|
2222
|
-
},
|
|
2223
|
-
async goal_advance(input) {
|
|
2224
|
-
const { node_id, ...body } = input;
|
|
2225
|
-
return request("PATCH", `/api/goal/nodes/${node_id}/advance`, body);
|
|
2226
|
-
},
|
|
2227
|
-
async goal_import(input) {
|
|
2228
|
-
return request("POST", "/api/goal/import", input);
|
|
2229
|
-
},
|
|
2230
|
-
async goal_decompose(input) {
|
|
2231
|
-
return request("POST", "/api/goal/decompose", input);
|
|
2232
|
-
},
|
|
2233
|
-
async goal_list(input) {
|
|
2234
|
-
return request("GET", `/api/goal/nodes${toQueryString(input)}`);
|
|
2235
|
-
},
|
|
2236
|
-
async goal_get(input) {
|
|
2237
|
-
const { node_id } = input;
|
|
2238
|
-
return request("GET", `/api/goal/nodes/${node_id}`);
|
|
2239
|
-
},
|
|
2240
|
-
async goal_frontier(input) {
|
|
2241
|
-
return request("GET", `/api/goal/frontier${toQueryString(input)}`);
|
|
2242
|
-
},
|
|
2243
|
-
// --- Directed relays (team-mode Phase 2) ---
|
|
2244
|
-
async relay_open(input) {
|
|
2245
|
-
return request("POST", "/api/relay", input);
|
|
2246
|
-
},
|
|
2247
|
-
async relay_inbox(input) {
|
|
2248
|
-
return request("GET", `/api/relay/inbox${toQueryString(input)}`);
|
|
2249
|
-
},
|
|
2250
|
-
async relay_get(input) {
|
|
2251
|
-
const { id } = input;
|
|
2252
|
-
return request("GET", `/api/relay/${id}`);
|
|
2253
|
-
},
|
|
2254
|
-
async relay_claim(input) {
|
|
2255
|
-
const { id, ...body } = input;
|
|
2256
|
-
return request("POST", `/api/relay/${id}/claim`, body);
|
|
2257
|
-
},
|
|
2258
|
-
async relay_advance(input) {
|
|
2259
|
-
const { id, ...body } = input;
|
|
2260
|
-
return request("PATCH", `/api/relay/${id}/advance`, body);
|
|
2261
|
-
},
|
|
2262
|
-
async relay_nudge(input) {
|
|
2263
|
-
const { id, ...body } = input;
|
|
2264
|
-
return request("POST", `/api/relay/${id}/nudge`, body);
|
|
2265
|
-
},
|
|
2266
|
-
// --- World-model snapshots + forkable sandboxes (T378) ---
|
|
2267
|
-
async ctx_snapshot_create(input) {
|
|
2268
|
-
return request("POST", "/api/world/snapshots", input);
|
|
2269
|
-
},
|
|
2270
|
-
async ctx_fork_world(input) {
|
|
2271
|
-
return request("POST", "/api/world/fork", input);
|
|
2272
|
-
},
|
|
2273
|
-
async ctx_diff_world(input) {
|
|
2274
|
-
const { sandbox_workspace } = input;
|
|
2275
|
-
return request("GET", `/api/world/sandboxes/${encodeURIComponent(sandbox_workspace)}/diff`);
|
|
2276
|
-
},
|
|
2277
|
-
async ctx_discard_sandbox(input) {
|
|
2278
|
-
const { sandbox_workspace } = input;
|
|
2279
|
-
return request("DELETE", `/api/world/sandboxes/${encodeURIComponent(sandbox_workspace)}`);
|
|
2280
|
-
},
|
|
2281
|
-
// --- T668: tool-profile discovery -- see TOOL_GROUPS/loadToolGroup above.
|
|
2282
|
-
// Local-only: neither handler makes an HTTP call to the API. ---
|
|
2283
|
-
async ctx_tool_groups() {
|
|
2284
|
-
const groups = Object.entries(TOOL_GROUPS).map(([key, group]) => ({
|
|
2285
|
-
group: key,
|
|
2286
|
-
description: group.description,
|
|
2287
|
-
tool_count: group.tools.length,
|
|
2288
|
-
loaded: group.tools.every((name) => activeToolNames.has(name)),
|
|
2289
|
-
tools: group.tools,
|
|
2290
|
-
}));
|
|
2291
|
-
return {
|
|
2292
|
-
loaded_tool_count: activeToolNames.size,
|
|
2293
|
-
total_tool_count: tools.length,
|
|
2294
|
-
groups,
|
|
2295
|
-
how_to_load: 'Call ctx_load_tool_group with { group: "<name>" } (or "all") to add a group\'s tools to this session. If your tool list does not refresh automatically, call the newly loaded tool directly by name anyway -- ContextQ accepts a call for any known tool name regardless of what tools/list currently returns -- or reconnect with the environment variable CONTEXT_MCP_TOOL_PROFILE=full to start with every tool loaded.',
|
|
2296
|
-
};
|
|
2297
|
-
},
|
|
2298
|
-
async ctx_load_tool_group(input) {
|
|
2299
|
-
const { group } = input;
|
|
2300
|
-
if (typeof group !== "string" || group.trim() === "") {
|
|
2301
|
-
return { error: 'group is required -- call ctx_tool_groups to see valid group names, or pass "all".' };
|
|
2302
|
-
}
|
|
2303
|
-
const { added, unknownGroup } = loadToolGroup(group);
|
|
2304
|
-
if (unknownGroup) {
|
|
2305
|
-
return { error: `Unknown group "${group}". Call ctx_tool_groups to see valid group names.` };
|
|
2306
|
-
}
|
|
2307
|
-
return {
|
|
2308
|
-
group: group.trim().toLowerCase(),
|
|
2309
|
-
added_tools: added,
|
|
2310
|
-
already_loaded: added.length === 0,
|
|
2311
|
-
note: "These tools are now callable. If they do not appear in your tool list yet, call them directly by name -- ContextQ accepts a call for any known tool name regardless of what tools/list currently shows.",
|
|
2312
|
-
};
|
|
2313
|
-
},
|
|
2314
|
-
};
|
|
2315
|
-
// ---------------------------------------------------------------------------
|
|
2316
|
-
// T375: episodic tool-call trace — fire-and-forget logging of EVERY tool
|
|
2317
|
-
// invocation via POST /api/agent/sessions/:id/trace-events.
|
|
2318
|
-
//
|
|
2319
|
-
// The MCP process is long-lived (one process per client session), so the
|
|
2320
|
-
// "current" agent session id is tracked in module state: adopted whenever a
|
|
2321
|
-
// tool call's args carry `session_id` (most agent_*/goal_*/relay_* tools
|
|
2322
|
-
// already require or accept one) or from the id returned by
|
|
2323
|
-
// `agent_session_start`. Tool calls made before any session is known are
|
|
2324
|
-
// simply not traced — there is nothing to attribute them to yet, and this
|
|
2325
|
-
// never affects the tool's own result either way.
|
|
2326
|
-
//
|
|
2327
|
-
// `traceToolCall` is invoked WITHOUT `await` from `handleToolCall` below and
|
|
2328
|
-
// swallows every error internally — a slow, failing, or misconfigured trace
|
|
2329
|
-
// endpoint can NEVER block or fail the tool path it is describing.
|
|
2330
|
-
// ---------------------------------------------------------------------------
|
|
2331
|
-
let currentSessionId = null;
|
|
2332
|
-
// ---------------------------------------------------------------------------
|
|
2333
|
-
// T446: first-call brain-state injection -- see
|
|
2334
|
-
// docs/tech/brain-state-injection.md "Injection mechanics / stdio transport".
|
|
2335
|
-
// One MCP stdio process = one guard: `brainStateInjected` is the ENTIRE
|
|
2336
|
-
// dedupe store for this transport, no map/session-id bookkeeping needed.
|
|
2337
|
-
// Fetch failure (endpoint down, no key, network error) must never break the
|
|
2338
|
-
// tool call it rides on -- `buildBrainStateBlock` swallows every error and
|
|
2339
|
-
// returns null, which the caller treats as "nothing to inject".
|
|
2340
|
-
// ---------------------------------------------------------------------------
|
|
2341
|
-
let brainStateInjected = false; // module-scope: one MCP stdio process = one guard
|
|
2342
|
-
function brainStateInjectionEnabled() {
|
|
2343
|
-
return process.env.CONTEXTQ_BRAIN_STATE_INJECT !== "false";
|
|
2344
|
-
}
|
|
2345
|
-
// ---------------------------------------------------------------------------
|
|
2346
|
-
// T447: auto-checkpoint after a successful write tool -- see
|
|
2347
|
-
// docs/tech/brain-state-injection.md ("no end-of-chat signal on
|
|
2348
|
-
// Desktop/Cowork" is the same gap this closes incrementally instead of
|
|
2349
|
-
// relying on a session-end hook). Reuses the same endpoint `agent_checkpoint`
|
|
2350
|
-
// already calls (POST /api/agent/sessions/:id/checkpoint) so there is no new
|
|
2351
|
-
// server-side surface.
|
|
2352
|
-
//
|
|
2353
|
-
// Fire-and-forget, coalesced to at most one auto-checkpoint per minute
|
|
2354
|
-
// (module-scope timestamp guard), and every failure mode (no session, no
|
|
2355
|
-
// key, network error) is swallowed -- an auto-checkpoint must never affect
|
|
2356
|
-
// the tool result it rides on, mirroring `traceToolCall`'s contract above.
|
|
2357
|
-
// ---------------------------------------------------------------------------
|
|
2358
|
-
const AUTO_CHECKPOINT_COALESCE_MS = 60_000;
|
|
2359
|
-
let lastAutoCheckpointAt = 0;
|
|
2360
|
-
// Write tools = anything that mutates server-side state. Read/query tools
|
|
2361
|
-
// (ctx_search, ctx_list, agent_boot, agent_resume, goal_list, ...) and the
|
|
2362
|
-
// purely advisory agent_context_pressure are deliberately excluded.
|
|
2363
|
-
// agent_checkpoint itself is excluded too -- it IS a checkpoint, chaining an
|
|
2364
|
-
// auto-checkpoint off of it would just be a redundant duplicate call.
|
|
2365
|
-
const WRITE_TOOLS = new Set([
|
|
2366
|
-
"ctx_save",
|
|
2367
|
-
"ctx_update",
|
|
2368
|
-
"ctx_delete",
|
|
2369
|
-
"ctx_bulk_update",
|
|
2370
|
-
"ctx_undo_archive",
|
|
2371
|
-
"ctx_import",
|
|
2372
|
-
"ctx_dream",
|
|
2373
|
-
"ctx_link",
|
|
2374
|
-
"ctx_evolve",
|
|
2375
|
-
"ctx_regenerate_mocs",
|
|
2376
|
-
"ctx_saved_searches_create",
|
|
2377
|
-
"ctx_saved_searches_update",
|
|
2378
|
-
"ctx_saved_searches_delete",
|
|
2379
|
-
"ctx_saved_searches_run",
|
|
2380
|
-
"ctx_run_saved_search",
|
|
2381
|
-
"ctx_ingest",
|
|
2382
|
-
"ctx_remember",
|
|
2383
|
-
"ctx_memory_review_run",
|
|
2384
|
-
"ctx_audit_cleanup_run",
|
|
2385
|
-
"ctx_admin_rate_limit_set",
|
|
2386
|
-
"ctx_use",
|
|
2387
|
-
"agent_session_start",
|
|
2388
|
-
"agent_session_end",
|
|
2389
|
-
"agent_task_upsert",
|
|
2390
|
-
"agent_task_tick",
|
|
2391
|
-
"agent_lesson_add",
|
|
2392
|
-
"agent_handoff",
|
|
2393
|
-
"agent_identity_attest",
|
|
2394
|
-
"agent_intention_set",
|
|
2395
|
-
"agent_intention_cancel",
|
|
2396
|
-
"goal_set_objective",
|
|
2397
|
-
"goal_add",
|
|
2398
|
-
"goal_link_dep",
|
|
2399
|
-
"goal_advance",
|
|
2400
|
-
"goal_import",
|
|
2401
|
-
"goal_decompose",
|
|
2402
|
-
"relay_open",
|
|
2403
|
-
"relay_claim",
|
|
2404
|
-
"relay_advance",
|
|
2405
|
-
"relay_nudge",
|
|
2406
|
-
"ctx_snapshot_create",
|
|
2407
|
-
"ctx_fork_world",
|
|
2408
|
-
"ctx_discard_sandbox",
|
|
2409
|
-
]);
|
|
2410
|
-
function autoCheckpointEnabled() {
|
|
2411
|
-
return process.env.CONTEXTQ_AUTO_CHECKPOINT !== "false";
|
|
2412
|
-
}
|
|
2413
|
-
// Not awaited by the caller -- kicked off and immediately forgotten so it
|
|
2414
|
-
// never adds latency to the write tool's own response.
|
|
2415
|
-
function autoCheckpoint(sessionId, toolName) {
|
|
2416
|
-
const now = Date.now();
|
|
2417
|
-
if (now - lastAutoCheckpointAt < AUTO_CHECKPOINT_COALESCE_MS)
|
|
2418
|
-
return;
|
|
2419
|
-
lastAutoCheckpointAt = now;
|
|
2420
|
-
request("POST", `/api/agent/sessions/${sessionId}/checkpoint`, {
|
|
2421
|
-
summary: `auto-checkpoint after ${toolName}`,
|
|
2422
|
-
}).catch(() => {
|
|
2423
|
-
// Network error, missing session, missing API key, etc. -- never
|
|
2424
|
-
// surface to the tool caller. Roll back the timestamp guard so a
|
|
2425
|
-
// transient failure doesn't silently disable auto-checkpoint for a
|
|
2426
|
-
// full minute.
|
|
2427
|
-
lastAutoCheckpointAt = 0;
|
|
2428
|
-
});
|
|
2429
|
-
}
|
|
2430
|
-
async function buildBrainStateBlock() {
|
|
2431
|
-
try {
|
|
2432
|
-
const result = await request("GET", "/api/agent/brain-state");
|
|
2433
|
-
if (!result || typeof result !== "object")
|
|
2434
|
-
return null;
|
|
2435
|
-
const block = result.block;
|
|
2436
|
-
return typeof block === "string" ? block : null;
|
|
2437
|
-
}
|
|
2438
|
-
catch {
|
|
2439
|
-
// Endpoint unreachable, no API key configured, malformed response, etc.
|
|
2440
|
-
// Injection is a nice-to-have -- never let it surface as a tool error.
|
|
2441
|
-
return null;
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
function extractSessionId(args) {
|
|
2445
|
-
const v = args?.session_id;
|
|
2446
|
-
return typeof v === "number" ? v : null;
|
|
2447
|
-
}
|
|
2448
|
-
function extractSessionIdFromResult(result) {
|
|
2449
|
-
if (result && typeof result === "object" && "id" in result) {
|
|
2450
|
-
const id = result.id;
|
|
2451
|
-
if (typeof id === "number")
|
|
2452
|
-
return id;
|
|
2453
|
-
}
|
|
2454
|
-
return null;
|
|
2455
|
-
}
|
|
2456
|
-
function traceToolCall(sessionId, toolName, args, outcome, isError, durationMs) {
|
|
2457
|
-
// Deliberately not awaited by the caller — see the module doc-comment
|
|
2458
|
-
// above. `.catch` here is the last line of defense; `request()` already
|
|
2459
|
-
// rejects cleanly on non-2xx/network errors, never throws synchronously.
|
|
2460
|
-
request("POST", `/api/agent/sessions/${sessionId}/trace-events`, {
|
|
2461
|
-
tool_name: toolName,
|
|
2462
|
-
args,
|
|
2463
|
-
result: isError ? { error: String(outcome) } : outcome,
|
|
2464
|
-
duration_ms: durationMs,
|
|
2465
|
-
}).catch(() => {
|
|
2466
|
-
// Trace writes must never surface to the tool caller — swallow silently.
|
|
2467
|
-
});
|
|
2468
|
-
}
|
|
2469
|
-
export async function handleToolCall(name, args,
|
|
2470
|
-
// T558: the incoming `tools/call` request's `_meta` envelope, passed
|
|
2471
|
-
// through by index.ts's request handler. Optional + untyped here on
|
|
2472
|
-
// purpose -- `extractTraceContext` already validates + narrows it.
|
|
2473
|
-
meta) {
|
|
2474
|
-
const handler = handlers[name];
|
|
2475
|
-
if (!handler) {
|
|
2476
|
-
return {
|
|
2477
|
-
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
2478
|
-
isError: true,
|
|
2479
|
-
};
|
|
2480
|
-
}
|
|
2481
|
-
// Adopt an explicit session_id from the incoming args BEFORE the call, so
|
|
2482
|
-
// this trace attributes to the session THIS call belongs to (matters when
|
|
2483
|
-
// a client interleaves calls across multiple sessions).
|
|
2484
|
-
const argSessionId = extractSessionId(args);
|
|
2485
|
-
if (argSessionId !== null)
|
|
2486
|
-
currentSessionId = argSessionId;
|
|
2487
|
-
// T558: scope the trace context to EXACTLY this tool invocation's
|
|
2488
|
-
// `handler(args)` call below -- not the trace-event/auto-checkpoint side
|
|
2489
|
-
// calls further down, which are separate requests, not "this call's
|
|
2490
|
-
// underlying /api/* request".
|
|
2491
|
-
const traceContext = extractTraceContext(meta);
|
|
2492
|
-
const startedAt = Date.now();
|
|
2493
|
-
try {
|
|
2494
|
-
const result = await traceContextStorage.run(traceContext, () => handler(args));
|
|
2495
|
-
const durationMs = Date.now() - startedAt;
|
|
2496
|
-
if (name === "agent_session_start") {
|
|
2497
|
-
const newId = extractSessionIdFromResult(result);
|
|
2498
|
-
if (newId !== null)
|
|
2499
|
-
currentSessionId = newId;
|
|
2500
|
-
}
|
|
2501
|
-
if (currentSessionId !== null) {
|
|
2502
|
-
traceToolCall(currentSessionId, name, args, result, false, durationMs);
|
|
2503
|
-
if (WRITE_TOOLS.has(name) && autoCheckpointEnabled()) {
|
|
2504
|
-
autoCheckpoint(currentSessionId, name);
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
const response = {
|
|
2508
|
-
content: [
|
|
2509
|
-
{
|
|
2510
|
-
type: "text",
|
|
2511
|
-
text: result !== null ? JSON.stringify(result, null, 2) : "OK",
|
|
2512
|
-
},
|
|
2513
|
-
],
|
|
2514
|
-
};
|
|
2515
|
-
// First successful tool call this process: try to append the brain-state
|
|
2516
|
-
// block. Set the guard unconditionally BEFORE the eligibility checks so
|
|
2517
|
-
// an `agent_boot` call (which already returns rich context) or a
|
|
2518
|
-
// disabled/failed fetch still consumes the one-shot -- never retried
|
|
2519
|
-
// later in the same process.
|
|
2520
|
-
if (!brainStateInjected) {
|
|
2521
|
-
brainStateInjected = true;
|
|
2522
|
-
if (name !== "agent_boot" && brainStateInjectionEnabled()) {
|
|
2523
|
-
const block = await buildBrainStateBlock();
|
|
2524
|
-
if (block) {
|
|
2525
|
-
response.content.push({ type: "text", text: block });
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
return response;
|
|
2530
|
-
}
|
|
2531
|
-
catch (err) {
|
|
2532
|
-
const durationMs = Date.now() - startedAt;
|
|
2533
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2534
|
-
if (currentSessionId !== null) {
|
|
2535
|
-
traceToolCall(currentSessionId, name, args, message, true, durationMs);
|
|
2536
|
-
}
|
|
2537
|
-
return {
|
|
2538
|
-
content: [{ type: "text", text: `Error: ${message}` }],
|
|
2539
|
-
isError: true,
|
|
2540
|
-
};
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
//# sourceMappingURL=tools.js.map
|