@lerianstudio/matcher-mcp 1.3.0 → 1.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +26 -0
  2. package/dist/auth/request-token.js +50 -2
  3. package/dist/auth/request-token.js.map +1 -1
  4. package/dist/config.js +61 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/copilot/agent.js +181 -0
  7. package/dist/copilot/agent.js.map +1 -0
  8. package/dist/copilot/provider-anthropic.js +151 -0
  9. package/dist/copilot/provider-anthropic.js.map +1 -0
  10. package/dist/copilot/provider-factory.js +46 -0
  11. package/dist/copilot/provider-factory.js.map +1 -0
  12. package/dist/copilot/provider-openrouter.js +154 -0
  13. package/dist/copilot/provider-openrouter.js.map +1 -0
  14. package/dist/copilot/provider-shared.js +23 -0
  15. package/dist/copilot/provider-shared.js.map +1 -0
  16. package/dist/copilot/provider.js +15 -0
  17. package/dist/copilot/provider.js.map +1 -0
  18. package/dist/copilot/rate-limit.js +77 -0
  19. package/dist/copilot/rate-limit.js.map +1 -0
  20. package/dist/copilot/sse.js +69 -0
  21. package/dist/copilot/sse.js.map +1 -0
  22. package/dist/copilot/system-prompt.js +113 -0
  23. package/dist/copilot/system-prompt.js.map +1 -0
  24. package/dist/copilot/tenant-config.js +145 -0
  25. package/dist/copilot/tenant-config.js.map +1 -0
  26. package/dist/copilot/tools.js +241 -0
  27. package/dist/copilot/tools.js.map +1 -0
  28. package/dist/copilot/turns-handler.js +322 -0
  29. package/dist/copilot/turns-handler.js.map +1 -0
  30. package/dist/observability/otel.js +184 -2
  31. package/dist/observability/otel.js.map +1 -1
  32. package/dist/spec/openapi.yaml +82 -1
  33. package/dist/tools/context/create.js +3 -1
  34. package/dist/tools/context/create.js.map +1 -1
  35. package/dist/tools/context/index.js +3 -1
  36. package/dist/tools/context/index.js.map +1 -1
  37. package/dist/tools/context/next-step.js +93 -0
  38. package/dist/tools/context/next-step.js.map +1 -0
  39. package/dist/tools/source/create.js +3 -1
  40. package/dist/tools/source/create.js.map +1 -1
  41. package/dist/transport/body-limit.js +27 -0
  42. package/dist/transport/body-limit.js.map +1 -1
  43. package/dist/transport/http.js +35 -25
  44. package/dist/transport/http.js.map +1 -1
  45. package/package.json +3 -1
