xcode-copilot-server 1.0.4 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +56 -29
  2. package/config.json5 +49 -41
  3. package/dist/config.d.ts +9 -4
  4. package/dist/config.js +25 -7
  5. package/dist/config.js.map +1 -1
  6. package/dist/context.d.ts +1 -0
  7. package/dist/conversation-manager.d.ts +32 -0
  8. package/dist/conversation-manager.js +120 -0
  9. package/dist/conversation-manager.js.map +1 -0
  10. package/dist/copilot-service.d.ts +1 -3
  11. package/dist/copilot-service.js +3 -23
  12. package/dist/copilot-service.js.map +1 -1
  13. package/dist/handlers/completions/streaming.d.ts +1 -1
  14. package/dist/handlers/completions/streaming.js +13 -24
  15. package/dist/handlers/completions/streaming.js.map +1 -1
  16. package/dist/handlers/completions.d.ts +2 -2
  17. package/dist/handlers/completions.js +75 -63
  18. package/dist/handlers/completions.js.map +1 -1
  19. package/dist/handlers/errors.d.ts +5 -0
  20. package/dist/handlers/errors.js +10 -0
  21. package/dist/handlers/errors.js.map +1 -0
  22. package/dist/handlers/messages/count-tokens.d.ts +3 -0
  23. package/dist/handlers/messages/count-tokens.js +72 -0
  24. package/dist/handlers/messages/count-tokens.js.map +1 -0
  25. package/dist/handlers/messages/streaming.d.ts +6 -0
  26. package/dist/handlers/messages/streaming.js +274 -0
  27. package/dist/handlers/messages/streaming.js.map +1 -0
  28. package/dist/handlers/messages/tool-result-handler.d.ts +4 -0
  29. package/dist/handlers/messages/tool-result-handler.js +19 -0
  30. package/dist/handlers/messages/tool-result-handler.js.map +1 -0
  31. package/dist/handlers/messages.d.ts +4 -0
  32. package/dist/handlers/messages.js +150 -0
  33. package/dist/handlers/messages.js.map +1 -0
  34. package/dist/handlers/models.d.ts +0 -1
  35. package/dist/handlers/models.js +1 -2
  36. package/dist/handlers/models.js.map +1 -1
  37. package/dist/handlers/session-config.d.ts +15 -0
  38. package/dist/handlers/session-config.js +79 -0
  39. package/dist/handlers/session-config.js.map +1 -0
  40. package/dist/handlers/streaming-utils.d.ts +9 -0
  41. package/dist/handlers/streaming-utils.js +19 -0
  42. package/dist/handlers/streaming-utils.js.map +1 -0
  43. package/dist/index.js +22 -7
  44. package/dist/index.js.map +1 -1
  45. package/dist/logger.d.ts +0 -1
  46. package/dist/logger.js +4 -10
  47. package/dist/logger.js.map +1 -1
  48. package/dist/providers/anthropic.d.ts +5 -0
  49. package/dist/providers/anthropic.js +28 -0
  50. package/dist/providers/anthropic.js.map +1 -0
  51. package/dist/providers/index.d.ts +15 -0
  52. package/dist/providers/index.js +7 -0
  53. package/dist/providers/index.js.map +1 -0
  54. package/dist/providers/openai.d.ts +5 -0
  55. package/dist/providers/openai.js +25 -0
  56. package/dist/providers/openai.js.map +1 -0
  57. package/dist/providers/types.d.ts +7 -0
  58. package/dist/providers/types.js +2 -0
  59. package/dist/providers/types.js.map +1 -0
  60. package/dist/schemas/anthropic.d.ts +140 -0
  61. package/dist/schemas/anthropic.js +58 -0
  62. package/dist/schemas/anthropic.js.map +1 -0
  63. package/dist/schemas/config.d.ts +127 -0
  64. package/dist/schemas/config.js +44 -0
  65. package/dist/schemas/config.js.map +1 -0
  66. package/dist/schemas/openai.d.ts +98 -0
  67. package/dist/schemas/openai.js +76 -0
  68. package/dist/schemas/openai.js.map +1 -0
  69. package/dist/server.d.ts +2 -1
  70. package/dist/server.js +11 -20
  71. package/dist/server.js.map +1 -1
  72. package/dist/tool-bridge/index.d.ts +4 -0
  73. package/dist/tool-bridge/index.js +8 -0
  74. package/dist/tool-bridge/index.js.map +1 -0
  75. package/dist/tool-bridge/reply-tracker.d.ts +10 -0
  76. package/dist/tool-bridge/reply-tracker.js +28 -0
  77. package/dist/tool-bridge/reply-tracker.js.map +1 -0
  78. package/dist/tool-bridge/routes.d.ts +4 -0
  79. package/dist/tool-bridge/routes.js +111 -0
  80. package/dist/tool-bridge/routes.js.map +1 -0
  81. package/dist/tool-bridge/session-lifecycle.d.ts +16 -0
  82. package/dist/tool-bridge/session-lifecycle.js +43 -0
  83. package/dist/tool-bridge/session-lifecycle.js.map +1 -0
  84. package/dist/tool-bridge/state.d.ts +34 -0
  85. package/dist/tool-bridge/state.js +77 -0
  86. package/dist/tool-bridge/state.js.map +1 -0
  87. package/dist/tool-bridge/tool-cache.d.ts +10 -0
  88. package/dist/tool-bridge/tool-cache.js +82 -0
  89. package/dist/tool-bridge/tool-cache.js.map +1 -0
  90. package/dist/tool-bridge/tool-router.d.ts +12 -0
  91. package/dist/tool-bridge/tool-router.js +82 -0
  92. package/dist/tool-bridge/tool-router.js.map +1 -0
  93. package/dist/utils/anthropic-prompt.d.ts +2 -0
  94. package/dist/utils/anthropic-prompt.js +52 -0
  95. package/dist/utils/anthropic-prompt.js.map +1 -0
  96. package/dist/utils/model-resolver.d.ts +3 -0
  97. package/dist/utils/model-resolver.js +45 -0
  98. package/dist/utils/model-resolver.js.map +1 -0
  99. package/dist/utils/prompt.d.ts +2 -14
  100. package/dist/utils/prompt.js +11 -14
  101. package/dist/utils/prompt.js.map +1 -1
  102. package/package.json +5 -4
