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,77 @@
1
+ import { ToolCache } from "./tool-cache.js";
2
+ import { ToolRouter } from "./tool-router.js";
3
+ import { ReplyTracker } from "./reply-tracker.js";
4
+ import { SessionLifecycle } from "./session-lifecycle.js";
5
+ export class ToolBridgeState {
6
+ toolCache = new ToolCache();
7
+ toolRouter = new ToolRouter();
8
+ replyTracker = new ReplyTracker();
9
+ session = new SessionLifecycle(this.toolRouter);
10
+ cacheTools(tools) {
11
+ this.toolCache.cacheTools(tools);
12
+ }
13
+ getCachedTools() {
14
+ return this.toolCache.getCachedTools();
15
+ }
16
+ resolveToolName(name) {
17
+ return this.toolCache.resolveToolName(name);
18
+ }
19
+ normalizeArgs(toolName, args) {
20
+ return this.toolCache.normalizeArgs(toolName, args);
21
+ }
22
+ hasPendingToolCall(toolCallId) {
23
+ return this.toolRouter.hasPendingToolCall(toolCallId);
24
+ }
25
+ hasExpectedTool(name) {
26
+ return this.toolRouter.hasExpectedTool(name);
27
+ }
28
+ registerExpected(toolCallId, toolName) {
29
+ this.toolRouter.registerExpected(toolCallId, toolName);
30
+ }
31
+ registerMCPRequest(name, resolve, reject) {
32
+ this.toolRouter.registerMCPRequest(name, resolve, reject);
33
+ }
34
+ resolveToolCall(toolCallId, result) {
35
+ return this.toolRouter.resolveToolCall(toolCallId, result);
36
+ }
37
+ get hasPending() {
38
+ return this.toolRouter.hasPending;
39
+ }
40
+ get currentReply() {
41
+ return this.replyTracker.currentReply;
42
+ }
43
+ setReply(reply) {
44
+ this.replyTracker.setReply(reply);
45
+ }
46
+ clearReply() {
47
+ this.replyTracker.clearReply();
48
+ }
49
+ notifyStreamingDone() {
50
+ this.replyTracker.notifyStreamingDone();
51
+ }
52
+ waitForStreamingDone() {
53
+ return this.replyTracker.waitForStreamingDone();
54
+ }
55
+ get sessionActive() {
56
+ return this.session.sessionActive;
57
+ }
58
+ get hadError() {
59
+ return this.session.hadError;
60
+ }
61
+ markSessionActive() {
62
+ this.session.markSessionActive();
63
+ }
64
+ markSessionErrored() {
65
+ this.session.markSessionErrored();
66
+ }
67
+ markSessionInactive() {
68
+ this.session.markSessionInactive();
69
+ }
70
+ onSessionEnd(callback) {
71
+ this.session.onSessionEnd(callback);
72
+ }
73
+ cleanup() {
74
+ this.session.cleanup();
75
+ }
76
+ }
77
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/tool-bridge/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AnthropicToolDefinition } from "../schemas/anthropic.js";
2
+ export declare class ToolCache {
3
+ private cachedTools;
4
+ cacheTools(tools: AnthropicToolDefinition[]): void;
5
+ getCachedTools(): AnthropicToolDefinition[];
6
+ resolveToolName(name: string): string;
7
+ normalizeArgs(toolName: string, args: Record<string, unknown>): Record<string, unknown>;
8
+ private resolveKey;
9
+ private resolveValue;
10
+ }
@@ -0,0 +1,82 @@
1
+ // The Copilot model likes to rename CLI-style flags to camelCase, so we
2
+ // need a lookup for the ones that can't be derived automatically.
3
+ const FLAG_ALIASES = new Map([
4
+ ["ignoreCase", "-i"],
5
+ ["caseInsensitive", "-i"],
6
+ ["lineNumbers", "-n"],
7
+ ["showLineNumbers", "-n"],
8
+ ["afterContext", "-A"],
9
+ ["linesAfter", "-A"],
10
+ ["beforeContext", "-B"],
11
+ ["linesBefore", "-B"],
12
+ ]);
13
+ function camelToSnake(s) {
14
+ return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
15
+ }
16
+ function snakeToCamel(s) {
17
+ return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
18
+ }
19
+ export class ToolCache {
20
+ cachedTools = [];
21
+ cacheTools(tools) {
22
+ this.cachedTools = tools;
23
+ }
24
+ getCachedTools() {
25
+ return this.cachedTools;
26
+ }
27
+ // The model sometimes hallucinates shortened tool names like "XcodeRead"
28
+ // so we resolve them against the cached list to match what Xcode sent.
29
+ resolveToolName(name) {
30
+ if (this.cachedTools.some((t) => t.name === name))
31
+ return name;
32
+ const suffix = `__${name}`;
33
+ const matches = this.cachedTools.filter((t) => t.name.endsWith(suffix));
34
+ if (matches.length === 1 && matches[0])
35
+ return matches[0].name;
36
+ return name;
37
+ }
38
+ // The Copilot model doesn't always respect the exact property names or
39
+ // enum values in tool schemas, e.g. "ignoreCase" instead of "-i",
40
+ // "outputMode" instead of "output_mode", "filesWithMatches" instead of
41
+ // "files_with_matches". We remap these against the actual schema so the
42
+ // downstream executor doesn't reject them with InputValidationError.
43
+ normalizeArgs(toolName, args) {
44
+ const tool = this.cachedTools.find((t) => t.name === toolName);
45
+ const props = tool?.input_schema?.properties;
46
+ if (!props)
47
+ return args;
48
+ const schemaKeys = new Set(Object.keys(props));
49
+ const result = {};
50
+ for (const [key, value] of Object.entries(args)) {
51
+ const resolvedKey = this.resolveKey(key, schemaKeys);
52
+ result[resolvedKey] = this.resolveValue(value, props[resolvedKey]);
53
+ }
54
+ return result;
55
+ }
56
+ resolveKey(key, schemaKeys) {
57
+ if (schemaKeys.has(key))
58
+ return key;
59
+ const snake = camelToSnake(key);
60
+ if (schemaKeys.has(snake))
61
+ return snake;
62
+ const camel = snakeToCamel(key);
63
+ if (schemaKeys.has(camel))
64
+ return camel;
65
+ const alias = FLAG_ALIASES.get(key);
66
+ if (alias && schemaKeys.has(alias))
67
+ return alias;
68
+ return key;
69
+ }
70
+ resolveValue(value, schemaProp) {
71
+ if (typeof value !== "string" || !schemaProp?.enum)
72
+ return value;
73
+ if (schemaProp.enum.includes(value))
74
+ return value;
75
+ // e.g. "filesWithMatches" becomes "files_with_matches"
76
+ const snake = camelToSnake(value);
77
+ if (schemaProp.enum.includes(snake))
78
+ return snake;
79
+ return value;
80
+ }
81
+ }
82
+ //# sourceMappingURL=tool-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-cache.js","sourceRoot":"","sources":["../../src/tool-bridge/tool-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare class ToolRouter {
2
+ private readonly expectedByName;
3
+ private readonly pendingByCallId;
4
+ hasPendingToolCall(toolCallId: string): boolean;
5
+ hasExpectedTool(name: string): boolean;
6
+ registerExpected(toolCallId: string, toolName: string): void;
7
+ registerMCPRequest(name: string, resolve: (result: string) => void, reject: (err: Error) => void): void;
8
+ resolveToolCall(toolCallId: string, result: string): boolean;
9
+ get hasPending(): boolean;
10
+ rejectAll(reason: string): void;
11
+ private addPending;
12
+ }
@@ -0,0 +1,82 @@
1
+ const TOOL_TIMEOUT_MS = 5 * 60 * 1000;
2
+ export class ToolRouter {
3
+ expectedByName = new Map();
4
+ pendingByCallId = new Map();
5
+ hasPendingToolCall(toolCallId) {
6
+ if (this.pendingByCallId.has(toolCallId))
7
+ return true;
8
+ for (const [, queue] of this.expectedByName) {
9
+ if (queue.includes(toolCallId))
10
+ return true;
11
+ }
12
+ return false;
13
+ }
14
+ hasExpectedTool(name) {
15
+ const queue = this.expectedByName.get(name);
16
+ return !!queue && queue.length > 0;
17
+ }
18
+ registerExpected(toolCallId, toolName) {
19
+ const queue = this.expectedByName.get(toolName);
20
+ if (queue) {
21
+ queue.push(toolCallId);
22
+ }
23
+ else {
24
+ this.expectedByName.set(toolName, [toolCallId]);
25
+ }
26
+ }
27
+ registerMCPRequest(name, resolve, reject) {
28
+ const queue = this.expectedByName.get(name);
29
+ if (!queue?.length) {
30
+ reject(new Error(`No expected tool call for "${name}"`));
31
+ return;
32
+ }
33
+ const toolCallId = queue.shift();
34
+ if (queue.length === 0)
35
+ this.expectedByName.delete(name);
36
+ if (!toolCallId)
37
+ return;
38
+ this.addPending(toolCallId, resolve, reject);
39
+ }
40
+ resolveToolCall(toolCallId, result) {
41
+ const pending = this.pendingByCallId.get(toolCallId);
42
+ if (pending) {
43
+ clearTimeout(pending.timeout);
44
+ this.pendingByCallId.delete(toolCallId);
45
+ pending.resolve(result);
46
+ return true;
47
+ }
48
+ // The CLI may resolve a tool without going through the MCP endpoint (e.g. if
49
+ // the tool name wasn't in our tools/list response and the CLI failed it
50
+ // immediately). Clean up the stale expected entry so it doesn't poison
51
+ // future registerMCPRequest calls for the same tool name.
52
+ for (const [name, queue] of this.expectedByName) {
53
+ const idx = queue.indexOf(toolCallId);
54
+ if (idx !== -1) {
55
+ queue.splice(idx, 1);
56
+ if (queue.length === 0)
57
+ this.expectedByName.delete(name);
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+ get hasPending() {
64
+ return this.pendingByCallId.size > 0 || this.expectedByName.size > 0;
65
+ }
66
+ rejectAll(reason) {
67
+ this.expectedByName.clear();
68
+ for (const [, pending] of this.pendingByCallId) {
69
+ clearTimeout(pending.timeout);
70
+ pending.reject(new Error(reason));
71
+ }
72
+ this.pendingByCallId.clear();
73
+ }
74
+ addPending(toolCallId, resolve, reject) {
75
+ const timeout = setTimeout(() => {
76
+ this.pendingByCallId.delete(toolCallId);
77
+ reject(new Error(`Tool call ${toolCallId} timed out`));
78
+ }, TOOL_TIMEOUT_MS);
79
+ this.pendingByCallId.set(toolCallId, { toolCallId, resolve, reject, timeout });
80
+ }
81
+ }
82
+ //# sourceMappingURL=tool-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-router.js","sourceRoot":"","sources":["../../src/tool-bridge/tool-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { AnthropicMessage } from "../schemas/anthropic.js";
2
+ export declare function formatAnthropicPrompt(messages: AnthropicMessage[], excludedFilePatterns: string[]): string;
@@ -0,0 +1,52 @@
1
+ import { filterExcludedFiles } from "./prompt.js";
2
+ function extractToolResultText(content) {
3
+ if (content == null)
4
+ return "";
5
+ if (typeof content === "string")
6
+ return content;
7
+ return content.map((b) => b.text).join("");
8
+ }
9
+ function formatBlocks(blocks, role, excludedFilePatterns, parts) {
10
+ for (const block of blocks) {
11
+ switch (block.type) {
12
+ case "text":
13
+ if (!block.text)
14
+ break;
15
+ if (role === "user") {
16
+ parts.push(`[User]: ${filterExcludedFiles(block.text, excludedFilePatterns)}`);
17
+ }
18
+ else {
19
+ parts.push(`[Assistant]: ${block.text}`);
20
+ }
21
+ break;
22
+ case "tool_use":
23
+ parts.push(`[Assistant called tool ${block.name} with args: ${JSON.stringify(block.input)}]`);
24
+ break;
25
+ case "tool_result": {
26
+ const text = extractToolResultText(block.content);
27
+ parts.push(`[Tool result for ${block.tool_use_id}]: ${text}`);
28
+ break;
29
+ }
30
+ }
31
+ }
32
+ }
33
+ // The Copilot SDK expects a single flat prompt string, so we convert the
34
+ // structured Anthropic messages into that format.
35
+ export function formatAnthropicPrompt(messages, excludedFilePatterns) {
36
+ const parts = [];
37
+ for (const msg of messages) {
38
+ if (typeof msg.content === "string") {
39
+ if (msg.role === "user") {
40
+ parts.push(`[User]: ${filterExcludedFiles(msg.content, excludedFilePatterns)}`);
41
+ }
42
+ else {
43
+ parts.push(`[Assistant]: ${msg.content}`);
44
+ }
45
+ }
46
+ else {
47
+ formatBlocks(msg.content, msg.role, excludedFilePatterns, parts);
48
+ }
49
+ }
50
+ return parts.join("\n\n");
51
+ }
52
+ //# sourceMappingURL=anthropic-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic-prompt.js","sourceRoot":"","sources":["../../src/utils/anthropic-prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,SAAS,qBAAqB,CAC5B,OAA8D;IAE9D,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC/B,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,YAAY,CACnB,MAAsB,EACtB,IAA0B,EAC1B,oBAA8B,EAC9B,KAAe;IAEf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,MAAM;gBACT,IAAI,CAAC,KAAK,CAAC,IAAI;oBAAE,MAAM;gBACvB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,KAAK,CAAC,IAAI,CACR,WAAW,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,oBAAoB,CAAC,EAAE,CACnE,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,IAAI,CAAC,gBAAgB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC3C,CAAC;gBACD,MAAM;YAER,KAAK,UAAU;gBACb,KAAK,CAAC,IAAI,CACR,0BAA0B,KAAK,CAAC,IAAI,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAClF,CAAC;gBACF,MAAM;YAER,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,IAAI,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAClD,KAAK,CAAC,IAAI,CAAC,oBAAoB,KAAK,CAAC,WAAW,MAAM,IAAI,EAAE,CAAC,CAAC;gBAC9D,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,kDAAkD;AAClD,MAAM,UAAU,qBAAqB,CACnC,QAA4B,EAC5B,oBAA8B;IAE9B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACxB,KAAK,CAAC,IAAI,CACR,WAAW,mBAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAE,CACpE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ModelInfo } from "@github/copilot-sdk";
2
+ import type { Logger } from "../logger.js";
3
+ export declare function resolveModel(requestedModel: string, availableModels: ModelInfo[], logger?: Logger): string | undefined;
@@ -0,0 +1,45 @@
1
+ function normalize(id) {
2
+ return id.replace(/-\d{8}$/, "").replace(/\./g, "-");
3
+ }
4
+ // We need to group models by family for fallback matching, so we grab the
5
+ // prefix before the first digit (e.g. "claude-sonnet-4-5" becomes "claude-sonnet-").
6
+ // This works for Claude naming but would lump all GPT models into "gpt-"
7
+ // so it'll need reworking once we support non-Claude model families here
8
+ // e.g. if we add Codex support.
9
+ function extractFamily(id) {
10
+ const match = id.match(/^(.*?-)\d/);
11
+ return match?.[1] ?? id;
12
+ }
13
+ export function resolveModel(requestedModel, availableModels, logger) {
14
+ if (availableModels.some((m) => m.id === requestedModel)) {
15
+ return requestedModel;
16
+ }
17
+ const normalizedRequest = normalize(requestedModel);
18
+ const normalizedMatch = availableModels.find((m) => normalize(m.id) === normalizedRequest);
19
+ if (normalizedMatch) {
20
+ logger?.debug(`Model "${requestedModel}" resolved to "${normalizedMatch.id}" (normalized match)`);
21
+ return normalizedMatch.id;
22
+ }
23
+ // Requested version may not exist in Copilot yet (e.g. opus 4.6 falls back to opus 4.5),
24
+ // so fall back to the closest model in the same family.
25
+ const requestFamily = extractFamily(normalizedRequest);
26
+ const familyMatches = availableModels.filter((m) => extractFamily(normalize(m.id)) === requestFamily);
27
+ let best;
28
+ let bestLen = 0;
29
+ for (const m of familyMatches) {
30
+ const norm = normalize(m.id);
31
+ let len = 0;
32
+ const minLen = Math.min(normalizedRequest.length, norm.length);
33
+ while (len < minLen && normalizedRequest[len] === norm[len])
34
+ len++;
35
+ if (len > bestLen) {
36
+ bestLen = len;
37
+ best = m;
38
+ }
39
+ }
40
+ if (!best)
41
+ return undefined;
42
+ logger?.warn(`Model "${requestedModel}" not available, falling back to "${best.id}" (closest in family)`);
43
+ return best.id;
44
+ }
45
+ //# sourceMappingURL=model-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/utils/model-resolver.ts"],"names":[],"mappings":"AAGA,SAAS,SAAS,CAAC,EAAU;IAC3B,OAAO,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,0EAA0E;AAC1E,qFAAqF;AACrF,yEAAyE;AACzE,yEAAyE;AACzE,gCAAgC;AAChC,SAAS,aAAa,CAAC,EAAU;IAC/B,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACpC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,cAAsB,EACtB,eAA4B,EAC5B,MAAe;IAEf,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,cAAc,CAAC,EAAE,CAAC;QACzD,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,iBAAiB,GAAG,SAAS,CAAC,cAAc,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAC1C,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,iBAAiB,CAC7C,CAAC;IACF,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,EAAE,KAAK,CACX,UAAU,cAAc,kBAAkB,eAAe,CAAC,EAAE,sBAAsB,CACnF,CAAC;QACF,OAAO,eAAe,CAAC,EAAE,CAAC;IAC5B,CAAC;IAED,yFAAyF;IACzF,wDAAwD;IACxD,MAAM,aAAa,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,eAAe,CAAC,MAAM,CAC1C,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,aAAa,CACxD,CAAC;IAEF,IAAI,IAA2B,CAAC;IAChC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7B,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/D,OAAO,GAAG,GAAG,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC;YAAE,GAAG,EAAE,CAAC;QACnE,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC;YAClB,OAAO,GAAG,GAAG,CAAC;YACd,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,EAAE,IAAI,CACV,UAAU,cAAc,qCAAqC,IAAI,CAAC,EAAE,uBAAuB,CAC5F,CAAC;IACF,OAAO,IAAI,CAAC,EAAE,CAAC;AACjB,CAAC"}
@@ -1,15 +1,3 @@
1
- import type { Message } from "../types.js";
2
- /**
3
- * Strips fenced code blocks whose header contains any of the given patterns
4
- * (case-insensitive). Xcode formats search results as fenced blocks with a
5
- * header like ` ```swift:/path/to/File.swift `, so the patterns are matched
6
- * against the file path in that header.
7
- *
8
- * Xcode's search results can include full file contents for every match, so
9
- * some files can be thousands of lines and add nothing useful to the prompt.
10
- * For example, a mock data file might match the search query but its contents
11
- * aren't helpful for generating a useful response.
12
- */
1
+ import { type ChatCompletionMessage } from "../schemas/openai.js";
13
2
  export declare function filterExcludedFiles(s: string, patterns: string[]): string;
14
- /** System/developer messages are skipped they're passed via `SessionConfig.systemMessage`. */
15
- export declare function formatPrompt(messages: Message[], excludedFilePatterns: string[]): string;
3
+ export declare function formatPrompt(messages: ChatCompletionMessage[], excludedFilePatterns: string[]): string;
@@ -1,18 +1,11 @@
1
- import { extractContentText } from "../schemas.js";
1
+ import { extractContentText } from "../schemas/openai.js";
2
2
  function escapeRegex(s) {
3
3
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
4
  }
5
- /**
6
- * Strips fenced code blocks whose header contains any of the given patterns
7
- * (case-insensitive). Xcode formats search results as fenced blocks with a
8
- * header like ` ```swift:/path/to/File.swift `, so the patterns are matched
9
- * against the file path in that header.
10
- *
11
- * Xcode's search results can include full file contents for every match, so
12
- * some files can be thousands of lines and add nothing useful to the prompt.
13
- * For example, a mock data file might match the search query but its contents
14
- * aren't helpful for generating a useful response.
15
- */
5
+ // Xcode's search results can include full file contents for every match, so
6
+ // some files end up being thousands of lines and add nothing useful. We strip
7
+ // fenced code blocks whose header matches the excluded patterns (Xcode formats
8
+ // them as ```swift:/path/to/File.swift).
16
9
  export function filterExcludedFiles(s, patterns) {
17
10
  if (patterns.length === 0)
18
11
  return s;
@@ -20,7 +13,8 @@ export function filterExcludedFiles(s, patterns) {
20
13
  const re = new RegExp("```\\w*:[^\\n]*(?:" + joined + ")[^\\n]*\\n.*?\\n```\\n?", "gis");
21
14
  return s.replace(re, "");
22
15
  }
23
- /** System/developer messages are skipped they're passed via `SessionConfig.systemMessage`. */
16
+ // System/developer messages are skipped because they're passed separately via
17
+ // SessionConfig.systemMessage.
24
18
  export function formatPrompt(messages, excludedFilePatterns) {
25
19
  const parts = [];
26
20
  for (const msg of messages) {
@@ -28,7 +22,6 @@ export function formatPrompt(messages, excludedFilePatterns) {
28
22
  switch (msg.role) {
29
23
  case "system":
30
24
  case "developer":
31
- // Handled via SessionConfig.systemMessage
32
25
  continue;
33
26
  case "user":
34
27
  parts.push(`[User]: ${filterExcludedFiles(content, excludedFilePatterns)}`);
@@ -46,6 +39,10 @@ export function formatPrompt(messages, excludedFilePatterns) {
46
39
  case "tool":
47
40
  parts.push(`[Tool result for ${msg.tool_call_id ?? "unknown"}]: ${content}`);
48
41
  break;
42
+ case undefined:
43
+ break;
44
+ default:
45
+ throw msg.role;
49
46
  }
50
47
  }
51
48
  return parts.join("\n\n");
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/utils/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAS,EAAE,QAAkB;IAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEpC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,IAAI,MAAM,CACnB,oBAAoB,GAAG,MAAM,GAAG,0BAA0B,EAC1D,KAAK,CACN,CAAC;IACF,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,YAAY,CAC1B,QAAmB,EACnB,oBAA8B;IAE9B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEhD,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,QAAQ,CAAC;YACd,KAAK,WAAW;gBACd,0CAA0C;gBAC1C,SAAS;YAEX,KAAK,MAAM;gBACT,KAAK,CAAC,IAAI,CAAC,WAAW,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAE,CAAC,CAAC;gBAC5E,MAAM;YAER,KAAK,WAAW;gBACd,IAAI,OAAO,EAAE,CAAC;oBACZ,KAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC;gBACxC,CAAC;gBACD,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;oBACnB,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;wBAChC,KAAK,CAAC,IAAI,CACR,0BAA0B,EAAE,CAAC,QAAQ,CAAC,IAAI,eAAe,EAAE,CAAC,QAAQ,CAAC,SAAS,GAAG,CAClF,CAAC;oBACJ,CAAC;gBACH,CAAC;gBACD,MAAM;YAER,KAAK,MAAM;gBACT,KAAK,CAAC,IAAI,CAAC,oBAAoB,GAAG,CAAC,YAAY,IAAI,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;gBAC7E,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/utils/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA8B,MAAM,sBAAsB,CAAC;AAEtF,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,+EAA+E;AAC/E,yCAAyC;AACzC,MAAM,UAAU,mBAAmB,CAAC,CAAS,EAAE,QAAkB;IAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEpC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,IAAI,MAAM,CACnB,oBAAoB,GAAG,MAAM,GAAG,0BAA0B,EAC1D,KAAK,CACN,CAAC;IACF,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,MAAM,UAAU,YAAY,CAC1B,QAAiC,EACjC,oBAA8B;IAE9B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEhD,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,QAAQ,CAAC;YACd,KAAK,WAAW;gBACd,SAAS;YAEX,KAAK,MAAM;gBACT,KAAK,CAAC,IAAI,CAAC,WAAW,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAE,CAAC,CAAC;gBAC5E,MAAM;YAER,KAAK,WAAW;gBACd,IAAI,OAAO,EAAE,CAAC;oBACZ,KAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC;gBACxC,CAAC;gBACD,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;oBACnB,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;wBAChC,KAAK,CAAC,IAAI,CACR,0BAA0B,EAAE,CAAC,QAAQ,CAAC,IAAI,eAAe,EAAE,CAAC,QAAQ,CAAC,SAAS,GAAG,CAClF,CAAC;oBACJ,CAAC;gBACH,CAAC;gBACD,MAAM;YAER,KAAK,MAAM;gBACT,KAAK,CAAC,IAAI,CAAC,oBAAoB,GAAG,CAAC,YAAY,IAAI,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;gBAC7E,MAAM;YAER,KAAK,SAAS;gBACZ,MAAM;YAER;gBACE,MAAM,GAAG,CAAC,IAAoB,CAAC;QACnC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xcode-copilot-server",
3
- "version": "1.0.4",
3
+ "version": "2.0.1",
4
4
  "description": "OpenAI-compatible proxy API server for Xcode, powered by GitHub Copilot",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  "test": "vitest run",
27
27
  "test:watch": "vitest",
28
28
  "lint": "eslint .",
29
- "typecheck": "tsc --noEmit",
29
+ "typecheck": "tsc -p tsconfig.check.json",
30
30
  "prepublishOnly": "npm run build"
31
31
  },
32
32
  "dependencies": {
@@ -34,15 +34,16 @@
34
34
  "@github/copilot-sdk": "0.1.23",
35
35
  "fastify": "5.7.4",
36
36
  "json5": "2.2.3",
37
+ "tokenx": "1.3.0",
37
38
  "zod": "4.3.6"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@eslint/js": "9.39.2",
41
- "@types/node": "25.2.1",
42
+ "@types/node": "25.2.3",
42
43
  "eslint": "9.39.2",
43
44
  "tsx": "4.21.0",
44
45
  "typescript": "5.9.3",
45
- "typescript-eslint": "8.54.0",
46
+ "typescript-eslint": "8.55.0",
46
47
  "vitest": "4.0.18"
47
48
  }
48
49
  }