@telora/daemon 0.19.3 → 0.19.6

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/build-info.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "commitSha": "99cad9d2c",
3
- "builtAt": "2026-06-28T10:43:22.563Z",
2
+ "commitSha": "4523841b3",
3
+ "builtAt": "2026-06-28T15:29:31.449Z",
4
4
  "expectedMigrations": [
5
5
  "20250829113330_create_org_nodes_table.sql",
6
6
  "20250829113402_fix_function_security_search_path.sql",
@@ -687,6 +687,7 @@
687
687
  "20260626201457_seed_builtin_stage_models_opus.sql",
688
688
  "20260626204927_local_model_sizing_metadata.sql",
689
689
  "20260627124000_local_model_sizing_metadata_writes.sql",
690
- "20260627231555_normalize_focus_coding_mcp_tools.sql"
690
+ "20260627231555_normalize_focus_coding_mcp_tools.sql",
691
+ "20260628140456_loop_review_watermark_and_proposals.sql"
691
692
  ]
692
693
  }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Localhost translating proxy for local-model (OSS) Codex Responses traffic.
3
+ *
4
+ * Codex serializes each MCP server as one `type:"namespace"` Responses tool;
5
+ * OSS inference servers (llama-server) drop that tool type, so the model never
6
+ * sees Telora MCP tools (see namespace-bridge.ts + the runbook). This proxy is
7
+ * the localhost shim that bridges the gap for registered local-model passes
8
+ * ONLY -- hosted/cloud and Claude passes never route through it.
9
+ *
10
+ * Request path (model-bound, CERTAIN): the proxy buffers each `/responses`
11
+ * request, flattens `mcp__*` namespace tools into ordinary `function` tools via
12
+ * flattenNamespaceTools(), and forwards the rewritten body upstream so a
13
+ * namespace-incompatible server receives the Telora tool schemas.
14
+ *
15
+ * Response path (Codex-bound, CAPTURE-GATED): the upstream SSE stream is parsed
16
+ * and any flat `mcp__`-named function call is rewritten toward Codex's canonical
17
+ * namespace identity via normalizeToolCall(). The EXACT envelope Codex accepts
18
+ * is confirmed against a live OSS capture before routing is flipped on by
19
+ * default (runbook step 3); until then this stays opt-in and the rewrite is the
20
+ * single isolated place to pin once the capture lands.
21
+ *
22
+ * Reception guard: the proxy records whether any tools-bearing request it
23
+ * forwarded actually carried the required Telora control tools (after
24
+ * flattening). The spawn path reads getReceptionState() to fail a tool-less
25
+ * pass loud (durable session) instead of letting it silently loop.
26
+ *
27
+ * Binds 127.0.0.1 on an ephemeral port. Logs are redacted (no prompt content).
28
+ */
29
+ /** Tools a local-model coding pass must be able to call to sync Telora state. */
30
+ export declare const PROXY_REQUIRED_TOOLS: readonly ["telora_product_issue", "telora_product_delivery"];
31
+ /**
32
+ * Env flag that opts a daemon into routing local-model passes through this
33
+ * proxy. OPT-IN (default off) until the response envelope is confirmed against a
34
+ * live OSS capture (runbook step 3) -- unlike the opt-OUT loop flags.
35
+ */
36
+ export declare const LOCAL_MODEL_MCP_PROXY_FLAG = "TELORA_LOCAL_MODEL_MCP_PROXY";
37
+ /**
38
+ * Pure decision: should this pass route through the translating proxy? True only
39
+ * for a registered local-model pass when the opt-in flag is truthy. A non-local
40
+ * pass (hosted/cloud/Claude) NEVER routes -- regardless of the flag -- so the
41
+ * hosted path is provably untouched.
42
+ *
43
+ * Opt-in semantics (inverse of shouldRunLoop): undefined/''/'0'/'false' -> off;
44
+ * any other value -> on.
45
+ */
46
+ export declare function shouldRouteLocalModelThroughProxy(flagValue: string | undefined, isLocalModelPass: boolean): boolean;
47
+ export interface ProxyReceptionState {
48
+ /** A request carrying a non-empty tools array was forwarded at least once. */
49
+ sawToolsRequest: boolean;
50
+ /** Every required Telora tool was present (after flattening) in such a request. */
51
+ requiredToolsDelivered: boolean;
52
+ /** Required tools observed missing from a tools-bearing request. */
53
+ missingTools: string[];
54
+ }
55
+ export interface CodexMcpProxyHandle {
56
+ /** Base URL to advertise to Codex as the provider base_url (mirrors upstream path). */
57
+ url: string;
58
+ port: number;
59
+ getReceptionState(): ProxyReceptionState;
60
+ close(): Promise<void>;
61
+ }
62
+ export interface StartProxyOptions {
63
+ /** The real OSS inference server base URL, e.g. http://10.1.1.x:8888/v1. */
64
+ upstreamBaseUrl: string;
65
+ requiredTools?: readonly string[];
66
+ /** Injected fetch (tests); defaults to global fetch. */
67
+ fetchImpl?: typeof fetch;
68
+ /** Injected logger (tests); defaults to console. */
69
+ logger?: {
70
+ log: (msg: string) => void;
71
+ warn: (msg: string) => void;
72
+ };
73
+ }
74
+ /**
75
+ * Decode the set of MCP sub-tools present in a (already-flattened) tools array.
76
+ * Plain built-in functions (non-mcp__ names) are ignored.
77
+ */
78
+ export declare function decodeDeliveredTools(tools: unknown): Set<string>;
79
+ /** Exit category for a pass whose required Telora tools never reached the model. */
80
+ export declare const MCP_TOOLS_UNDELIVERED_EXIT_CATEGORY = "mcp_tools_undelivered";
81
+ /**
82
+ * Durable terminal-session payload for a pass that ran without its Telora
83
+ * control surface. Same shape as the other session-update builders; kept in this
84
+ * neutral module so the close handler (spawner/helpers) can use it without a
85
+ * circular import on focus-executor.
86
+ */
87
+ export declare function mcpToolsUndeliveredSessionUpdate(missingTools: readonly string[]): {
88
+ status: string;
89
+ exit_reason: string;
90
+ exit_category: string;
91
+ };
92
+ /**
93
+ * Pure decision: given a proxy's final reception state, should this pass be
94
+ * failed closed (durable) because the model never received its required Telora
95
+ * control tools? True only when a tools-bearing request was seen AND a required
96
+ * tool was missing -- a pass that simply never sent tools (no model turn) is not
97
+ * penalized. Drives mcpToolsUndeliveredSessionUpdate at team close.
98
+ */
99
+ export declare function shouldFailClosedForUndeliveredTools(reception: ProxyReceptionState): {
100
+ fail: boolean;
101
+ missing: string[];
102
+ };
103
+ /**
104
+ * Update a reception state given the tools array of a freshly-flattened request.
105
+ * Only requests that actually carry tools count toward delivery (the first model
106
+ * turn carries the tool list; later turns may omit it). Pure; returned as a new
107
+ * object.
108
+ */
109
+ export declare function updateReceptionState(prev: ProxyReceptionState, flattenedTools: unknown, requiredTools: readonly string[]): ProxyReceptionState;
110
+ /**
111
+ * Rewrite a single decoded SSE event object: if it carries a flat `mcp__`
112
+ * function call, normalize it toward Codex's canonical namespace/tool identity.
113
+ * Returns the (possibly) rewritten object. CAPTURE-GATED shape -- see module
114
+ * header; pin here once the live envelope is confirmed.
115
+ */
116
+ export declare function rewriteResponseEvent(event: unknown): unknown;
117
+ /**
118
+ * Transform an SSE response chunk stream. Stateful over `data:` lines: each
119
+ * complete `data: {json}` line is parsed, run through rewriteResponseEvent, and
120
+ * re-serialized. Non-data lines and unparseable payloads pass through verbatim.
121
+ * Returns a function that consumes raw text chunks and returns transformed text.
122
+ */
123
+ export declare function createSseTransformer(): (chunk: string) => string;
124
+ /**
125
+ * Start the localhost translating proxy. Resolves once it is listening.
126
+ */
127
+ export declare function startCodexMcpProxy(opts: StartProxyOptions): Promise<CodexMcpProxyHandle>;
128
+ //# sourceMappingURL=codex-mcp-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-mcp-proxy.d.ts","sourceRoot":"","sources":["../../../src/backends/codex/codex-mcp-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAWH,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,8DAA+D,CAAC;AAEjG;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,iCAAiC,CAAC;AAEzE;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,EAAE,OAAO,GACxB,OAAO,CAIT;AAED,MAAM,WAAW,mBAAmB;IAClC,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;IACzB,mFAAmF;IACnF,sBAAsB,EAAE,OAAO,CAAC;IAChC,oEAAoE;IACpE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,IAAI,mBAAmB,CAAC;IACzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,oDAAoD;IACpD,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACtE;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAUhE;AAED,oFAAoF;AACpF,eAAO,MAAM,mCAAmC,0BAA0B,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,CASA;AAED;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,mBAAmB,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAGtC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,mBAAmB,EACzB,cAAc,EAAE,OAAO,EACvB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,mBAAmB,CASrB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA4B5D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAahE;AA0BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA8FxF"}
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Localhost translating proxy for local-model (OSS) Codex Responses traffic.
3
+ *
4
+ * Codex serializes each MCP server as one `type:"namespace"` Responses tool;
5
+ * OSS inference servers (llama-server) drop that tool type, so the model never
6
+ * sees Telora MCP tools (see namespace-bridge.ts + the runbook). This proxy is
7
+ * the localhost shim that bridges the gap for registered local-model passes
8
+ * ONLY -- hosted/cloud and Claude passes never route through it.
9
+ *
10
+ * Request path (model-bound, CERTAIN): the proxy buffers each `/responses`
11
+ * request, flattens `mcp__*` namespace tools into ordinary `function` tools via
12
+ * flattenNamespaceTools(), and forwards the rewritten body upstream so a
13
+ * namespace-incompatible server receives the Telora tool schemas.
14
+ *
15
+ * Response path (Codex-bound, CAPTURE-GATED): the upstream SSE stream is parsed
16
+ * and any flat `mcp__`-named function call is rewritten toward Codex's canonical
17
+ * namespace identity via normalizeToolCall(). The EXACT envelope Codex accepts
18
+ * is confirmed against a live OSS capture before routing is flipped on by
19
+ * default (runbook step 3); until then this stays opt-in and the rewrite is the
20
+ * single isolated place to pin once the capture lands.
21
+ *
22
+ * Reception guard: the proxy records whether any tools-bearing request it
23
+ * forwarded actually carried the required Telora control tools (after
24
+ * flattening). The spawn path reads getReceptionState() to fail a tool-less
25
+ * pass loud (durable session) instead of letting it silently loop.
26
+ *
27
+ * Binds 127.0.0.1 on an ephemeral port. Logs are redacted (no prompt content).
28
+ */
29
+ import { createServer } from 'node:http';
30
+ import { flattenNamespaceTools, normalizeToolCall, decodeFlatToolName, redactRequestForLog, } from './namespace-bridge.js';
31
+ /** Tools a local-model coding pass must be able to call to sync Telora state. */
32
+ export const PROXY_REQUIRED_TOOLS = ['telora_product_issue', 'telora_product_delivery'];
33
+ /**
34
+ * Env flag that opts a daemon into routing local-model passes through this
35
+ * proxy. OPT-IN (default off) until the response envelope is confirmed against a
36
+ * live OSS capture (runbook step 3) -- unlike the opt-OUT loop flags.
37
+ */
38
+ export const LOCAL_MODEL_MCP_PROXY_FLAG = 'TELORA_LOCAL_MODEL_MCP_PROXY';
39
+ /**
40
+ * Pure decision: should this pass route through the translating proxy? True only
41
+ * for a registered local-model pass when the opt-in flag is truthy. A non-local
42
+ * pass (hosted/cloud/Claude) NEVER routes -- regardless of the flag -- so the
43
+ * hosted path is provably untouched.
44
+ *
45
+ * Opt-in semantics (inverse of shouldRunLoop): undefined/''/'0'/'false' -> off;
46
+ * any other value -> on.
47
+ */
48
+ export function shouldRouteLocalModelThroughProxy(flagValue, isLocalModelPass) {
49
+ if (!isLocalModelPass)
50
+ return false;
51
+ const v = (flagValue ?? '').trim().toLowerCase();
52
+ return v !== '' && v !== '0' && v !== 'false' && v !== 'off' && v !== 'no';
53
+ }
54
+ /**
55
+ * Decode the set of MCP sub-tools present in a (already-flattened) tools array.
56
+ * Plain built-in functions (non-mcp__ names) are ignored.
57
+ */
58
+ export function decodeDeliveredTools(tools) {
59
+ const out = new Set();
60
+ if (!Array.isArray(tools))
61
+ return out;
62
+ for (const t of tools) {
63
+ if (t?.type === 'function' && typeof t.name === 'string') {
64
+ const decoded = decodeFlatToolName(t.name);
65
+ if (decoded)
66
+ out.add(decoded.tool);
67
+ }
68
+ }
69
+ return out;
70
+ }
71
+ /** Exit category for a pass whose required Telora tools never reached the model. */
72
+ export const MCP_TOOLS_UNDELIVERED_EXIT_CATEGORY = 'mcp_tools_undelivered';
73
+ /**
74
+ * Durable terminal-session payload for a pass that ran without its Telora
75
+ * control surface. Same shape as the other session-update builders; kept in this
76
+ * neutral module so the close handler (spawner/helpers) can use it without a
77
+ * circular import on focus-executor.
78
+ */
79
+ export function mcpToolsUndeliveredSessionUpdate(missingTools) {
80
+ const missing = missingTools.length > 0 ? missingTools.join(', ') : '(unknown)';
81
+ return {
82
+ status: 'failed',
83
+ exit_reason: `Local-model pass ran without its Telora control surface: required tool(s) not delivered to the model: ${missing}. ` +
84
+ `Failing the pass durably instead of re-spawning into the same tool-less loop.`,
85
+ exit_category: MCP_TOOLS_UNDELIVERED_EXIT_CATEGORY,
86
+ };
87
+ }
88
+ /**
89
+ * Pure decision: given a proxy's final reception state, should this pass be
90
+ * failed closed (durable) because the model never received its required Telora
91
+ * control tools? True only when a tools-bearing request was seen AND a required
92
+ * tool was missing -- a pass that simply never sent tools (no model turn) is not
93
+ * penalized. Drives mcpToolsUndeliveredSessionUpdate at team close.
94
+ */
95
+ export function shouldFailClosedForUndeliveredTools(reception) {
96
+ const fail = reception.sawToolsRequest && !reception.requiredToolsDelivered;
97
+ return { fail, missing: fail ? reception.missingTools : [] };
98
+ }
99
+ /**
100
+ * Update a reception state given the tools array of a freshly-flattened request.
101
+ * Only requests that actually carry tools count toward delivery (the first model
102
+ * turn carries the tool list; later turns may omit it). Pure; returned as a new
103
+ * object.
104
+ */
105
+ export function updateReceptionState(prev, flattenedTools, requiredTools) {
106
+ if (!Array.isArray(flattenedTools) || flattenedTools.length === 0)
107
+ return prev;
108
+ const delivered = decodeDeliveredTools(flattenedTools);
109
+ const missing = requiredTools.filter(t => !delivered.has(t));
110
+ return {
111
+ sawToolsRequest: true,
112
+ requiredToolsDelivered: prev.requiredToolsDelivered || missing.length === 0,
113
+ missingTools: missing,
114
+ };
115
+ }
116
+ /**
117
+ * Rewrite a single decoded SSE event object: if it carries a flat `mcp__`
118
+ * function call, normalize it toward Codex's canonical namespace/tool identity.
119
+ * Returns the (possibly) rewritten object. CAPTURE-GATED shape -- see module
120
+ * header; pin here once the live envelope is confirmed.
121
+ */
122
+ export function rewriteResponseEvent(event) {
123
+ if (!event || typeof event !== 'object')
124
+ return event;
125
+ const ev = event;
126
+ // Codex emits function calls as output items; rewrite any item whose name is
127
+ // a flat mcp__ name. Handle both a bare function_call item and a response that
128
+ // nests output items.
129
+ const rewriteItem = (item) => {
130
+ if (item.type === 'function_call' && typeof item.name === 'string') {
131
+ const canonical = normalizeToolCall({
132
+ name: item.name,
133
+ arguments: item.arguments,
134
+ call_id: typeof item.call_id === 'string' ? item.call_id : undefined,
135
+ });
136
+ if (canonical) {
137
+ item.name = canonical.name;
138
+ item.namespace = canonical.namespace;
139
+ }
140
+ }
141
+ };
142
+ if (ev.type === 'function_call')
143
+ rewriteItem(ev);
144
+ if (ev.item && typeof ev.item === 'object')
145
+ rewriteItem(ev.item);
146
+ const response = ev.response;
147
+ if (response && Array.isArray(response.output)) {
148
+ for (const item of response.output) {
149
+ if (item && typeof item === 'object')
150
+ rewriteItem(item);
151
+ }
152
+ }
153
+ return ev;
154
+ }
155
+ /**
156
+ * Transform an SSE response chunk stream. Stateful over `data:` lines: each
157
+ * complete `data: {json}` line is parsed, run through rewriteResponseEvent, and
158
+ * re-serialized. Non-data lines and unparseable payloads pass through verbatim.
159
+ * Returns a function that consumes raw text chunks and returns transformed text.
160
+ */
161
+ export function createSseTransformer() {
162
+ let buffer = '';
163
+ return (chunk) => {
164
+ buffer += chunk;
165
+ let out = '';
166
+ let idx;
167
+ while ((idx = buffer.indexOf('\n')) !== -1) {
168
+ const line = buffer.slice(0, idx);
169
+ buffer = buffer.slice(idx + 1);
170
+ out += transformSseLine(line) + '\n';
171
+ }
172
+ return out;
173
+ };
174
+ }
175
+ function transformSseLine(line) {
176
+ const prefix = 'data:';
177
+ if (!line.startsWith(prefix))
178
+ return line;
179
+ const payload = line.slice(prefix.length).trimStart();
180
+ if (!payload || payload === '[DONE]')
181
+ return line;
182
+ let parsed;
183
+ try {
184
+ parsed = JSON.parse(payload);
185
+ }
186
+ catch {
187
+ return line; // not JSON we understand -- leave untouched
188
+ }
189
+ const rewritten = rewriteResponseEvent(parsed);
190
+ return `data: ${JSON.stringify(rewritten)}`;
191
+ }
192
+ function readBody(req) {
193
+ return new Promise((resolve, reject) => {
194
+ const chunks = [];
195
+ req.on('data', (c) => chunks.push(c));
196
+ req.on('end', () => resolve(Buffer.concat(chunks)));
197
+ req.on('error', reject);
198
+ });
199
+ }
200
+ /**
201
+ * Start the localhost translating proxy. Resolves once it is listening.
202
+ */
203
+ export function startCodexMcpProxy(opts) {
204
+ const requiredTools = opts.requiredTools ?? PROXY_REQUIRED_TOOLS;
205
+ const doFetch = opts.fetchImpl ?? fetch;
206
+ const logger = opts.logger ?? { log: (m) => console.log(m), warn: (m) => console.warn(m) };
207
+ const upstream = new URL(opts.upstreamBaseUrl);
208
+ let reception = {
209
+ sawToolsRequest: false,
210
+ requiredToolsDelivered: false,
211
+ missingTools: [],
212
+ };
213
+ const server = createServer((req, res) => {
214
+ void handleRequest(req, res);
215
+ });
216
+ async function handleRequest(req, res) {
217
+ try {
218
+ const raw = await readBody(req);
219
+ const isResponses = (req.url ?? '').includes('/responses') && (req.method ?? 'GET').toUpperCase() === 'POST';
220
+ let forwardBody = raw;
221
+ if (isResponses && raw.length > 0) {
222
+ try {
223
+ const parsed = JSON.parse(raw.toString('utf8'));
224
+ const flattened = flattenNamespaceTools(parsed);
225
+ reception = updateReceptionState(reception, flattened.tools, requiredTools);
226
+ forwardBody = JSON.stringify(flattened);
227
+ logger.log(`[codex-mcp-proxy] forwarded responses request ${JSON.stringify(redactRequestForLog(flattened))}`);
228
+ }
229
+ catch {
230
+ // Non-JSON body -- forward verbatim.
231
+ forwardBody = raw;
232
+ }
233
+ }
234
+ // Forward to upstream origin + the original request path.
235
+ const target = `${upstream.origin}${req.url ?? ''}`;
236
+ const headers = {};
237
+ for (const [k, v] of Object.entries(req.headers)) {
238
+ if (v === undefined)
239
+ continue;
240
+ if (k.toLowerCase() === 'host' || k.toLowerCase() === 'content-length')
241
+ continue;
242
+ headers[k] = Array.isArray(v) ? v.join(',') : v;
243
+ }
244
+ const upstreamResp = await doFetch(target, {
245
+ method: req.method,
246
+ headers,
247
+ body: req.method === 'GET' || req.method === 'HEAD' ? undefined : forwardBody,
248
+ });
249
+ const respHeaders = {};
250
+ upstreamResp.headers.forEach((value, key) => {
251
+ if (key.toLowerCase() === 'content-length')
252
+ return; // transformed length differs
253
+ respHeaders[key] = value;
254
+ });
255
+ res.writeHead(upstreamResp.status, respHeaders);
256
+ const contentType = upstreamResp.headers.get('content-type') ?? '';
257
+ if (contentType.includes('text/event-stream') && upstreamResp.body) {
258
+ const transform = createSseTransformer();
259
+ const reader = upstreamResp.body.getReader();
260
+ const decoder = new TextDecoder();
261
+ for (;;) {
262
+ const { done, value } = await reader.read();
263
+ if (done)
264
+ break;
265
+ res.write(transform(decoder.decode(value, { stream: true })));
266
+ }
267
+ res.end();
268
+ }
269
+ else {
270
+ const buf = Buffer.from(await upstreamResp.arrayBuffer());
271
+ res.end(buf);
272
+ }
273
+ }
274
+ catch (err) {
275
+ logger.warn(`[codex-mcp-proxy] upstream error: ${err.message}`);
276
+ if (!res.headersSent)
277
+ res.writeHead(502, { 'content-type': 'text/plain' });
278
+ res.end('proxy upstream error');
279
+ }
280
+ }
281
+ return new Promise((resolve, reject) => {
282
+ server.on('error', reject);
283
+ server.listen(0, '127.0.0.1', () => {
284
+ const port = server.address().port;
285
+ // Advertise the proxy with the SAME path as upstream so Codex's
286
+ // base_url + "/responses" reconstructs the upstream path on forward.
287
+ const url = `http://127.0.0.1:${port}${upstream.pathname.replace(/\/$/, '')}`;
288
+ resolve({
289
+ url,
290
+ port,
291
+ getReceptionState: () => reception,
292
+ close: () => new Promise((res) => server.close(() => res())),
293
+ });
294
+ });
295
+ });
296
+ }
297
+ //# sourceMappingURL=codex-mcp-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-mcp-proxy.js","sourceRoot":"","sources":["../../../src/backends/codex/codex-mcp-proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Read-only Telora MCP preflight for local-model passes.
3
+ *
4
+ * A local-model coding team is only useful if the agent can actually reach the
5
+ * Telora issue/delivery control surface through its MCP server. When that
6
+ * provisioning is broken -- the MCP package cannot be resolved, the server fails
7
+ * to start, or it does not expose the required tools -- a spawned team produces
8
+ * no useful work and no diagnostic: the focus sits "ready" and the daemon
9
+ * re-spawns into the same wall every poll (UDE #3, the silent spawn-loop wedge).
10
+ *
11
+ * This preflight proves, BEFORE the coding team spawns, that the required Telora
12
+ * tools are present and callable, by running a read-only JSON-RPC handshake
13
+ * (`initialize` + `tools/list`) against the provisioned Telora MCP server over
14
+ * stdio. On failure it returns a non-allowed gate result carrying a precise
15
+ * reason; the caller turns that into a durable terminal session and skips the
16
+ * spawn (the same fail-closed contract as gateLocalModelBackendSpawn). It NEVER
17
+ * mutates Telora -- the handshake reads the tool list only.
18
+ *
19
+ * The probe is an injectable seam so the gate is unit-testable without spawning
20
+ * a real subprocess.
21
+ */
22
+ import type { DaemonConfig } from '../../types.js';
23
+ /** Exit category stamped on the session when the MCP preflight refuses a spawn. */
24
+ export declare const MCP_PREFLIGHT_EXIT_CATEGORY = "mcp_preflight_failed";
25
+ /**
26
+ * The Telora control-surface tools a coding team must be able to call. A local
27
+ * pass that cannot see these cannot keep issue/delivery state synchronized, so
28
+ * their absence is terminal for the spawn.
29
+ */
30
+ export declare const MCP_PREFLIGHT_REQUIRED_TOOLS: readonly ["telora_product_issue", "telora_product_delivery"];
31
+ /** How long the handshake may take before the probe gives up (ms). */
32
+ export declare const MCP_PREFLIGHT_TIMEOUT_MS = 20000;
33
+ export interface McpPreflightResult {
34
+ allowed: boolean;
35
+ reason?: string;
36
+ /** Exit category for the durable terminal session when !allowed. */
37
+ category?: string;
38
+ }
39
+ /**
40
+ * Probe seam: resolve the tool names the provisioned Telora MCP server exposes.
41
+ * Production implementation spawns the server and runs the stdio handshake;
42
+ * tests inject a fake.
43
+ */
44
+ export type McpToolProbe = (config: DaemonConfig) => Promise<string[]>;
45
+ /**
46
+ * Real probe: spawn `node <telora-mcp-server>` with the execution profile env,
47
+ * perform the MCP `initialize` + `tools/list` handshake over stdio, and return
48
+ * the exposed tool names. Throws on resolution failure, spawn error, timeout,
49
+ * or a malformed/absent tools list. Read-only: it lists tools and never calls
50
+ * one.
51
+ *
52
+ * Identity (TELORA_URL / TELORA_TRACKER_ID) is intentionally NOT injected: the
53
+ * MCP server resolves it from the single source `~/.telora/daemon.json` via
54
+ * loadCredentials() (inherited process.env still propagates when present). The
55
+ * `config` parameter is part of the McpToolProbe seam (used by injected test
56
+ * fakes) but unused by this server-handshake implementation.
57
+ */
58
+ export declare function probeMcpToolsViaHandshake(_config: DaemonConfig): Promise<string[]>;
59
+ /**
60
+ * Gate a local-model spawn on a read-only proof that the required Telora MCP
61
+ * tools are callable. Returns {allowed:true} for every non-local pass (hosted /
62
+ * claude) without probing -- their MCP wiring is unchanged and out of scope.
63
+ *
64
+ * For a local pass: run the probe; if every required tool is present the gate
65
+ * allows the spawn, otherwise it refuses with a precise, actionable reason
66
+ * (resolution/spawn/timeout failure, or the specific missing tool names). The
67
+ * caller fails the spawn closed (durable terminal session, no team launched).
68
+ */
69
+ export declare function preflightLocalModelMcp(config: DaemonConfig, isLocalModelPass: boolean, opts?: {
70
+ probe?: McpToolProbe;
71
+ requiredTools?: readonly string[];
72
+ }): Promise<McpPreflightResult>;
73
+ //# sourceMappingURL=mcp-preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-preflight.d.ts","sourceRoot":"","sources":["../../../src/backends/codex/mcp-preflight.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,mFAAmF;AACnF,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,8DAG/B,CAAC;AAEX,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAUvE;;;;;;;;;;;;GAYG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgGxF;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,YAAY,EACpB,gBAAgB,EAAE,OAAO,EACzB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,YAAY,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GACrE,OAAO,CAAC,kBAAkB,CAAC,CAgC7B"}