@@ -0,0 +1,274 @@
1
+ import { formatCompaction, SSE_HEADERS, sendSSEEvent as sendEvent } from "../streaming-utils.js";
2
+ const MCP_PREFIX = "xcode-bridge-";
3
+ // Xcode doesn't know about the "xcode-bridge-" prefix the CLI adds
4
+ function stripMCPPrefix(name) {
5
+ return name.startsWith(MCP_PREFIX) ? name.slice(MCP_PREFIX.length) : name;
6
+ }
7
+ export function startReply(reply, model) {
8
+ reply.raw.writeHead(200, SSE_HEADERS);
9
+ const messageStart = {
10
+ type: "message_start",
11
+ message: {
12
+ id: `msg_${String(Date.now())}`,
13
+ type: "message",
14
+ role: "assistant",
15
+ content: [],
16
+ model,
17
+ stop_reason: null,
18
+ usage: { input_tokens: 0, output_tokens: 0 },
19
+ },
20
+ };
21
+ sendEvent(reply, "message_start", messageStart);
22
+ }
23
+ export async function handleAnthropicStreaming(state, session, prompt, model, logger, hasBridge = false) {
24
+ const reply = state.currentReply;
25
+ if (!reply)
26
+ throw new Error("No reply set on bridge state");
27
+ startReply(reply, model);
28
+ state.markSessionActive();
29
+ let pendingDeltas = [];
30
+ let sessionDone = false;
31
+ let textBlockStarted = false;
32
+ const toolNames = new Map();
33
+ // This listener outlives the initial HTTP response because during a
34
+ // tool_use flow the reply gets ended, then Xcode sends a continuation
35
+ // request that sets a new reply on state. So, we read state.currentReply
36
+ // each time instead of closing over the original reply variable.
37
+ function getReply() {
38
+ return state.currentReply;
39
+ }
40
+ function ensureTextBlock(r) {
41
+ if (!textBlockStarted) {
42
+ const blockStart = {
43
+ type: "content_block_start",
44
+ index: 0,
45
+ content_block: { type: "text", text: "" },
46
+ };
47
+ sendEvent(r, "content_block_start", blockStart);
48
+ textBlockStarted = true;
49
+ }
50
+ }
51
+ function flushPending() {
52
+ const r = getReply();
53
+ if (!r || pendingDeltas.length === 0)
54
+ return;
55
+ ensureTextBlock(r);
56
+ for (const text of pendingDeltas) {
57
+ const delta = {
58
+ type: "content_block_delta",
59
+ index: 0,
60
+ delta: { type: "text_delta", text },
61
+ };
62
+ sendEvent(r, "content_block_delta", delta);
63
+ }
64
+ pendingDeltas = [];
65
+ }
66
+ function sendEpilogue(r, stopReason) {
67
+ const messageDelta = {
68
+ type: "message_delta",
69
+ delta: { stop_reason: stopReason, stop_sequence: null },
70
+ usage: { output_tokens: 0 },
71
+ };
72
+ sendEvent(r, "message_delta", messageDelta);
73
+ sendEvent(r, "message_stop", { type: "message_stop" });
74
+ }
75
+ function emitToolUseBlocks(r, toolRequests) {
76
+ let startIndex;
77
+ if (textBlockStarted) {
78
+ sendEvent(r, "content_block_stop", {
79
+ type: "content_block_stop",
80
+ index: 0,
81
+ });
82
+ startIndex = 1;
83
+ }
84
+ else {
85
+ startIndex = 0;
86
+ }
87
+ let index = startIndex;
88
+ for (const tr of toolRequests) {
89
+ sendEvent(r, "content_block_start", {
90
+ type: "content_block_start",
91
+ index,
92
+ content_block: { type: "tool_use", id: tr.toolCallId, name: tr.name, input: {} },
93
+ });
94
+ const argsJson = tr.arguments != null ? JSON.stringify(tr.arguments) : "{}";
95
+ sendEvent(r, "content_block_delta", {
96
+ type: "content_block_delta",
97
+ index,
98
+ delta: { type: "input_json_delta", partial_json: argsJson },
99
+ });
100
+ sendEvent(r, "content_block_stop", {
101
+ type: "content_block_stop",
102
+ index,
103
+ });
104
+ index++;
105
+ }
106
+ }
107
+ function finishReply(r, stopReason) {
108
+ if (stopReason === "end_turn" && textBlockStarted) {
109
+ sendEvent(r, "content_block_stop", {
110
+ type: "content_block_stop",
111
+ index: 0,
112
+ });
113
+ }
114
+ sendEpilogue(r, stopReason);
115
+ r.raw.end();
116
+ state.clearReply();
117
+ state.notifyStreamingDone();
118
+ textBlockStarted = false;
119
+ }
120
+ const unsubscribe = session.on((event) => {
121
+ logger.debug(`Session event: ${event.type}`);
122
+ if (event.type === "tool.execution_start") {
123
+ const d = event.data;
124
+ toolNames.set(d.toolCallId, d.toolName);
125
+ logger.debug(`Running ${d.toolName} (id=${d.toolCallId}, args=${JSON.stringify(d.arguments)})`);
126
+ return;
127
+ }
128
+ if (event.type === "tool.execution_complete") {
129
+ const d = event.data;
130
+ const name = toolNames.get(d.toolCallId) ?? d.toolCallId;
131
+ toolNames.delete(d.toolCallId);
132
+ const detail = d.success
133
+ ? JSON.stringify(d.result?.content)
134
+ : d.error?.message ?? "failed";
135
+ logger.debug(`${name} done (success=${String(d.success)}, ${detail})`);
136
+ return;
137
+ }
138
+ switch (event.type) {
139
+ case "assistant.message_delta":
140
+ if (event.data.deltaContent) {
141
+ logger.debug(`Delta: ${event.data.deltaContent}`);
142
+ pendingDeltas.push(event.data.deltaContent);
143
+ }
144
+ break;
145
+ case "assistant.message": {
146
+ logger.debug(`assistant.message: toolRequests=${String(event.data.toolRequests?.length ?? 0)}, content=${JSON.stringify(event.data)}`);
147
+ if (event.data.toolRequests && event.data.toolRequests.length > 0) {
148
+ // non-bridge tools (e.g. report_intent) are handled internally by the CLI
149
+ const bridgeRequests = hasBridge
150
+ ? event.data.toolRequests.filter((tr) => tr.name.startsWith(MCP_PREFIX))
151
+ : event.data.toolRequests;
152
+ if (hasBridge && bridgeRequests.length < event.data.toolRequests.length) {
153
+ const skipped = event.data.toolRequests.length - bridgeRequests.length;
154
+ logger.debug(`Skipped ${String(skipped)} non-bridge tool request(s) (handled internally by CLI)`);
155
+ }
156
+ // Xcode needs to see the names it originally sent, and the args
157
+ // need to match the schema because the Copilot model sometimes
158
+ // uses different naming conventions (e.g. "ignoreCase" vs "-i")
159
+ const stripped = bridgeRequests.map((tr) => {
160
+ const resolved = state.resolveToolName(stripMCPPrefix(tr.name));
161
+ return {
162
+ ...tr,
163
+ name: resolved,
164
+ arguments: state.normalizeArgs(resolved, (tr.arguments ?? {})),
165
+ };
166
+ });
167
+ if (stripped.length > 0) {
168
+ // register even without a reply because the model might have retried
169
+ // a tool after an internal failure
170
+ for (const tr of stripped) {
171
+ logger.info(`Tool request: name="${tr.name}", id="${tr.toolCallId}", args=${JSON.stringify(tr.arguments)}`);
172
+ state.registerExpected(tr.toolCallId, tr.name);
173
+ }
174
+ const r = getReply();
175
+ if (r) {
176
+ flushPending();
177
+ emitToolUseBlocks(r, stripped);
178
+ finishReply(r, "tool_use");
179
+ }
180
+ else {
181
+ logger.debug("assistant.message with tool requests but no reply (internal retry), registered expected tools");
182
+ }
183
+ }
184
+ else {
185
+ logger.debug("All tool requests were non-bridge, no tool_use blocks emitted");
186
+ }
187
+ }
188
+ else {
189
+ const r = getReply();
190
+ if (r) {
191
+ logger.debug(`assistant.message with no tool requests, flushing ${String(pendingDeltas.length)} pending deltas`);
192
+ flushPending();
193
+ }
194
+ }
195
+ break;
196
+ }
197
+ case "session.idle": {
198
+ logger.info(`Done, wrapping up stream (pendingDeltas=${String(pendingDeltas.length)})`);
199
+ sessionDone = true;
200
+ state.markSessionInactive();
201
+ flushPending();
202
+ const r = getReply();
203
+ if (r) {
204
+ // spec requires at least one content block
205
+ if (!textBlockStarted) {
206
+ ensureTextBlock(r);
207
+ }
208
+ finishReply(r, "end_turn");
209
+ }
210
+ unsubscribe();
211
+ break;
212
+ }
213
+ case "session.compaction_start":
214
+ logger.info("Compacting context...");
215
+ break;
216
+ case "session.compaction_complete":
217
+ logger.info(`Context compacted: ${formatCompaction(event.data)}`);
218
+ break;
219
+ case "session.error": {
220
+ logger.error(`Session error: ${event.data.message}`);
221
+ sessionDone = true;
222
+ state.markSessionErrored();
223
+ state.markSessionInactive();
224
+ const r = getReply();
225
+ if (r) {
226
+ if (textBlockStarted) {
227
+ sendEvent(r, "content_block_stop", {
228
+ type: "content_block_stop",
229
+ index: 0,
230
+ });
231
+ }
232
+ sendEpilogue(r, "end_turn");
233
+ r.raw.end();
234
+ state.clearReply();
235
+ }
236
+ textBlockStarted = false;
237
+ unsubscribe();
238
+ state.notifyStreamingDone();
239
+ break;
240
+ }
241
+ default:
242
+ logger.debug(`Unhandled event: ${event.type}, data=${JSON.stringify(event.data)}`);
243
+ break;
244
+ }
245
+ });
246
+ reply.raw.on("close", () => {
247
+ if (!sessionDone && state.currentReply === reply) {
248
+ logger.info("Client disconnected, aborting session");
249
+ textBlockStarted = false;
250
+ state.markSessionErrored();
251
+ state.cleanup();
252
+ unsubscribe();
253
+ session.abort().catch((err) => {
254
+ logger.error("Failed to abort session:", err);
255
+ });
256
+ state.notifyStreamingDone();
257
+ }
258
+ });
259
+ const done = state.waitForStreamingDone();
260
+ session.send({ prompt }).catch((err) => {
261
+ logger.error("Failed to send prompt:", err);
262
+ sessionDone = true;
263
+ textBlockStarted = false;
264
+ const r = getReply();
265
+ if (r) {
266
+ r.raw.end();
267
+ state.clearReply();
268
+ }
269
+ unsubscribe();
270
+ state.notifyStreamingDone();
271
+ });
272
+ return done;
273
+ }
274
+ //# sourceMappingURL=streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../../src/handlers/messages/streaming.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,IAAI,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAWjG,MAAM,UAAU,GAAG,eAAe,CAAC;AAEnC,mEAAmE;AACnE,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAmB,EAAE,KAAa;IAC3D,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAEtC,MAAM,YAAY,GAAsB;QACtC,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE;YACP,EAAE,EAAE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE;YAC/B,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,EAAE;YACX,KAAK;YACL,WAAW,EAAE,IAAI;YACjB,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;SAC7C;KACF,CAAC;IACF,SAAS,CAAC,KAAK,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAsB,EACtB,OAAuB,EACvB,MAAc,EACd,KAAa,EACb,MAAc,EACd,SAAS,GAAG,KAAK;IAEjB,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC;IACjC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC5D,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACzB,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAE1B,IAAI,aAAa,GAAa,EAAE,CAAC;IACjC,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,oEAAoE;IACpE,sEAAsE;IACtE,yEAAyE;IACzE,iEAAiE;IACjE,SAAS,QAAQ;QACf,OAAO,KAAK,CAAC,YAAY,CAAC;IAC5B,CAAC;IAED,SAAS,eAAe,CAAC,CAAe;QACtC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,MAAM,UAAU,GAA2B;gBACzC,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,CAAC;gBACR,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;aAC1C,CAAC;YACF,SAAS,CAAC,CAAC,EAAE,qBAAqB,EAAE,UAAU,CAAC,CAAC;YAChD,gBAAgB,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,SAAS,YAAY;QACnB,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC7C,eAAe,CAAC,CAAC,CAAC,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,MAAM,KAAK,GAA2B;gBACpC,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE;aACpC,CAAC;YACF,SAAS,CAAC,CAAC,EAAE,qBAAqB,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;QACD,aAAa,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,SAAS,YAAY,CAAC,CAAe,EAAE,UAAkB;QACvD,MAAM,YAAY,GAAsB;YACtC,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,EAAE;YACvD,KAAK,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE;SAC5B,CAAC;QACF,SAAS,CAAC,CAAC,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC;QAC5C,SAAS,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,cAAc,EAA6B,CAAC,CAAC;IACpF,CAAC;IAED,SAAS,iBAAiB,CACxB,CAAe,EACf,YAA8E;QAE9E,IAAI,UAAkB,CAAC;QACvB,IAAI,gBAAgB,EAAE,CAAC;YACrB,SAAS,CAAC,CAAC,EAAE,oBAAoB,EAAE;gBACjC,IAAI,EAAE,oBAAoB;gBAC1B,KAAK,EAAE,CAAC;aACuB,CAAC,CAAC;YACnC,UAAU,GAAG,CAAC,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,CAAC,CAAC;QACjB,CAAC;QAED,IAAI,KAAK,GAAG,UAAU,CAAC;QACvB,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,SAAS,CAAC,CAAC,EAAE,qBAAqB,EAAE;gBAClC,IAAI,EAAE,qBAAqB;gBAC3B,KAAK;gBACL,aAAa,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;aACjF,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5E,SAAS,CAAC,CAAC,EAAE,qBAAqB,EAAE;gBAClC,IAAI,EAAE,qBAAqB;gBAC3B,KAAK;gBACL,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,YAAY,EAAE,QAAQ,EAAE;aAC5D,CAAC,CAAC;YAEH,SAAS,CAAC,CAAC,EAAE,oBAAoB,EAAE;gBACjC,IAAI,EAAE,oBAAoB;gBAC1B,KAAK;aAC0B,CAAC,CAAC;YAEnC,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IAED,SAAS,WAAW,CAAC,CAAe,EAAE,UAAkB;QACtD,IAAI,UAAU,KAAK,UAAU,IAAI,gBAAgB,EAAE,CAAC;YAClD,SAAS,CAAC,CAAC,EAAE,oBAAoB,EAAE;gBACjC,IAAI,EAAE,oBAAoB;gBAC1B,KAAK,EAAE,CAAC;aACuB,CAAC,CAAC;QACrC,CAAC;QACD,YAAY,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC5B,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC5B,gBAAgB,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE;QACvC,MAAM,CAAC,KAAK,CAAC,kBAAkB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YACrB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,QAAQ,CAAC,CAAC,UAAU,UAAU,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAChG,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YACrB,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC;YACzD,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO;gBACtB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;gBACnC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;YACjC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,kBAAkB,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QAED,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,yBAAyB;gBAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;oBAC5B,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;oBAClD,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAC9C,CAAC;gBACD,MAAM;YAER,KAAK,mBAAmB,CAAC,CAAC,CAAC;gBACzB,MAAM,CAAC,KAAK,CAAC,mCAAmC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAEvI,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClE,0EAA0E;oBAC1E,MAAM,cAAc,GAAG,SAAS;wBAC9B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;wBACxE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;oBAE5B,IAAI,SAAS,IAAI,cAAc,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;wBACxE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC;wBACvE,MAAM,CAAC,KAAK,CAAC,WAAW,MAAM,CAAC,OAAO,CAAC,yDAAyD,CAAC,CAAC;oBACpG,CAAC;oBAED,gEAAgE;oBAChE,+DAA+D;oBAC/D,gEAAgE;oBAChE,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;wBACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;wBAChE,OAAO;4BACL,GAAG,EAAE;4BACL,IAAI,EAAE,QAAQ;4BACd,SAAS,EAAE,KAAK,CAAC,aAAa,CAC5B,QAAQ,EACR,CAAC,EAAE,CAAC,SAAS,IAAI,EAAE,CAA4B,CAChD;yBACF,CAAC;oBACJ,CAAC,CAAC,CAAC;oBAEH,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACxB,qEAAqE;wBACrE,mCAAmC;wBACnC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;4BAC1B,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,UAAU,WAAW,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;4BAC5G,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;wBACjD,CAAC;wBAED,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;wBACrB,IAAI,CAAC,EAAE,CAAC;4BACN,YAAY,EAAE,CAAC;4BACf,iBAAiB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;4BAC/B,WAAW,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;wBAC7B,CAAC;6BAAM,CAAC;4BACN,MAAM,CAAC,KAAK,CAAC,+FAA+F,CAAC,CAAC;wBAChH,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;oBAChF,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;oBACrB,IAAI,CAAC,EAAE,CAAC;wBACN,MAAM,CAAC,KAAK,CAAC,qDAAqD,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;wBACjH,YAAY,EAAE,CAAC;oBACjB,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,CAAC,IAAI,CAAC,2CAA2C,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACxF,WAAW,GAAG,IAAI,CAAC;gBACnB,KAAK,CAAC,mBAAmB,EAAE,CAAC;gBAC5B,YAAY,EAAE,CAAC;gBACf,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;gBACrB,IAAI,CAAC,EAAE,CAAC;oBACN,2CAA2C;oBAC3C,IAAI,CAAC,gBAAgB,EAAE,CAAC;wBACtB,eAAe,CAAC,CAAC,CAAC,CAAC;oBACrB,CAAC;oBACD,WAAW,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBAC7B,CAAC;gBACD,WAAW,EAAE,CAAC;gBACd,MAAM;YACR,CAAC;YAED,KAAK,0BAA0B;gBAC7B,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;gBACrC,MAAM;YAER,KAAK,6BAA6B;gBAChC,MAAM,CAAC,IAAI,CAAC,sBAAsB,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClE,MAAM;YAER,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,CAAC,KAAK,CAAC,kBAAkB,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;gBACrD,WAAW,GAAG,IAAI,CAAC;gBACnB,KAAK,CAAC,kBAAkB,EAAE,CAAC;gBAC3B,KAAK,CAAC,mBAAmB,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;gBACrB,IAAI,CAAC,EAAE,CAAC;oBACN,IAAI,gBAAgB,EAAE,CAAC;wBACrB,SAAS,CAAC,CAAC,EAAE,oBAAoB,EAAE;4BACjC,IAAI,EAAE,oBAAoB;4BAC1B,KAAK,EAAE,CAAC;yBACuB,CAAC,CAAC;oBACrC,CAAC;oBACD,YAAY,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBAC5B,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;oBACZ,KAAK,CAAC,UAAU,EAAE,CAAC;gBACrB,CAAC;gBACD,gBAAgB,GAAG,KAAK,CAAC;gBACzB,WAAW,EAAE,CAAC;gBACd,KAAK,CAAC,mBAAmB,EAAE,CAAC;gBAC5B,MAAM;YACR,CAAC;YAED;gBACE,MAAM,CAAC,KAAK,CAAC,oBAAoB,KAAK,CAAC,IAAI,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACnF,MAAM;QACV,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACzB,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;YACrD,gBAAgB,GAAG,KAAK,CAAC;YACzB,KAAK,CAAC,kBAAkB,EAAE,CAAC;YAC3B,KAAK,CAAC,OAAO,EAAE,CAAC;YAChB,WAAW,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACrC,MAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;YAChD,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,KAAK,CAAC,oBAAoB,EAAE,CAAC;IAE1C,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QAC9C,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;QAC5C,WAAW,GAAG,IAAI,CAAC;QACnB,gBAAgB,GAAG,KAAK,CAAC;QACzB,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;QACrB,IAAI,CAAC,EAAE,CAAC;YACN,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,CAAC;QACD,WAAW,EAAE,CAAC;QACd,KAAK,CAAC,mBAAmB,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AnthropicMessage } from "../../schemas/anthropic.js";
2
+ import type { ToolBridgeState } from "../../tool-bridge/state.js";
3
+ import type { Logger } from "../../logger.js";
4
+ export declare function resolveToolResults(messages: AnthropicMessage[], state: ToolBridgeState, logger: Logger): void;
@@ -0,0 +1,19 @@
1
+ export function resolveToolResults(messages, state, logger) {
2
+ const lastMsg = messages[messages.length - 1];
3
+ if (!lastMsg || lastMsg.role !== "user" || typeof lastMsg.content === "string")
4
+ return;
5
+ for (const block of lastMsg.content) {
6
+ if (block.type === "tool_result") {
7
+ const resultText = typeof block.content === "string"
8
+ ? block.content
9
+ : Array.isArray(block.content)
10
+ ? block.content.map((b) => b.text).join("\n")
11
+ : "";
12
+ logger.debug(`Resolving tool result for ${block.tool_use_id}`);
13
+ if (!state.resolveToolCall(block.tool_use_id, resultText)) {
14
+ logger.warn(`No pending MCP request for tool_use_id ${block.tool_use_id}`);
15
+ }
16
+ }
17
+ }
18
+ }
19
+ //# sourceMappingURL=tool-result-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-result-handler.js","sourceRoot":"","sources":["../../../src/handlers/messages/tool-result-handler.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,kBAAkB,CAChC,QAA4B,EAC5B,KAAsB,EACtB,MAAc;IAEd,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO;IAEvF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;gBAClD,CAAC,CAAC,KAAK,CAAC,OAAO;gBACf,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;oBAC5B,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAC7C,CAAC,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,KAAK,CAAC,6BAA6B,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,CAAC;gBAC1D,MAAM,CAAC,IAAI,CAAC,0CAA0C,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastifyRequest, FastifyReply } from "fastify";
2
+ import type { AppContext } from "../context.js";
3
+ import type { ConversationManager } from "../conversation-manager.js";
4
+ export declare function createMessagesHandler({ service, logger, config, port }: AppContext, manager: ConversationManager): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
@@ -0,0 +1,150 @@
1
+ import { AnthropicMessagesRequestSchema, extractAnthropicSystem, } from "../schemas/anthropic.js";
2
+ import { formatAnthropicPrompt } from "../utils/anthropic-prompt.js";
3
+ import { resolveModel } from "../utils/model-resolver.js";
4
+ import { createSessionConfig } from "./session-config.js";
5
+ import { resolveToolResults } from "./messages/tool-result-handler.js";
6
+ import { handleAnthropicStreaming, startReply } from "./messages/streaming.js";
7
+ import { sendAnthropicError as sendError } from "./errors.js";
8
+ export function createMessagesHandler({ service, logger, config, port }, manager) {
9
+ return async function handleMessages(request, reply) {
10
+ const parseResult = AnthropicMessagesRequestSchema.safeParse(request.body);
11
+ if (!parseResult.success) {
12
+ const firstIssue = parseResult.error.issues[0];
13
+ sendError(reply, 400, "invalid_request_error", firstIssue?.message ?? "Invalid request body");
14
+ return;
15
+ }
16
+ const req = parseResult.data;
17
+ const existingConv = manager.findByContinuation(req.messages);
18
+ if (existingConv) {
19
+ const state = existingConv.state;
20
+ logger.info(`Continuation for conversation ${existingConv.id} (hasPending=${String(state.hasPending)}, sessionActive=${String(state.sessionActive)})`);
21
+ state.setReply(reply);
22
+ startReply(reply, req.model);
23
+ // TODO: the continuation doesn't own the session so we can't abort it
24
+ // here. The original streaming handler will let it run to idle harmlessly
25
+ // because it guards against null replies, but ideally we'd abort it too.
26
+ reply.raw.on("close", () => {
27
+ if (state.currentReply === reply) {
28
+ logger.info("Client disconnected during continuation");
29
+ state.cleanup();
30
+ state.notifyStreamingDone();
31
+ }
32
+ });
33
+ resolveToolResults(req.messages, state, logger);
34
+ await state.waitForStreamingDone();
35
+ existingConv.sentMessageCount = req.messages.length;
36
+ return;
37
+ }
38
+ const { conversation, isReuse } = manager.findForNewRequest();
39
+ const state = conversation.state;
40
+ state.markSessionActive();
41
+ logger.info(isReuse
42
+ ? `Reusing primary conversation ${conversation.id}`
43
+ : `New conversation ${conversation.id}`);
44
+ // SDK doesn't support switching models mid-session (github/copilot-sdk#409)
45
+ if (isReuse && conversation.model && conversation.model !== req.model) {
46
+ logger.warn(`Model mismatch: session uses "${conversation.model}" but request sent "${req.model}" (SDK does not support mid-session model switching)`);
47
+ }
48
+ const tools = req.tools;
49
+ const hasTools = !!tools?.length;
50
+ const hasBridge = hasTools && config.toolBridge;
51
+ if (tools?.length) {
52
+ state.cacheTools(tools);
53
+ }
54
+ let prompt;
55
+ try {
56
+ prompt = formatAnthropicPrompt(req.messages.slice(conversation.sentMessageCount), config.excludedFilePatterns);
57
+ }
58
+ catch (err) {
59
+ sendError(reply, 400, "invalid_request_error", err instanceof Error ? err.message : String(err));
60
+ if (isReuse) {
61
+ state.markSessionInactive();
62
+ }
63
+ else {
64
+ manager.remove(conversation.id);
65
+ }
66
+ return;
67
+ }
68
+ logger.debug(`Prompt (${isReuse ? "incremental" : "full"}): ${String(prompt.length)} chars`);
69
+ if (!isReuse) {
70
+ const systemMessage = extractAnthropicSystem(req.system);
71
+ logger.debug(`System message length: ${String(systemMessage?.length ?? 0)} chars`);
72
+ logger.debug(`Tools in request: ${tools ? String(tools.length) : "0"}`);
73
+ if (tools) {
74
+ logger.debug(`Tool names: ${tools.map((t) => t.name).join(", ")}`);
75
+ }
76
+ let copilotModel = req.model;
77
+ let supportsReasoningEffort = false;
78
+ try {
79
+ const models = await service.listModels();
80
+ const resolved = resolveModel(req.model, models, logger);
81
+ if (!resolved) {
82
+ sendError(reply, 400, "invalid_request_error", `Model "${req.model}" is not available. Available models: ${models.map((m) => m.id).join(", ")}`);
83
+ manager.remove(conversation.id);
84
+ return;
85
+ }
86
+ copilotModel = resolved;
87
+ if (config.reasoningEffort) {
88
+ const modelInfo = models.find((m) => m.id === copilotModel);
89
+ supportsReasoningEffort =
90
+ modelInfo?.capabilities.supports.reasoningEffort ?? false;
91
+ if (!supportsReasoningEffort) {
92
+ logger.debug(`Model "${copilotModel}" does not support reasoning effort, ignoring config`);
93
+ }
94
+ }
95
+ }
96
+ catch (err) {
97
+ logger.warn("Failed to list models, passing model through as-is:", err);
98
+ }
99
+ conversation.model = copilotModel;
100
+ if (hasBridge) {
101
+ logger.info("Tool bridge active (in-process MCP)");
102
+ }
103
+ const sessionConfig = createSessionConfig({
104
+ model: copilotModel,
105
+ systemMessage,
106
+ logger,
107
+ config,
108
+ supportsReasoningEffort,
109
+ cwd: service.cwd,
110
+ hasToolBridge: hasBridge,
111
+ port,
112
+ conversationId: conversation.id,
113
+ });
114
+ try {
115
+ conversation.session = await service.createSession(sessionConfig);
116
+ }
117
+ catch (err) {
118
+ logger.error("Creating session failed:", err);
119
+ sendError(reply, 500, "api_error", "Failed to create session");
120
+ manager.remove(conversation.id);
121
+ return;
122
+ }
123
+ }
124
+ if (!conversation.session) {
125
+ logger.error("Primary conversation has no session, clearing");
126
+ manager.clearPrimary();
127
+ sendError(reply, 500, "api_error", "Session lost, please retry");
128
+ return;
129
+ }
130
+ state.setReply(reply);
131
+ try {
132
+ logger.info(`Streaming response for conversation ${conversation.id}`);
133
+ await handleAnthropicStreaming(state, conversation.session, prompt, req.model, logger, hasBridge);
134
+ conversation.sentMessageCount = req.messages.length;
135
+ if (conversation.isPrimary && state.hadError) {
136
+ manager.clearPrimary();
137
+ }
138
+ }
139
+ catch (err) {
140
+ logger.error("Request failed:", err);
141
+ if (conversation.isPrimary) {
142
+ manager.clearPrimary();
143
+ }
144
+ if (!reply.sent) {
145
+ sendError(reply, 500, "api_error", err instanceof Error ? err.message : "Internal error");
146
+ }
147
+ }
148
+ };
149
+ }
150
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/handlers/messages.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
1
  import type { FastifyRequest, FastifyReply } from "fastify";