@@ -0,0 +1,241 @@
1
+ // Read-only tool surface over the matcher operation index.
2
+ //
3
+ // The copilot agent loop offers the model a list of matcher GET operations as
4
+ // JSON-Schema function defs (`buildReadToolDefs`) and — in task 1.2.2 —
5
+ // executes one by operationId through the capability layer. The GET filter is
6
+ // the safe read gate: a mutation can never enter the tool list, so the agent is
7
+ // structurally read-only in Phase 1. POST-shaped reads (e.g. simulateFeeSchedule)
8
+ // are deliberately excluded here and deferred to Phase 4's allow-list.
9
+ //
10
+ // The index is parsed once and memoized (`getIndex`), mirroring server.ts's
11
+ // boot-index memo so the tool-list builder and the executor share one parse and
12
+ // importing this module never touches the filesystem.
13
+ import { requireMatcherClient } from '../auth/request-token.js';
14
+ import { loadSpec } from '../spec/load.js';
15
+ import { InvokeError, Invoker } from '../tools/generic/invoke.js';
16
+ import { isMatcherApiError } from '../tools/tool-error.js';
17
+ /** The boot operation index, parsed once and memoized. Lazy so importing this
18
+ * module reads no files; the spec is immutable boot data. */
19
+ let indexCache;
20
+ export function getIndex() {
21
+ if (indexCache === undefined) {
22
+ indexCache = loadSpec().index;
23
+ }
24
+ return indexCache;
25
+ }
26
+ /**
27
+ * The read + write tool-def lists derived from an operation index, memoized by
28
+ * the index's IDENTITY. `getIndex()` returns one stable object, so the production
29
+ * path is built once; an injected index (tests) is memoized under its own
30
+ * identity, so it never returns another index's stale lists. The def-array
31
+ * synthesis is pure over the immutable index, so caching it is sound.
32
+ */
33
+ const toolDefsCache = new WeakMap();
34
+ export function getToolDefs(index) {
35
+ let cached = toolDefsCache.get(index);
36
+ if (cached === undefined) {
37
+ cached = { read: buildReadToolDefs(index), write: buildWriteToolDefs(index) };
38
+ toolDefsCache.set(index, cached);
39
+ }
40
+ return cached;
41
+ }
42
+ /**
43
+ * The config write surface the copilot may PROPOSE (never execute). Every entry
44
+ * is a non-GET operation over one of the seven configuration resource families —
45
+ * match rules, reconciliation contexts, fee schedules, fee rules, sources, field
46
+ * maps, and source bindings — across the create/update/delete/archive/restore/
47
+ * reorder/clone verbs the spec declares. This set is the write-barrier's positive
48
+ * gate: an operationId absent from it produces NO write tool def
49
+ * (`buildWriteToolDefs`) and the agent loop never routes a call to the proposal
50
+ * path — structurally unproposable, mirroring how a non-GET op is unreadable.
51
+ * Exception, match-run, ingestion, discovery, and governance mutations are
52
+ * deliberately excluded — scheduling and the simulate/preview POST-shaped
53
+ * "reads" too (they mutate nothing, so they are neither read nor proposed here).
54
+ */
55
+ export const WRITE_ALLOWLIST = new Set([
56
+ // match rules
57
+ 'createMatchRule',
58
+ 'updateMatchRule',
59
+ 'deleteMatchRule',
60
+ 'reorderMatchRules',
61
+ // reconciliation contexts
62
+ 'createContext',
63
+ 'updateContext',
64
+ 'archiveContext',
65
+ 'restoreContext',
66
+ 'cloneContext',
67
+ // fee schedules
68
+ 'createFeeSchedule',
69
+ 'updateFeeSchedule',
70
+ 'deleteFeeSchedule',
71
+ // fee rules
72
+ 'createFeeRule',
73
+ 'updateFeeRule',
74
+ 'deleteFeeRule',
75
+ // sources
76
+ 'createSource',
77
+ 'updateSource',
78
+ 'archiveSource',
79
+ 'restoreSource',
80
+ // field maps
81
+ 'createFieldMap',
82
+ 'updateFieldMap',
83
+ 'deleteFieldMap',
84
+ // source bindings
85
+ 'createSourceBinding',
86
+ 'updateSourceBinding',
87
+ 'deleteSourceBinding',
88
+ ]);
89
+ /**
90
+ * Synthesize the JSON Schema (2020-12) an operation's tool def advertises: a
91
+ * `pathParams` object (each name → `{type:'string'}`, all required), a `query`
92
+ * object (from the op's query params, `required` honored), and — when
93
+ * `includeBody` is set and the op declares a request body — a `body` property
94
+ * carrying the op's dereferenced request-body schema verbatim (required, so the
95
+ * model supplies a complete write). Containers are omitted when the op has none,
96
+ * so a param-less op advertises an empty object schema. Reads pass
97
+ * `includeBody: false` (GET carries no body); writes pass `true`.
98
+ */
99
+ function inputSchemaFor(op, includeBody = false) {
100
+ const properties = {};
101
+ const required = [];
102
+ if (op.pathParams.length > 0) {
103
+ const props = {};
104
+ for (const name of op.pathParams) {
105
+ props[name] = { type: 'string' };
106
+ }
107
+ properties.pathParams = {
108
+ type: 'object',
109
+ properties: props,
110
+ required: [...op.pathParams],
111
+ };
112
+ required.push('pathParams');
113
+ }
114
+ const queryEntries = Object.entries(op.queryParams);
115
+ if (queryEntries.length > 0) {
116
+ const props = {};
117
+ const requiredQuery = [];
118
+ for (const [name, param] of queryEntries) {
119
+ props[name] = { type: param.type };
120
+ if (param.required) {
121
+ requiredQuery.push(name);
122
+ }
123
+ }
124
+ properties.query = {
125
+ type: 'object',
126
+ properties: props,
127
+ ...(requiredQuery.length > 0 ? { required: requiredQuery } : {}),
128
+ };
129
+ if (requiredQuery.length > 0) {
130
+ required.push('query');
131
+ }
132
+ }
133
+ if (includeBody && op.requestBodySchema !== undefined) {
134
+ properties.body = op.requestBodySchema;
135
+ required.push('body');
136
+ }
137
+ return {
138
+ type: 'object',
139
+ properties,
140
+ ...(required.length > 0 ? { required } : {}),
141
+ };
142
+ }
143
+ /**
144
+ * Build the read-only tool list: one `LLMToolDef` per GET operation in the
145
+ * index, each with a synthesized JSON-Schema input. The GET filter is the read
146
+ * gate — no mutating operation can appear. Description is the op summary,
147
+ * falling back to `METHOD path` when the spec omits one.
148
+ */
149
+ export function buildReadToolDefs(index) {
150
+ const defs = [];
151
+ for (const op of index.operations.values()) {
152
+ if (op.method !== 'GET') {
153
+ continue;
154
+ }
155
+ defs.push({
156
+ name: op.operationId,
157
+ description: op.summary || `${op.method} ${op.path}`,
158
+ inputSchema: inputSchemaFor(op),
159
+ });
160
+ }
161
+ return defs;
162
+ }
163
+ /**
164
+ * Build the write tool list: one `LLMToolDef` per `WRITE_ALLOWLIST` operation
165
+ * present in the index, each advertising its request body (plus path/query
166
+ * params) so the model can PROPOSE a complete, well-shaped write. This set is
167
+ * disjoint from `buildReadToolDefs` by construction — the allow-list holds only
168
+ * non-GET config ops — so the agent loop routes a call by allow-list membership:
169
+ * a member is proposed (never executed), everything else takes the read path. An
170
+ * operationId absent from the allow-list yields no def and is unproposable.
171
+ */
172
+ export function buildWriteToolDefs(index) {
173
+ const defs = [];
174
+ for (const op of index.operations.values()) {
175
+ if (!WRITE_ALLOWLIST.has(op.operationId)) {
176
+ continue;
177
+ }
178
+ defs.push({
179
+ name: op.operationId,
180
+ description: op.summary || `${op.method} ${op.path}`,
181
+ inputSchema: inputSchemaFor(op, true),
182
+ });
183
+ }
184
+ return defs;
185
+ }
186
+ /**
187
+ * Execute one model-requested read tool through the existing capability layer
188
+ * (`Invoker.assemble` + the token-bound `MatcherClient`) and return a
189
+ * `LLMToolResult` the agent loop feeds back into the next turn.
190
+ *
191
+ * The op is looked up and GET-gated first (defense in depth — the tool list is
192
+ * already GET-only), so a mutating operationId is refused before any client is
193
+ * built and can never issue a request. The model's `input` carries `pathParams`
194
+ * and `query` per the schema `buildReadToolDefs` advertises; they are forwarded
195
+ * verbatim to `Invoker.assemble`, which owns path substitution and the tenant
196
+ * guardrail (a tenant_id/tenantId/tenant key is rejected there — the third rail
197
+ * stays enforced, never stripped-and-hidden). This function NEVER throws: every
198
+ * path — success, pre-flight InvokeError, matcher RFC 9457 problem, or an
199
+ * unexpected fault — resolves to a result so the loop always has one per call.
200
+ * The bearer token is never logged and never placed in a returned message.
201
+ *
202
+ * `clientFor` is injectable for tests (mirrors `dispatch`'s param); production
203
+ * passes nothing and gets the request-scoped, fail-closed `requireMatcherClient`.
204
+ */
205
+ export async function executeReadTool(call, index, clientFor = requireMatcherClient) {
206
+ const op = index.operations.get(call.name);
207
+ if (op === undefined || op.method !== 'GET') {
208
+ return { id: call.id, content: 'operation not available', isError: true };
209
+ }
210
+ try {
211
+ const pathParams = call.input.pathParams;
212
+ const query = call.input.query;
213
+ // ponytail: one Invoker per call (a fresh Ajv2020 that a GET never compiles);
214
+ // negligible next to the network round-trip. Memoize by index if tool-call
215
+ // volume ever dominates.
216
+ const request = new Invoker(index).assemble({
217
+ operationId: call.name,
218
+ ...(pathParams !== undefined ? { pathParams } : {}),
219
+ ...(query !== undefined ? { query } : {}),
220
+ });
221
+ const data = await clientFor().request(request);
222
+ return { id: call.id, content: JSON.stringify(data ?? null) };
223
+ }
224
+ catch (err) {
225
+ // Pre-flight failure (unknown op, missing path param, tenant guardrail, …).
226
+ if (err instanceof InvokeError) {
227
+ return { id: call.id, content: err.message, isError: true };
228
+ }
229
+ // Matcher RFC 9457 problem — safe to show the model (never token-bearing).
230
+ if (isMatcherApiError(err)) {
231
+ return {
232
+ id: call.id,
233
+ content: JSON.stringify({ status: err.status, ...err.problem }),
234
+ isError: true,
235
+ };
236
+ }
237
+ // Anything unexpected: a fixed, information-free message (no leak).
238
+ return { id: call.id, content: 'tool execution failed', isError: true };
239
+ }
240
+ }
241
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/copilot/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,322 @@
1
+ // The `POST /agent/turns` request handler — the copilot's HTTP entry point.
2
+ //
3
+ // It streams one agent turn over Server-Sent Events: parse + validate the
4
+ // stateless `AgentTurnRequest`, resolve the config-selected provider, build the
5
+ // tool list (read ops executed inline + allow-listed write ops the loop only
6
+ // PROPOSES) + matcher system prompt, fail fast on a missing bearer AND probe
7
+ // matcher's auth (one cheap authenticated GET) before constructing any paid
8
+ // provider, then drive the agent loop, forwarding text/tool/proposal/done to the
9
+ // SSE sink. Every
10
+ // named failure (bad body, over-cap body, unconfigured provider, missing bearer,
11
+ // unexpected fault) degrades to a single SSE `error` + `done` — the browser reads
12
+ // SSE, so there is no bare HTTP status to react to once the stream may have begun.
13
+ //
14
+ // Security: the bearer token is resolved from the request-scoped ALS store by
15
+ // `requireMatcherClient` (seeded at the HTTP boundary) and is never read, logged,
16
+ // or emitted here. No tenant identifier is accepted or forwarded — the tenant is
17
+ // implicit in the relayed JWT (the Invoker guardrail enforces it downstream).
18
+ import { createHash } from 'node:crypto';
19
+ import { getRequestBearerToken, getRequestTenantId, requireMatcherClient, UnauthenticatedError, } from '../auth/request-token.js';
20
+ import { MatcherApiError } from '../matcher/client.js';
21
+ import { BodyTooLargeError, readBody } from '../transport/body-limit.js';
22
+ import { runAgentTurn } from './agent.js';
23
+ import { CopilotUnconfiguredError, getProvider } from './provider-factory.js';
24
+ import { RateLimiter } from './rate-limit.js';
25
+ import { createSseSink } from './sse.js';
26
+ import { buildSystemPrompt } from './system-prompt.js';
27
+ import { resolveCopilotConfig } from './tenant-config.js';
28
+ import { getIndex, getToolDefs } from './tools.js';
29
+ /**
30
+ * Process-lifetime rate limiter shared across turns. Buckets are keyed on the
31
+ * key-only tenant id; a turn with no decodable tenant shares one bucket under
32
+ * RATE_LIMIT_ANON_KEY (the BYOC single-tenant / spoofed-token case — bounds a
33
+ * runaway client, not a determined adversary). Per-process → effective cluster
34
+ * limit is this × replica count.
35
+ */
36
+ const turnRateLimiter = new RateLimiter();
37
+ /** Shared bucket key for turns whose relayed token carries no decodable tenant. */
38
+ const RATE_LIMIT_ANON_KEY = '__no_tenant__';
39
+ /**
40
+ * Auth-probe decision cache: sha256(bearer) → epoch-ms expiry of a matcher-verified
41
+ * "authenticated" verdict. A short TTL lets one probe cover a burst of turns from
42
+ * the same caller instead of one probe per turn. Only SUCCESS is cached — a token
43
+ * matcher rejects re-probes each turn (a cheap Authorize reject; the attacker gains
44
+ * nothing and legit token refresh is never locked out). The raw token is NEVER
45
+ * stored — only its hash keys the map.
46
+ *
47
+ * Bounded at PROBE_CACHE_MAX_ENTRIES: on an insert that finds the map at/over the
48
+ * cap, expired entries are swept first, so a stream of distinct short-lived tokens
49
+ * cannot grow the map for the process lifetime. Exported for the bound test only.
50
+ */
51
+ const PROBE_CACHE_TTL_MS = 5_000;
52
+ export const PROBE_CACHE_MAX_ENTRIES = 10_000;
53
+ export const probeOkCache = new Map();
54
+ /**
55
+ * The cheapest Bearer-authenticated matcher GET. Matcher's `Authorize` middleware
56
+ * gates `/v1/contexts`, so a forged/expired token is rejected (401/403) here —
57
+ * before any paid LLM call. `limit=1` bounds the matcher-side query; the response
58
+ * body is discarded (only the auth verdict matters).
59
+ */
60
+ const AUTH_PROBE_REQUEST = {
61
+ method: 'GET',
62
+ path: '/v1/contexts',
63
+ query: { limit: 1 },
64
+ };
65
+ /** Operator remediation shown when matcher REJECTS the relayed token (401/403). */
66
+ const PROBE_REJECTED_REMEDIATION = 'The matcher credential was rejected. Check that your token is valid and has ' +
67
+ 'not expired, then retry. This server relays your credential verbatim and holds ' +
68
+ 'none of its own.';
69
+ /**
70
+ * Verify the relayed bearer against matcher BEFORE any paid provider call: a
71
+ * JWT-shaped but unauthorized token must not be able to spend LLM budget. Issues
72
+ * ONE authenticated GET (matcher's `Authorize` gates it) unless a fresh cached
73
+ * verdict exists. Returns `true` when matcher accepts the token, `false` when it
74
+ * rejects it (401/403). Any OTHER matcher failure (5xx, transport) is rethrown to
75
+ * the caller's last-resort guard — we fail closed rather than burn budget against
76
+ * an unverifiable backend. The raw token is never stored or logged; only its
77
+ * sha256 hash keys the cache.
78
+ */
79
+ async function probeMatcherAuth(client) {
80
+ const token = getRequestBearerToken();
81
+ const cacheKey = token !== undefined
82
+ ? createHash('sha256').update(token).digest('hex')
83
+ : undefined;
84
+ const now = Date.now();
85
+ if (cacheKey !== undefined && (probeOkCache.get(cacheKey) ?? 0) > now) {
86
+ return true; // fresh authenticated verdict — skip the probe
87
+ }
88
+ try {
89
+ await client.request(AUTH_PROBE_REQUEST);
90
+ }
91
+ catch (err) {
92
+ if (err instanceof MatcherApiError &&
93
+ (err.status === 401 || err.status === 403)) {
94
+ return false;
95
+ }
96
+ throw err;
97
+ }
98
+ if (cacheKey !== undefined) {
99
+ // ponytail: sweep-expired-on-insert-past-cap. The O(n) sweep runs only when the
100
+ // map is at the cap; with a 5s TTL, >10k live entries within that window is not
101
+ // a real load, so no LRU. If it ever is, evict oldest-by-expiry here.
102
+ if (probeOkCache.size >= PROBE_CACHE_MAX_ENTRIES) {
103
+ for (const [key, expiry] of probeOkCache) {
104
+ if (expiry <= now) {
105
+ probeOkCache.delete(key);
106
+ }
107
+ }
108
+ }
109
+ probeOkCache.set(cacheKey, now + PROBE_CACHE_TTL_MS);
110
+ }
111
+ return true;
112
+ }
113
+ /**
114
+ * The messages the agent loop receives. Browser-supplied `screenContext` is
115
+ * injected as a synthetic FIRST user message — the SAME (untrusted) trust tier as
116
+ * the operator's own input — rather than folded into the system prompt, so a
117
+ * crafted route/summary can never reach the higher-trust system tier. The label
118
+ * is a plain statement of fact, not an instruction.
119
+ */
120
+ function buildTurnMessages(request) {
121
+ const { screenContext } = request;
122
+ if (screenContext === undefined) {
123
+ return request.messages;
124
+ }
125
+ const parts = [`route ${screenContext.route}`];
126
+ if (screenContext.contextId !== undefined) {
127
+ parts.push(`reconciliation context ${screenContext.contextId}`);
128
+ }
129
+ if (screenContext.summary !== undefined) {
130
+ parts.push(screenContext.summary);
131
+ }
132
+ return [
133
+ {
134
+ role: 'user',
135
+ content: `[screen context] The operator is currently viewing: ${parts.join(' — ')}.`,
136
+ },
137
+ ...request.messages,
138
+ ];
139
+ }
140
+ /**
141
+ * Validate an unknown parsed body as an `AgentTurnRequest`. Returns the typed
142
+ * request on success, or `undefined` when the shape is wrong (non-empty
143
+ * `messages` of `{role,content}` with role in {user,assistant}). A malformed
144
+ * `screenContext` is dropped rather than rejecting the turn.
145
+ */
146
+ function parseAgentTurnRequest(body) {
147
+ if (typeof body !== 'object' || body === null) {
148
+ return undefined;
149
+ }
150
+ const record = body;
151
+ const { messages } = record;
152
+ if (!Array.isArray(messages) || messages.length === 0) {
153
+ return undefined;
154
+ }
155
+ for (const message of messages) {
156
+ if (typeof message !== 'object' || message === null) {
157
+ return undefined;
158
+ }
159
+ const { role, content } = message;
160
+ if (role !== 'user' && role !== 'assistant') {
161
+ return undefined;
162
+ }
163
+ if (typeof content !== 'string') {
164
+ return undefined;
165
+ }
166
+ }
167
+ const screenContext = parseScreenContext(record.screenContext);
168
+ return {
169
+ messages: messages,
170
+ ...(screenContext !== undefined ? { screenContext } : {}),
171
+ };
172
+ }
173
+ /** Parse an optional `screenContext`; drops it unless it carries a string `route`. */
174
+ function parseScreenContext(raw) {
175
+ if (typeof raw !== 'object' || raw === null) {
176
+ return undefined;
177
+ }
178
+ const record = raw;
179
+ if (typeof record.route !== 'string') {
180
+ return undefined;
181
+ }
182
+ return {
183
+ route: record.route,
184
+ ...(typeof record.contextId === 'string' ? { contextId: record.contextId } : {}),
185
+ ...(typeof record.summary === 'string' ? { summary: record.summary } : {}),
186
+ };
187
+ }
188
+ /**
189
+ * Handle one `POST /agent/turns` request, streaming the turn over SSE. Resolves
190
+ * once the turn is fully written (a terminal `done` has been emitted). Does not
191
+ * rethrow: any unexpected fault is caught and surfaced as a single `error`+`done`
192
+ * so the boundary never has to answer a half-streamed response.
193
+ */
194
+ export async function handleAgentTurn(config, req, res, deps = {}) {
195
+ const sink = createSseSink(res);
196
+ try {
197
+ // Fail fast on a missing/blank bearer before any body parsing or provider
198
+ // construction — the cheapest gate first, and so an unauthenticated probe
199
+ // learns nothing about the server's config (e.g. copilot_unconfigured). The
200
+ // client built here is REUSED for the pre-provider auth probe below (the read
201
+ // tools still resolve their own per-call client from the same request store).
202
+ let matcherClient;
203
+ try {
204
+ matcherClient = (deps.requireClient ??
205
+ (() => requireMatcherClient(undefined, config)))();
206
+ }
207
+ catch (err) {
208
+ if (err instanceof UnauthenticatedError) {
209
+ sink.error('unauthenticated', err.message);
210
+ sink.done('end');
211
+ return;
212
+ }
213
+ throw err;
214
+ }
215
+ const contentType = (req.headers['content-type'] ?? '').toLowerCase();
216
+ if (!contentType.includes('application/json')) {
217
+ sink.error('bad_request', 'content-type must be application/json');
218
+ sink.done('end');
219
+ return;
220
+ }
221
+ let body;
222
+ try {
223
+ const raw = await readBody(req, config.maxBodyBytes);
224
+ body = raw.length > 0 ? JSON.parse(raw) : undefined;
225
+ }
226
+ catch (err) {
227
+ if (err instanceof BodyTooLargeError) {
228
+ sink.error('payload_too_large', 'request body is too large');
229
+ sink.done('end');
230
+ return;
231
+ }
232
+ // JSON parse failure or a read error — a client fault, no internal detail.
233
+ sink.error('bad_request', 'request body is not valid JSON');
234
+ sink.done('end');
235
+ return;
236
+ }
237
+ const request = parseAgentTurnRequest(body);
238
+ if (request === undefined) {
239
+ sink.error('bad_request', 'messages must be a non-empty array of {role,content}, role one of user|assistant');
240
+ sink.done('end');
241
+ return;
242
+ }
243
+ // Probe matcher's `Authorize` with the relayed token BEFORE any tenant-derived
244
+ // work AND before constructing the paid provider (M1): a JWT-shaped-but-
245
+ // unauthorized token is rejected here and never reaches a billable LLM call —
246
+ // and, crucially, never selects a config slice or consumes a rate bucket, so a
247
+ // forged `tenant_id` cannot target a victim tenant's quota. Runs right after the
248
+ // body parse; probe volume is floored by the no-bearer reject in
249
+ // `requireMatcherClient`. A fresh cached verdict (keyed by token hash) skips the
250
+ // probe entirely.
251
+ if (!(await probeMatcherAuth(matcherClient))) {
252
+ sink.error('unauthenticated', PROBE_REJECTED_REMEDIATION);
253
+ sink.done('end');
254
+ return;
255
+ }
256
+ // Resolve the per-tenant config slice from the key-only tenant id decoded off
257
+ // the relayed JWT (getRequestTenantId). The token is already matcher-verified by
258
+ // the probe above, so a forged `tenant_id` can no longer reach this point to
259
+ // select a slice or key a bucket. A tenant with an override runs its own
260
+ // provider/model/key/limits; a tenant with no entry — or no decodable tenant —
261
+ // falls back to the global env config unchanged. The tenant is NEVER used for
262
+ // authorization here (matcher owns that via the relayed token); it only selects
263
+ // the config slice this turn uses and keys the rate bucket.
264
+ const tenantId = getRequestTenantId();
265
+ const resolvedConfig = resolveCopilotConfig(config, tenantId);
266
+ // Rate-limit gate: keyed on the now-verified tenant, applied before any provider
267
+ // spin-up so a rejected caller never triggers an LLM call. On limit → a single
268
+ // `rate_limited` SSE error + terminal done (a non-calm code → renders as
269
+ // TurnError in the UI, no UI change required). Sits after the auth probe, so a
270
+ // forged token is rejected as `unauthenticated` before it can touch a bucket.
271
+ const decision = (deps.rateLimiter ?? turnRateLimiter).check(tenantId ?? RATE_LIMIT_ANON_KEY, resolvedConfig.copilotRateLimitPerMin, resolvedConfig.copilotRateLimitBurst);
272
+ if (!decision.allowed) {
273
+ sink.error('rate_limited', `too many copilot requests; retry in ${decision.retryAfterSeconds}s`);
274
+ sink.done('end');
275
+ return;
276
+ }
277
+ let provider;
278
+ try {
279
+ provider = (deps.resolveProvider ?? getProvider)(resolvedConfig);
280
+ }
281
+ catch (err) {
282
+ if (err instanceof CopilotUnconfiguredError) {
283
+ sink.error('copilot_unconfigured', 'the copilot is not configured on this server');
284
+ sink.done('end');
285
+ return;
286
+ }
287
+ throw err;
288
+ }
289
+ const index = deps.index ?? getIndex();
290
+ // Reads execute inline; allow-listed writes are PROPOSED (never executed) by
291
+ // the agent loop's write-barrier. Both classes must be advertised or the model
292
+ // can never emit a write call and the proposal path is dead. The def lists are
293
+ // memoized by index identity (getToolDefs), so this is a cache hit per turn.
294
+ const { read, write } = getToolDefs(index);
295
+ await (deps.runTurn ?? runAgentTurn)({
296
+ provider,
297
+ tools: [...read, ...write],
298
+ index,
299
+ system: buildSystemPrompt(),
300
+ messages: buildTurnMessages(request),
301
+ sink,
302
+ maxIterations: resolvedConfig.copilotMaxToolIterations,
303
+ maxTurnTokens: resolvedConfig.copilotMaxTurnTokens,
304
+ // Telemetry labels (4.2.1): the instrumentation lives in runAgentTurn but is
305
+ // inert unless fed the resolved provider id, model, and key-only tenant.
306
+ providerName: resolvedConfig.copilotProvider,
307
+ ...(resolvedConfig.copilotModel !== undefined
308
+ ? { model: resolvedConfig.copilotModel }
309
+ : {}),
310
+ ...(tenantId !== undefined ? { tenant: tenantId } : {}),
311
+ });
312
+ }
313
+ catch (err) {
314
+ // Last-resort guard: a single safe SSE error + done, never a token or stack.
315
+ sink.error('internal', 'the copilot encountered an internal error');
316
+ sink.done('end');
317
+ // Log only the message string, never the raw error object — a provider-SDK
318
+ // error can carry request context (headers, config) we must not log wholesale.
319
+ console.error('matcher-mcp agent turn failed:', err instanceof Error ? err.message : String(err));
320
+ }
321
+ }
322
+ //# sourceMappingURL=turns-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turns-handler.js","sourceRoot":"","sources":["../../src/copilot/turns-handler.ts"],"names":[],"mappings":"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"}