toolroll 0.9.34 → 0.9.37
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/CHANGELOG.md +102 -0
- package/README.md +7 -0
- package/dist/assignment-adapters.d.ts +12 -48
- package/dist/assignment-adapters.js +33 -43
- package/dist/assignment.d.ts +1 -0
- package/dist/assignment.js +2 -2
- package/dist/browser-workspace.d.ts +3 -3
- package/dist/build-review.d.ts +1 -1
- package/dist/build-review.js +2 -1
- package/dist/builder.js +4 -7
- package/dist/chat-acceptance.d.ts +2 -2
- package/dist/chat-actions.d.ts +18 -12
- package/dist/chat-actions.js +15 -65
- package/dist/chat-ask.js +5 -5
- package/dist/chat-decide.d.ts +14 -1
- package/dist/chat-decide.js +12 -6
- package/dist/chat-delivery-state.d.ts +12 -70
- package/dist/chat-delivery-state.js +17 -135
- package/dist/chat-delivery.js +58 -30
- package/dist/chat-flow.js +8 -6
- package/dist/chat-inbox.js +1 -1
- package/dist/coding-context.d.ts +2 -39
- package/dist/coding-context.js +2 -1
- package/dist/coding-handoff.d.ts +6 -25
- package/dist/coding-handoff.js +11 -5
- package/dist/coding-types.d.ts +2 -26
- package/dist/coding-workspace.d.ts +2 -0
- package/dist/coding-workspace.js +50 -14
- package/dist/contracts/acceptance-terms.d.ts +14 -0
- package/dist/contracts/acceptance-terms.js +16 -0
- package/dist/contracts/chat-actions.d.ts +328 -0
- package/dist/contracts/chat-actions.js +133 -0
- package/dist/contracts/chat-callback-rows.d.ts +59 -0
- package/dist/contracts/chat-callback-rows.js +46 -0
- package/dist/contracts/chat-content.d.ts +276 -0
- package/dist/contracts/chat-content.js +127 -0
- package/dist/contracts/chat-tables.d.ts +6 -0
- package/dist/contracts/chat-tables.js +136 -0
- package/dist/contracts/coding-context.d.ts +97 -0
- package/dist/contracts/coding-context.js +60 -0
- package/dist/contracts/coding-handoff.d.ts +44 -0
- package/dist/contracts/coding-handoff.js +47 -0
- package/dist/contracts/coding-workspace.d.ts +165 -0
- package/dist/contracts/coding-workspace.js +71 -0
- package/dist/contracts/contract.d.ts +40 -7
- package/dist/contracts/contract.js +155 -19
- package/dist/contracts/decision.d.ts +45 -0
- package/dist/contracts/decision.js +45 -0
- package/dist/contracts/discord-callback.d.ts +45 -0
- package/dist/contracts/discord-callback.js +34 -0
- package/dist/contracts/flow-card.d.ts +50 -0
- package/dist/contracts/flow-card.js +46 -0
- package/dist/contracts/flow-propose.d.ts +325 -0
- package/dist/contracts/flow-propose.js +41 -0
- package/dist/contracts/flow-send.d.ts +119 -0
- package/dist/contracts/flow-send.js +112 -0
- package/dist/contracts/flow.d.ts +19405 -0
- package/dist/contracts/flow.js +364 -0
- package/dist/contracts/gateway-tools.d.ts +374 -0
- package/dist/contracts/gateway-tools.js +94 -0
- package/dist/contracts/handoff.d.ts +91 -0
- package/dist/contracts/handoff.js +82 -0
- package/dist/contracts/lead-context.d.ts +165 -0
- package/dist/contracts/lead-context.js +73 -0
- package/dist/contracts/lead-tools.d.ts +1905 -0
- package/dist/contracts/lead-tools.js +265 -0
- package/dist/contracts/memory-pass.d.ts +158 -0
- package/dist/contracts/memory-pass.js +102 -0
- package/dist/contracts/project-knowledge.d.ts +126 -0
- package/dist/contracts/project-knowledge.js +93 -0
- package/dist/contracts/project-memory.d.ts +117 -0
- package/dist/contracts/project-memory.js +51 -0
- package/dist/contracts/project-skills.d.ts +121 -0
- package/dist/contracts/project-skills.js +76 -0
- package/dist/contracts/proof.d.ts +136 -0
- package/dist/contracts/proof.js +110 -0
- package/dist/contracts/review-findings.d.ts +46 -0
- package/dist/contracts/review-findings.js +48 -0
- package/dist/contracts/route.d.ts +484 -0
- package/dist/contracts/route.js +128 -0
- package/dist/contracts/scope.d.ts +105 -0
- package/dist/contracts/scope.js +114 -0
- package/dist/contracts/scout-report.d.ts +234 -0
- package/dist/contracts/scout-report.js +141 -0
- package/dist/contracts/slack-callback.d.ts +43 -0
- package/dist/contracts/slack-callback.js +35 -0
- package/dist/contracts/sort-answer.d.ts +83 -0
- package/dist/contracts/sort-answer.js +77 -0
- package/dist/contracts/stage-output.d.ts +127 -0
- package/dist/contracts/stage-output.js +166 -0
- package/dist/contracts/task-sizing.d.ts +24 -0
- package/dist/contracts/task-sizing.js +28 -0
- package/dist/contracts/teammate-turn.d.ts +43 -0
- package/dist/contracts/teammate-turn.js +51 -0
- package/dist/contracts/teams-callback.d.ts +14 -0
- package/dist/contracts/teams-callback.js +14 -0
- package/dist/contracts/telegram-callback.d.ts +120 -0
- package/dist/contracts/telegram-callback.js +97 -0
- package/dist/contracts/verification-receipt.d.ts +148 -0
- package/dist/contracts/verification-receipt.js +87 -0
- package/dist/decision.d.ts +11 -20
- package/dist/decision.js +86 -167
- package/dist/demo.js +28 -11
- package/dist/discord-chat.js +31 -20
- package/dist/evidence.d.ts +3 -40
- package/dist/evidence.js +5 -1
- package/dist/flow-engine.js +14 -17
- package/dist/flow-gallery.d.ts +3 -2
- package/dist/flow-items.d.ts +4 -13
- package/dist/flow-items.js +1 -15
- package/dist/flow-send.d.ts +6 -37
- package/dist/flow-send.js +18 -14
- package/dist/flow-share.d.ts +10 -32
- package/dist/flow-share.js +72 -84
- package/dist/flow-sort.d.ts +7 -26
- package/dist/flow-sort.js +25 -20
- package/dist/flow-starters.d.ts +2 -1
- package/dist/flow-steps.js +3 -2
- package/dist/flow-triggers.d.ts +15 -72
- package/dist/flow-triggers.js +94 -83
- package/dist/flows.d.ts +37 -220
- package/dist/flows.js +356 -387
- package/dist/invoke.js +4 -4
- package/dist/lead-context.d.ts +4 -2
- package/dist/lead-context.js +24 -17
- package/dist/mate-tools.d.ts +4 -0
- package/dist/mate-tools.js +111 -308
- package/dist/mcp-connect.d.ts +27 -4
- package/dist/mcp-connect.js +92 -17
- package/dist/mcp.js +84 -216
- package/dist/memory-pass.d.ts +17 -42
- package/dist/memory-pass.js +54 -26
- package/dist/operate.js +31 -2
- package/dist/phase-routing.d.ts +19 -72
- package/dist/phase-routing.js +34 -164
- package/dist/plan.d.ts +5 -5
- package/dist/plan.js +5 -25
- package/dist/process-recovery-eligibility.d.ts +2 -2
- package/dist/process-recovery-eligibility.js +13 -8
- package/dist/project-knowledge.d.ts +16 -35
- package/dist/project-knowledge.js +42 -21
- package/dist/project-memory.d.ts +2 -28
- package/dist/project-memory.js +28 -13
- package/dist/project-skills.d.ts +16 -38
- package/dist/project-skills.js +41 -32
- package/dist/project-tools.d.ts +1 -0
- package/dist/project-tools.js +4 -4
- package/dist/proof.d.ts +15 -91
- package/dist/proof.js +90 -249
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +4 -16
- package/dist/result-evidence-readers.d.ts +42 -0
- package/dist/result-evidence-readers.js +91 -0
- package/dist/result-shots.d.ts +1 -1
- package/dist/result-shots.js +2 -1
- package/dist/review-switch.d.ts +3 -8
- package/dist/reviewer.d.ts +8 -12
- package/dist/reviewer.js +11 -35
- package/dist/scope.d.ts +21 -43
- package/dist/scope.js +42 -128
- package/dist/scout-report.d.ts +9 -194
- package/dist/scout-report.js +113 -264
- package/dist/scout.js +15 -11
- package/dist/serve.js +17 -101
- package/dist/slack-chat.js +26 -20
- package/dist/slack-state.js +2 -1
- package/dist/store.d.ts +9 -32
- package/dist/store.js +18 -15
- package/dist/structured-output.js +2 -1
- package/dist/subscription-chat.d.ts +4 -0
- package/dist/subscription-chat.js +4 -1
- package/dist/surface.js +8 -1
- package/dist/task-review-brief.d.ts +69 -0
- package/dist/task-review-brief.js +179 -0
- package/dist/task-sizing.d.ts +3 -27
- package/dist/task-sizing.js +11 -26
- package/dist/task-text.d.ts +0 -5
- package/dist/task-text.js +1 -3
- package/dist/teammate-question.js +7 -5
- package/dist/teammate-work.js +9 -5
- package/dist/teammates.d.ts +13 -64
- package/dist/teammates.js +32 -33
- package/dist/teams-chat.js +20 -11
- package/dist/telegram-flow.js +4 -3
- package/dist/telegram-mate.d.ts +4 -14
- package/dist/telegram-mate.js +8 -7
- package/dist/telegram-team.d.ts +2 -19
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +52 -42
- package/dist/text-limits.d.ts +212 -1
- package/dist/text-limits.js +214 -1
- package/dist/tools-ui.d.ts +2 -1
- package/dist/tools-ui.js +7 -4
- package/dist/verification-evidence.js +24 -12
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { TASK_SCOPE_TEXT_SCHEMA } from "./task-text.js";
|
|
2
1
|
import { dirname, join } from 'node:path';
|
|
3
2
|
import { statSync } from 'node:fs';
|
|
4
3
|
import { repositoryContextRead } from './repository-context.js';
|
|
5
4
|
import { ASSIGNMENT_TOOLS, assignmentForCoordinator } from "./assignment-adapters.js";
|
|
5
|
+
import { parseContract, toModelSchema } from "./contracts/contract.js";
|
|
6
|
+
import { GATEWAY_TOOL_INPUTS, GATEWAY_TOOL_OUTPUTS } from "./contracts/gateway-tools.js";
|
|
7
|
+
import { reportToolOutput } from "./contracts/lead-tools.js";
|
|
6
8
|
/**
|
|
7
9
|
* `toolroll mcp` — the MCP stdio server (MCP gateway spec v6).
|
|
8
10
|
*
|
|
@@ -71,167 +73,73 @@ const CONTRACT_GUIDE = [
|
|
|
71
73
|
"Approve, steer, answer, pick, mint, configure, merge: those verbs do not exist on this surface, by construction.",
|
|
72
74
|
"Claim one assignment, then follow list_assignment_updates with a saved cursor; inspect get_assignment when a current result or decision needs attention. claim_assignment records its lead; acknowledge_assignment records that lead's check of an exact ready receipt. Neither operation grants authority, accepts proof, answers decisions, approves work, publishes, or deploys.",
|
|
73
75
|
].join("\n");
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const types = rule["type"];
|
|
97
|
-
if (value === null) {
|
|
98
|
-
if (!types.includes("null"))
|
|
99
|
-
return "`" + key + "` must not be null";
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
const member = types.find(one => one !== "null");
|
|
103
|
-
const problem = invalidArgs({ type: "object", properties: { [key]: { ...rule, type: member ?? "string" } } }, { [key]: value });
|
|
104
|
-
if (problem !== null)
|
|
105
|
-
return problem;
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
if (rule["type"] === "array") {
|
|
109
|
-
if (!Array.isArray(value))
|
|
110
|
-
return "`" + key + "` must be an array";
|
|
111
|
-
const maxItems = rule["maxItems"];
|
|
112
|
-
if (typeof maxItems === "number" && value.length > maxItems)
|
|
113
|
-
return "`" + key + "` has more than " + String(maxItems) + " items";
|
|
114
|
-
const items = rule["items"];
|
|
115
|
-
if (items !== undefined) {
|
|
116
|
-
for (const one of value) {
|
|
117
|
-
const problem = invalidArgs({ type: "object", properties: { item: items } }, { item: one });
|
|
118
|
-
if (problem !== null)
|
|
119
|
-
return "`" + key + "`: " + problem.replace("`item`", "an item");
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (rule["type"] === "boolean" && typeof value !== "boolean")
|
|
125
|
-
return "`" + key + "` must be a boolean";
|
|
126
|
-
if (rule["type"] === "string") {
|
|
127
|
-
if (typeof value !== "string")
|
|
128
|
-
return "`" + key + "` must be a string";
|
|
129
|
-
const min = rule["minLength"];
|
|
130
|
-
const max = rule["maxLength"];
|
|
131
|
-
if (typeof min === "number" && value.length < min)
|
|
132
|
-
return "`" + key + "` is shorter than " + String(min);
|
|
133
|
-
if (typeof max === "number" && value.length > max)
|
|
134
|
-
return "`" + key + "` is longer than " + String(max);
|
|
135
|
-
const pattern = rule["pattern"];
|
|
136
|
-
if (typeof pattern === "string" && !new RegExp(pattern).test(value))
|
|
137
|
-
return "`" + key + "` does not match its required format";
|
|
138
|
-
const allowed = rule["enum"];
|
|
139
|
-
if (Array.isArray(allowed) && !allowed.includes(value))
|
|
140
|
-
return "`" + key + "` must be one of " + allowed.join(", ");
|
|
141
|
-
}
|
|
142
|
-
if (rule["type"] === "integer") {
|
|
143
|
-
if (typeof value !== "number" || !Number.isInteger(value))
|
|
144
|
-
return "`" + key + "` must be an integer";
|
|
145
|
-
const min = rule["minimum"];
|
|
146
|
-
const max = rule["maximum"];
|
|
147
|
-
if (typeof min === "number" && value < min)
|
|
148
|
-
return "`" + key + "` is below " + String(min);
|
|
149
|
-
if (typeof max === "number" && value > max)
|
|
150
|
-
return "`" + key + "` is above " + String(max);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
function str(args, name, max) {
|
|
156
|
-
const value = args[name];
|
|
157
|
-
if (typeof value !== "string" || value.length === 0 || value.length > max)
|
|
158
|
-
return null;
|
|
159
|
-
return value;
|
|
160
|
-
}
|
|
161
|
-
const TOOLS = [
|
|
162
|
-
{
|
|
163
|
-
name: 'get_project_context', description: 'Read bounded source excerpts or advisory static import impact in an admitted project. Falls back to text search without an index.',
|
|
164
|
-
inputSchema: { type: 'object', properties: { repo: { type: 'string', minLength: 1, maxLength: 4096 }, query: { type: 'string', minLength: 1, maxLength: 1000 }, mode: { type: 'string', enum: ['search', 'impact'] } }, required: ['repo', 'query'], additionalProperties: false },
|
|
76
|
+
/** An assignment tool: the adapter reads its call with the same schema `toolroll assignment` does. */
|
|
77
|
+
const assignmentHandler = (operation) => (ctx, args) => {
|
|
78
|
+
const result = assignmentForCoordinator(ctx.store, ctx.token, operation, args, ctx.now, ctx.evidenceRoot);
|
|
79
|
+
return result.ok ? { ok: true, body: result.body } : { ok: false, message: `${result.reason}: ${result.message}` };
|
|
80
|
+
};
|
|
81
|
+
const assignmentTool = (name) => {
|
|
82
|
+
const spec = ASSIGNMENT_TOOLS.find(one => one.name === name);
|
|
83
|
+
return { description: spec.description, handle: assignmentHandler(spec.operation) };
|
|
84
|
+
};
|
|
85
|
+
/** A propose_* tool: it writes one proposal row through the coordinator door. */
|
|
86
|
+
const proposeTool = (kind, description) => ({
|
|
87
|
+
description,
|
|
88
|
+
handle: (ctx, args) => {
|
|
89
|
+
const outcome = proposeAsCoordinator(ctx.store, ctx.token, kind, args, ctx.now, { readDecisions: ctx.readDecisions });
|
|
90
|
+
if (!outcome.ok)
|
|
91
|
+
return { ok: false, message: outcome.message };
|
|
92
|
+
return { ok: true, body: { proposal: outcome.id, kind: outcome.kind, awaiting: outcome.awaiting } };
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
const HANDLERS = {
|
|
96
|
+
get_project_context: {
|
|
97
|
+
description: 'Read bounded source excerpts or advisory static import impact in an admitted project. Falls back to text search without an index.',
|
|
165
98
|
handle: (ctx, args) => {
|
|
166
|
-
const repo =
|
|
99
|
+
const repo = args.repo;
|
|
167
100
|
if (!ctx.who.repos.includes(repo) || ctx.enrolled !== null && !ctx.enrolled.includes(repo))
|
|
168
101
|
return { ok: false, message: 'That project is outside your access.' };
|
|
169
|
-
return { ok: true, body: repositoryContextRead({ repo, query:
|
|
102
|
+
return { ok: true, body: repositoryContextRead({ repo, query: args.query, mode: args.mode === 'impact' ? 'impact' : 'search', audience: 'lead',
|
|
170
103
|
...(ctx.evidenceRoot === undefined ? {} : { cacheRoot: join(dirname(ctx.evidenceRoot), 'repository-context') }) }) };
|
|
171
104
|
},
|
|
172
105
|
},
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
{
|
|
181
|
-
name: "status",
|
|
106
|
+
get_assignment: assignmentTool("get_assignment"),
|
|
107
|
+
list_assignment_updates: assignmentTool("list_assignment_updates"),
|
|
108
|
+
claim_assignment: assignmentTool("claim_assignment"),
|
|
109
|
+
acknowledge_assignment: assignmentTool("acknowledge_assignment"),
|
|
110
|
+
get_assignment_brief: assignmentTool("get_assignment_brief"),
|
|
111
|
+
get_assignment_inbox: assignmentTool("get_assignment_inbox"),
|
|
112
|
+
acknowledge_assignment_delivery: assignmentTool("acknowledge_assignment_delivery"),
|
|
113
|
+
status: {
|
|
182
114
|
description: "The plane's liveness facts over your repo allowlist: what waits on the operator, what runs, what finished in the last 24h.",
|
|
183
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
184
115
|
handle: ctx => ({ ok: true, body: statusFor(ctx.store, ctx.who, ctx.now) }),
|
|
185
116
|
},
|
|
186
|
-
{
|
|
187
|
-
name: "list_tasks",
|
|
117
|
+
list_tasks: {
|
|
188
118
|
description: "Tasks in your repo allowlist. Filter by state and repo; cursor-paginated, stable order.",
|
|
189
|
-
inputSchema: {
|
|
190
|
-
type: "object",
|
|
191
|
-
properties: {
|
|
192
|
-
state: { type: "string", enum: ["queued", "running", "done", "failed", "cancelled"] },
|
|
193
|
-
repo: { type: "string", maxLength: 800 },
|
|
194
|
-
cursor: { type: "integer", minimum: 0 },
|
|
195
|
-
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
196
|
-
},
|
|
197
|
-
additionalProperties: false,
|
|
198
|
-
},
|
|
199
119
|
handle: (ctx, args) => {
|
|
200
120
|
const filter = {};
|
|
201
|
-
if (
|
|
202
|
-
filter.state = args
|
|
203
|
-
if (
|
|
204
|
-
filter.repo = args
|
|
205
|
-
if (
|
|
206
|
-
filter.cursor = args
|
|
207
|
-
if (
|
|
208
|
-
filter.limit = args
|
|
121
|
+
if (args.state !== undefined)
|
|
122
|
+
filter.state = args.state;
|
|
123
|
+
if (args.repo !== undefined)
|
|
124
|
+
filter.repo = args.repo;
|
|
125
|
+
if (args.cursor !== undefined)
|
|
126
|
+
filter.cursor = args.cursor;
|
|
127
|
+
if (args.limit !== undefined)
|
|
128
|
+
filter.limit = args.limit;
|
|
209
129
|
return { ok: true, body: listTasksFor(ctx.store, ctx.who, filter, ctx.now) };
|
|
210
130
|
},
|
|
211
131
|
},
|
|
212
|
-
{
|
|
213
|
-
name: "get_task",
|
|
132
|
+
get_task: {
|
|
214
133
|
description: "One task's deliverable (branch or report), scope standing, filer provenance, attempt ledger, and — for a finished scout — the report's title, summary, and follow-ups. A ref outside your allowlist answers not-found.",
|
|
215
|
-
inputSchema: {
|
|
216
|
-
type: "object",
|
|
217
|
-
properties: { ref: { type: "string", minLength: 1, maxLength: 64 } },
|
|
218
|
-
required: ["ref"],
|
|
219
|
-
additionalProperties: false,
|
|
220
|
-
},
|
|
221
134
|
handle: (ctx, args) => {
|
|
222
|
-
const
|
|
223
|
-
if (ref === null)
|
|
224
|
-
return { ok: false, message: "ref is a task id, 1-64 characters" };
|
|
225
|
-
const detail = taskDetailFor(ctx.store, ctx.who, ref, ctx.evidenceRoot, ctx.now);
|
|
135
|
+
const detail = taskDetailFor(ctx.store, ctx.who, args.ref, ctx.evidenceRoot, ctx.now);
|
|
226
136
|
if (detail === null)
|
|
227
|
-
return { ok: false, message: `not-found: no task \`${ref}\` in your repositories` };
|
|
137
|
+
return { ok: false, message: `not-found: no task \`${args.ref}\` in your repositories` };
|
|
228
138
|
return { ok: true, body: detail };
|
|
229
139
|
},
|
|
230
140
|
},
|
|
231
|
-
{
|
|
232
|
-
name: "list_repos",
|
|
141
|
+
list_repos: {
|
|
233
142
|
description: "The repositories your credential may see and file into, with their operating-mode standing.",
|
|
234
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
235
143
|
handle: ctx => {
|
|
236
144
|
// FAIL CLOSED when the project registry cannot be read (round-2
|
|
237
145
|
// finding 4): answering the full allowlist would claim enrollment
|
|
@@ -252,31 +160,25 @@ const TOOLS = [
|
|
|
252
160
|
};
|
|
253
161
|
},
|
|
254
162
|
},
|
|
255
|
-
{
|
|
256
|
-
name: "recap",
|
|
163
|
+
recap: {
|
|
257
164
|
description: "How things stand per repository in your allowlist, counts and ids: what waits on the operator (decisions, incidents, scopes awaiting approval), what runs, what is queued, finished, failed. Pass `since` (an ISO timestamp) to count only decisions, incidents, and attempts newer than it, to the hour; queued work and scopes awaiting approval always count.",
|
|
258
|
-
inputSchema: { type: "object", properties: { since: { type: "string", maxLength: 30 } }, additionalProperties: false },
|
|
259
165
|
handle: (ctx, args) => {
|
|
260
|
-
const since = args
|
|
261
|
-
if (since !== undefined && (
|
|
166
|
+
const since = args.since;
|
|
167
|
+
if (since !== undefined && (!ISO_STAMP_RULE.test(since) || Number.isNaN(Date.parse(since)))) {
|
|
262
168
|
return { ok: false, message: "since is an ISO timestamp like 2026-09-02T12:00:00Z" };
|
|
263
169
|
}
|
|
264
|
-
return { ok: true, body: labelRepos(recapOver(ctx.store, ctx.who.repos, ctx.now,
|
|
170
|
+
return { ok: true, body: labelRepos(recapOver(ctx.store, ctx.who.repos, ctx.now, since ?? null), index => ctx.who.repos[index] ?? "") };
|
|
265
171
|
},
|
|
266
172
|
},
|
|
267
|
-
{
|
|
268
|
-
name: "list_decisions",
|
|
173
|
+
list_decisions: {
|
|
269
174
|
description: "Open decisions in your allowlist: id, task, question, options (id, label, reversible), age in hours. Never consequences or recommendations; the operator answers them.",
|
|
270
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
271
175
|
handle: ctx => ({ ok: true, body: labelRepos(decisionsOver(ctx.store, ctx.who.repos, ctx.now), index => ctx.who.repos[index] ?? "") }),
|
|
272
176
|
},
|
|
273
|
-
{
|
|
274
|
-
name: "queue",
|
|
177
|
+
queue: {
|
|
275
178
|
description: "One repository's queue by column — the shared column, then each worker's reserved column — each in dispatch order.",
|
|
276
|
-
inputSchema: { type: "object", properties: { repo: { type: "string", minLength: 1, maxLength: 800 } }, required: ["repo"], additionalProperties: false },
|
|
277
179
|
handle: (ctx, args) => {
|
|
278
|
-
const repo =
|
|
279
|
-
if (
|
|
180
|
+
const repo = args.repo;
|
|
181
|
+
if (!ctx.who.repos.includes(repo))
|
|
280
182
|
return { ok: false, message: "not-found: that repository is not in your allowlist" };
|
|
281
183
|
// The installation-wide revision stays home (slice-2 review, finding
|
|
282
184
|
// 12): a coordinator cannot move queues, and the counter would tell it
|
|
@@ -285,52 +187,33 @@ const TOOLS = [
|
|
|
285
187
|
return { ok: true, body: { repo, ...columns } };
|
|
286
188
|
},
|
|
287
189
|
},
|
|
288
|
-
{
|
|
289
|
-
name: "get_decision",
|
|
190
|
+
get_decision: {
|
|
290
191
|
description: "One open decision in your allowlist in full: question, options with id, label, reversible, and consequence. Never the builder's recommendation.",
|
|
291
|
-
inputSchema: { type: "object", properties: { decision: { type: "integer", minimum: 1 } }, required: ["decision"], additionalProperties: false },
|
|
292
192
|
handle: (ctx, args) => {
|
|
293
|
-
const found = decisionOver(ctx.store, ctx.who.repos,
|
|
193
|
+
const found = decisionOver(ctx.store, ctx.who.repos, args.decision, ctx.now);
|
|
294
194
|
if (found === null)
|
|
295
195
|
return { ok: false, message: "not-found: no such decision in your repositories" };
|
|
296
|
-
ctx.readDecisions.add(
|
|
196
|
+
ctx.readDecisions.add(args.decision);
|
|
297
197
|
return { ok: true, body: labelRepos(found, index => ctx.who.repos[index] ?? "") };
|
|
298
198
|
},
|
|
299
199
|
},
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
200
|
+
propose_next: proposeTool("next", "Propose moving a queued task to the front of its column. An approver confirms; a queue that moved meanwhile refuses."),
|
|
201
|
+
propose_reserve: proposeTool("reserve", "Propose reserving a queued task for one worker, or releasing it to the shared queue with worker null."),
|
|
202
|
+
propose_hold: proposeTool("hold", "Propose holding a task's next attempt, with a reason. A running attempt is never interrupted."),
|
|
203
|
+
propose_unhold: proposeTool("unhold", "Propose lifting the operator's own hold on a task."),
|
|
204
|
+
propose_scope: proposeTool("scope", "Propose rewriting a task's scope. An approver confirms the rewrite, then approves it with a password — a scope you wrote never seals under a mode."),
|
|
205
|
+
propose_cancel: proposeTool("cancel", "Propose cancelling a task, with a reason. The approver arms and confirms it on the task itself."),
|
|
206
|
+
propose_answer: proposeTool("answer", "Propose an answer to an open decision you read with get_decision, with a rationale. The approver confirms where every consequence and the builder's recommendation are shown; an irreversible option needs their explicit confirmation."),
|
|
207
|
+
get_contract: {
|
|
303
208
|
description: "This surface's contract: what a coordinator may do, the proposal lifecycle, and the admission promise.",
|
|
304
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
305
209
|
handle: () => ({ ok: true, body: { contract: CONTRACT_GUIDE } }),
|
|
306
210
|
},
|
|
307
|
-
{
|
|
308
|
-
name: "file_proposal",
|
|
211
|
+
file_proposal: {
|
|
309
212
|
description: "File a task proposal into one of your repositories. It stays quarantined until the operator signs its scope.",
|
|
310
|
-
inputSchema: {
|
|
311
|
-
type: "object",
|
|
312
|
-
properties: {
|
|
313
|
-
repo: { type: "string", minLength: 1, maxLength: 800 },
|
|
314
|
-
title: { type: "string", minLength: 1, maxLength: 200 },
|
|
315
|
-
intent: TASK_SCOPE_TEXT_SCHEMA,
|
|
316
|
-
idempotency_key: { type: "string", minLength: 8, maxLength: 64 },
|
|
317
|
-
deliverable: { type: "string", enum: ["branch", "report"] },
|
|
318
|
-
},
|
|
319
|
-
required: ["repo", "title", "idempotency_key"],
|
|
320
|
-
additionalProperties: false,
|
|
321
|
-
},
|
|
322
213
|
handle: (ctx, args) => {
|
|
323
|
-
const repo = str(args, "repo", 800);
|
|
324
|
-
const title = str(args, "title", 200);
|
|
325
|
-
const key = str(args, "idempotency_key", 64);
|
|
326
|
-
const intent = typeof args["intent"] === "string" ? args["intent"] : undefined;
|
|
327
|
-
const deliverable = args["deliverable"] === "report" ? "report" : args["deliverable"] === "branch" ? "branch" : undefined;
|
|
328
|
-
if (repo === null || title === null || key === null) {
|
|
329
|
-
return { ok: false, message: "file_proposal needs repo, title, and idempotency_key (8-64 chars)" };
|
|
330
|
-
}
|
|
331
214
|
// The token, not the pre-verified identity: filing re-authenticates
|
|
332
215
|
// INSIDE its own transaction (the session's `who` is a courtesy).
|
|
333
|
-
const outcome = fileCoordinatorProposal(ctx.store, ctx.token, { repo, title, ...(intent === undefined ? {} : { intent }), ...(deliverable === undefined ? {} : { deliverable }), idempotencyKey:
|
|
216
|
+
const outcome = fileCoordinatorProposal(ctx.store, ctx.token, { repo: args.repo, title: args.title, ...(args.intent === undefined ? {} : { intent: args.intent }), ...(args.deliverable === undefined ? {} : { deliverable: args.deliverable }), idempotencyKey: args.idempotency_key }, ctx.now);
|
|
334
217
|
if (!outcome.ok)
|
|
335
218
|
return { ok: false, message: outcome.message };
|
|
336
219
|
return {
|
|
@@ -343,31 +226,12 @@ const TOOLS = [
|
|
|
343
226
|
};
|
|
344
227
|
},
|
|
345
228
|
},
|
|
346
|
-
|
|
347
|
-
/** The
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
description,
|
|
353
|
-
inputSchema: { type: "object", properties, required, additionalProperties: false },
|
|
354
|
-
handle: (ctx, args) => {
|
|
355
|
-
const outcome = proposeAsCoordinator(ctx.store, ctx.token, kind, args, ctx.now, { readDecisions: ctx.readDecisions });
|
|
356
|
-
if (!outcome.ok)
|
|
357
|
-
return { ok: false, message: outcome.message };
|
|
358
|
-
return { ok: true, body: { proposal: outcome.id, kind: outcome.kind, awaiting: outcome.awaiting } };
|
|
359
|
-
},
|
|
360
|
-
});
|
|
361
|
-
return [
|
|
362
|
-
make("next", "Propose moving a queued task to the front of its column. An approver confirms; a queue that moved meanwhile refuses.", { ref }, ["ref"]),
|
|
363
|
-
make("reserve", "Propose reserving a queued task for one worker, or releasing it to the shared queue with worker null.", { ref, worker: { type: ["string", "null"], maxLength: 60 } }, ["ref", "worker"]),
|
|
364
|
-
make("hold", "Propose holding a task's next attempt, with a reason. A running attempt is never interrupted.", { ref, reason: { type: "string", maxLength: 200 } }, ["ref", "reason"]),
|
|
365
|
-
make("unhold", "Propose lifting the operator's own hold on a task.", { ref }, ["ref"]),
|
|
366
|
-
make("scope", "Propose rewriting a task's scope. An approver confirms the rewrite, then approves it with a password — a scope you wrote never seals under a mode.", { ref, goal: TASK_SCOPE_TEXT_SCHEMA, not: TASK_SCOPE_TEXT_SCHEMA, touches: { type: "array", items: { type: "string", maxLength: 200 }, maxItems: 50 } }, ["ref", "goal"]),
|
|
367
|
-
make("cancel", "Propose cancelling a task, with a reason. The approver arms and confirms it on the task itself.", { ref, reason: { type: "string", maxLength: 200 } }, ["ref", "reason"]),
|
|
368
|
-
make("answer", "Propose an answer to an open decision you read with get_decision, with a rationale. The approver confirms where every consequence and the builder's recommendation are shown; an irreversible option needs their explicit confirmation.", { decision: { type: "integer", minimum: 1 }, option: { type: "string", minLength: 1, maxLength: 64 }, rationale: { type: "string", maxLength: 400 } }, ["decision", "option", "rationale"]),
|
|
369
|
-
];
|
|
370
|
-
}
|
|
229
|
+
};
|
|
230
|
+
/** The gateway's tools in tools/list order, each inputSchema derived from its contract. */
|
|
231
|
+
const TOOLS = Object.keys(GATEWAY_TOOL_INPUTS).map((name) => {
|
|
232
|
+
const handler = HANDLERS[name];
|
|
233
|
+
return { name, description: handler.description, inputSchema: toModelSchema(GATEWAY_TOOL_INPUTS[name]), handle: (ctx, args) => handler.handle(ctx, args) };
|
|
234
|
+
});
|
|
371
235
|
function fileIdentity(path) {
|
|
372
236
|
if (path === null)
|
|
373
237
|
return null;
|
|
@@ -474,17 +338,21 @@ export function serveMcp(store, token, io, clock = () => new Date(), enrolled =
|
|
|
474
338
|
error(id, -32602, "arguments must be an object");
|
|
475
339
|
return;
|
|
476
340
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
341
|
+
// The call is read by the schema tools/list advertised (review finding
|
|
342
|
+
// 2): unknown fields, bad types, bad enums, and bound violations are
|
|
343
|
+
// protocol errors (InvalidParams) naming each path, never tool refusals.
|
|
344
|
+
const read = parseContract(GATEWAY_TOOL_INPUTS[tool.name], rawArgs);
|
|
345
|
+
if (!read.ok) {
|
|
346
|
+
error(id, -32602, read.issues.map(one => one.line).join("; "));
|
|
481
347
|
return;
|
|
482
348
|
}
|
|
483
|
-
const answered = tool.handle({ store, who: session.who, token, enrolled, now: clock(), readDecisions, ...(evidenceRoot === undefined ? {} : { evidenceRoot }) },
|
|
349
|
+
const answered = tool.handle({ store, who: session.who, token, enrolled, now: clock(), readDecisions, ...(evidenceRoot === undefined ? {} : { evidenceRoot }) }, read.value);
|
|
484
350
|
if (!answered.ok) {
|
|
485
351
|
respond(id, era, { content: [{ type: "text", text: answered.message }], isError: true }, false);
|
|
486
352
|
return;
|
|
487
353
|
}
|
|
354
|
+
// Checked against its output schema before delivery; a disagreement is reported, never refused.
|
|
355
|
+
reportToolOutput("gateway", tool.name, GATEWAY_TOOL_OUTPUTS[tool.name], answered.body);
|
|
488
356
|
respond(id, era, { content: [{ type: "text", text: JSON.stringify(answered.body) }] }, false);
|
|
489
357
|
});
|
|
490
358
|
};
|
package/dist/memory-pass.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { Store } from './store.js';
|
|
2
2
|
import { performSubscriptionMateRequest } from './subscription-chat.js';
|
|
3
|
-
|
|
3
|
+
import { type MemoryProposal, type Verdict } from './contracts/memory-pass.js';
|
|
4
|
+
export type { MemoryProposal, Verdict };
|
|
5
|
+
/** One session's trace, in characters (TEXT_LIMITS.memoryTrace). */
|
|
6
|
+
export declare const MEMORY_TRACE_BYTES: 40000;
|
|
4
7
|
export declare const MEMORY_MIN_SESSIONS = 2;
|
|
5
8
|
export declare const MEMORY_MAX_PROPOSALS = 6;
|
|
6
|
-
|
|
9
|
+
/** The project instructions' budget: the same limit knowledge holds them to. */
|
|
10
|
+
export declare const INSTRUCTION_BUDGET_BYTES: 4000;
|
|
7
11
|
export declare const MEMORY_GAP_MAX_AGE_MS: number;
|
|
8
12
|
export type MemorySource = {
|
|
9
13
|
id: string;
|
|
@@ -37,26 +41,6 @@ export type MemorySurface = {
|
|
|
37
41
|
};
|
|
38
42
|
/** The always-loaded surface: instruction lines with stable ids, active decisions with theirs. Its hash keys the analysis cache. */
|
|
39
43
|
export declare function memorySurface(store: Store, repo: string, actor: string): MemorySurface;
|
|
40
|
-
export type Verdict = {
|
|
41
|
-
positive: {
|
|
42
|
-
instruction: string;
|
|
43
|
-
effect: string;
|
|
44
|
-
quote: string;
|
|
45
|
-
}[];
|
|
46
|
-
negative: {
|
|
47
|
-
instruction: string;
|
|
48
|
-
effect: string;
|
|
49
|
-
class: 'harm' | 'non-compliance' | 'irrelevant';
|
|
50
|
-
quote: string;
|
|
51
|
-
}[];
|
|
52
|
-
gaps: {
|
|
53
|
-
mistake: string;
|
|
54
|
-
proposedInstruction: string;
|
|
55
|
-
domain: 'project' | 'orchestration';
|
|
56
|
-
quote: string;
|
|
57
|
-
matchesGap?: string;
|
|
58
|
-
}[];
|
|
59
|
-
};
|
|
60
44
|
export type MemoryAnalyzer = (input: {
|
|
61
45
|
trace: string;
|
|
62
46
|
surface: MemorySurface;
|
|
@@ -84,8 +68,17 @@ export declare function defaultAnalyzer(store: Store, options: {
|
|
|
84
68
|
timeoutMs?: number;
|
|
85
69
|
runner?: Parameters<typeof performSubscriptionMateRequest>[1];
|
|
86
70
|
}): MemoryAnalyzer;
|
|
87
|
-
/**
|
|
88
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Read the analyser's answer with the tolerant parse-side schema, then keep only claims whose quote
|
|
73
|
+
* is really in the trace and, for an instruction's effect, whose id is on the surface audited.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseVerdict(text: string, trace: string, surface: MemorySurface): {
|
|
76
|
+
ok: true;
|
|
77
|
+
verdict: Verdict;
|
|
78
|
+
} | {
|
|
79
|
+
ok: false;
|
|
80
|
+
problem: string;
|
|
81
|
+
};
|
|
89
82
|
export type PassReport = {
|
|
90
83
|
repo: string;
|
|
91
84
|
surface: string;
|
|
@@ -111,24 +104,6 @@ export declare function runMemoryPass(store: Store, args: {
|
|
|
111
104
|
local?: boolean;
|
|
112
105
|
maxSessions?: number;
|
|
113
106
|
}, now?: Date): Promise<PassReport>;
|
|
114
|
-
export type MemoryProposal = {
|
|
115
|
-
id: number;
|
|
116
|
-
repo: string;
|
|
117
|
-
kind: 'instruction-add' | 'instruction-remove' | 'decision-add';
|
|
118
|
-
fingerprint: string;
|
|
119
|
-
title: string;
|
|
120
|
-
rationale: string;
|
|
121
|
-
beforeText: string | null;
|
|
122
|
-
afterText: string;
|
|
123
|
-
evidence: {
|
|
124
|
-
session: string;
|
|
125
|
-
quote: string;
|
|
126
|
-
}[];
|
|
127
|
-
sessions: number;
|
|
128
|
-
status: string;
|
|
129
|
-
createdAt: string;
|
|
130
|
-
surface: string;
|
|
131
|
-
};
|
|
132
107
|
/**
|
|
133
108
|
* Turn corroborated gaps and harm into a few proposals: an addition needs
|
|
134
109
|
* the gap seen in at least two distinct sessions; a removal needs harm from
|