2
2
  import type { AppContext } from "../context.js";
3
- /** GET /v1/models */
4
3
  export declare function createModelsHandler({ service, logger }: AppContext): (_request: FastifyRequest, reply: FastifyReply) => Promise<void>;
@@ -1,5 +1,4 @@
1
- import { currentTimestamp } from "../schemas.js";
2
- /** GET /v1/models */
1
+ import { currentTimestamp } from "../schemas/openai.js";
3
2
  export function createModelsHandler({ service, logger }) {
4
3
  return async function handleModels(_request, reply) {
5
4
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/handlers/models.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGjD,qBAAqB;AACrB,MAAM,UAAU,mBAAmB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAc;IACjE,OAAO,KAAK,UAAU,YAAY,CAChC,QAAwB,EACxB,KAAmB;QAEnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;YAE1C,MAAM,QAAQ,GAAmB;gBAC/B,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACvB,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,MAAM,EAAE,OAAO;oBACf,OAAO,EAAE,gBAAgB,EAAE;oBAC3B,QAAQ,EAAE,gBAAgB;iBAC3B,CAAC,CAAC;aACJ,CAAC;YAEF,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;YAC5C,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACrB,KAAK,EAAE;oBACL,OAAO,EAAE,uBAAuB;oBAChC,IAAI,EAAE,WAAW;iBAClB;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/handlers/models.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,MAAM,UAAU,mBAAmB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAc;IACjE,OAAO,KAAK,UAAU,YAAY,CAChC,QAAwB,EACxB,KAAmB;QAEnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;YAE1C,MAAM,QAAQ,GAAG;gBACf,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACvB,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,MAAM,EAAE,OAAgB;oBACxB,OAAO,EAAE,gBAAgB,EAAE;oBAC3B,QAAQ,EAAE,gBAAgB;iBAC3B,CAAC,CAAC;aACqB,CAAC;YAE3B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;YAC5C,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACrB,KAAK,EAAE;oBACL,OAAO,EAAE,uBAAuB;oBAChC,IAAI,EAAE,WAAW;iBAClB;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { SessionConfig } from "@github/copilot-sdk";
2
+ import type { ServerConfig } from "../config.js";
3
+ import type { Logger } from "../logger.js";
4
+ export interface SessionConfigOptions {
5
+ model: string;
6
+ systemMessage?: string | undefined;
7
+ logger: Logger;
8
+ config: ServerConfig;
9
+ supportsReasoningEffort: boolean;
10
+ cwd?: string | undefined;
11
+ hasToolBridge?: boolean;
12
+ port?: number | undefined;
13
+ conversationId?: string | undefined;
14
+ }
15
+ export declare function createSessionConfig({ model, systemMessage, logger, config, supportsReasoningEffort, cwd, hasToolBridge, port, conversationId, }: SessionConfigOptions): SessionConfig;
@@ -0,0 +1,79 @@
1
+ function isApproved(rule, kind) {
2
+ if (typeof rule === "boolean")
3
+ return rule;
4
+ return rule.some((k) => k === kind);
5
+ }
6
+ export function createSessionConfig({ model, systemMessage, logger, config, supportsReasoningEffort, cwd, hasToolBridge, port, conversationId, }) {
7
+ const hasBridge = !!hasToolBridge;
8
+ return {
9
+ model,
10
+ streaming: true,
11
+ infiniteSessions: { enabled: true },
12
+ workingDirectory: cwd ?? process.cwd(),
13
+ ...(systemMessage && {
14
+ systemMessage: {
15
+ mode: "replace",
16
+ content: systemMessage,
17
+ },
18
+ }),
19
+ mcpServers: {
20
+ ...Object.fromEntries(Object.entries(config.mcpServers).map(([name, server]) => [
21
+ name,
22
+ { ...server, tools: ["*"] },
23
+ ])),
24
+ ...(hasBridge && {
25
+ "xcode-bridge": {
26
+ type: "http",
27
+ url: `http://127.0.0.1:${String(port ?? 8080)}/mcp/${conversationId ?? ""}`,
28
+ tools: ["*"],
29
+ },
30
+ }),
31
+ },
32
+ // When the tool bridge is active, don't restrict availableTools so the CLI
33
+ // can expose the bridged tools to the model. The onPreToolUse hook handles
34
+ // permissions instead.
35
+ ...(!hasBridge && config.allowedCliTools.length > 0 && {
36
+ availableTools: config.allowedCliTools,
37
+ }),
38
+ ...(config.reasoningEffort && supportsReasoningEffort && {
39
+ reasoningEffort: config.reasoningEffort,
40
+ }),
41
+ onUserInputRequest: (request) => {
42
+ logger.debug(`User input requested: "${request.question}"`);
43
+ return Promise.resolve({
44
+ answer: "User input is not available. Ask your question in your response instead.",
45
+ wasFreeform: true,
46
+ });
47
+ },
48
+ onPermissionRequest: (request) => {
49
+ const approved = isApproved(config.autoApprovePermissions, request.kind);
50
+ logger.debug(`Permission "${request.kind}": ${approved ? "approved" : "denied"}`);
51
+ return Promise.resolve(approved
52
+ ? { kind: "approved" }
53
+ : { kind: "denied-by-rules" });
54
+ },
55
+ hooks: {
56
+ onPreToolUse: (input) => {
57
+ const toolName = input.toolName;
58
+ if (hasBridge && toolName.startsWith("xcode-bridge-")) {
59
+ logger.debug(`Tool "${toolName}": allowed (bridge)`);
60
+ return Promise.resolve({ permissionDecision: "allow" });
61
+ }
62
+ if (config.allowedCliTools.includes("*") || config.allowedCliTools.includes(toolName)) {
63
+ logger.debug(`Tool "${toolName}": allowed (CLI)`);
64
+ return Promise.resolve({ permissionDecision: "allow" });
65
+ }
66
+ for (const [serverName, server] of Object.entries(config.mcpServers)) {
67
+ const allowlist = server.allowedTools ?? [];
68
+ if (allowlist.includes("*") || allowlist.includes(toolName)) {
69
+ logger.debug(`Tool "${toolName}": allowed (${serverName})`);
70
+ return Promise.resolve({ permissionDecision: "allow" });
71
+ }
72
+ }
73
+ logger.debug(`Tool "${toolName}": denied (not in any allowlist)`);
74
+ return Promise.resolve({ permissionDecision: "deny" });
75
+ },
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=session-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-config.js","sourceRoot":"","sources":["../../src/handlers/session-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { FastifyReply } from "fastify";
2
+ export declare const SSE_HEADERS: {
3
+ readonly "Content-Type": "text/event-stream";
4
+ readonly "Cache-Control": "no-cache";
5
+ readonly Connection: "keep-alive";
6
+ readonly "X-Accel-Buffering": "no";
7
+ };
8
+ export declare function sendSSEEvent(reply: FastifyReply, type: string, data: unknown): void;
9
+ export declare function formatCompaction(data: unknown): string;
@@ -0,0 +1,19 @@
1
+ export const SSE_HEADERS = {
2
+ "Content-Type": "text/event-stream",
3
+ "Cache-Control": "no-cache",
4
+ Connection: "keep-alive",
5
+ "X-Accel-Buffering": "no",
6
+ };
7
+ export function sendSSEEvent(reply, type, data) {
8
+ reply.raw.write(`event: ${type}\ndata: ${JSON.stringify(data)}\n\n`);
9
+ }
10
+ export function formatCompaction(data) {
11
+ if (!data ||
12
+ typeof data !== "object" ||
13
+ !("preCompactionTokens" in data) ||
14
+ !("postCompactionTokens" in data)) {
15
+ return "compaction data unavailable";
16
+ }
17
+ return `${String(data.preCompactionTokens)} to ${String(data.postCompactionTokens)} tokens`;
18
+ }
19
+ //# sourceMappingURL=streaming-utils.js.map