agent-framework-js 0.2.0 → 0.3.0

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 (68) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/agents/index.cjs +7 -7
  3. package/dist/agents/index.d.cts +6 -6
  4. package/dist/agents/index.d.ts +6 -6
  5. package/dist/agents/index.js +2 -2
  6. package/dist/{chunk-245NZAGV.cjs → chunk-2WBJEXNY.cjs} +4 -4
  7. package/dist/{chunk-245NZAGV.cjs.map → chunk-2WBJEXNY.cjs.map} +1 -1
  8. package/dist/{chunk-S5776DOL.js → chunk-55NB43FN.js} +3 -3
  9. package/dist/{chunk-S5776DOL.js.map → chunk-55NB43FN.js.map} +1 -1
  10. package/dist/{chunk-NURRGYIU.cjs → chunk-FDCTSJMB.cjs} +13 -11
  11. package/dist/chunk-FDCTSJMB.cjs.map +1 -0
  12. package/dist/{chunk-ILBKDEEL.cjs → chunk-I55OVD23.cjs} +2 -2
  13. package/dist/chunk-I55OVD23.cjs.map +1 -0
  14. package/dist/{chunk-UIXNKPLQ.cjs → chunk-IHMPSELC.cjs} +4 -4
  15. package/dist/{chunk-UIXNKPLQ.cjs.map → chunk-IHMPSELC.cjs.map} +1 -1
  16. package/dist/{chunk-WEKU7735.js → chunk-KOPGBIES.js} +6 -4
  17. package/dist/chunk-KOPGBIES.js.map +1 -0
  18. package/dist/{chunk-E4VTVUYU.js → chunk-LC54DGGR.js} +2 -2
  19. package/dist/chunk-LC54DGGR.js.map +1 -0
  20. package/dist/{chunk-IGIFX6QO.js → chunk-PYIZ4PT3.js} +3 -3
  21. package/dist/{chunk-IGIFX6QO.js.map → chunk-PYIZ4PT3.js.map} +1 -1
  22. package/dist/{chunk-7M3EAGCA.js → chunk-QD2FFISV.js} +118 -20
  23. package/dist/chunk-QD2FFISV.js.map +1 -0
  24. package/dist/{chunk-U64OEHG6.cjs → chunk-XMDGLQFL.cjs} +118 -20
  25. package/dist/chunk-XMDGLQFL.cjs.map +1 -0
  26. package/dist/declarative/index.cjs +4 -4
  27. package/dist/declarative/index.d.cts +6 -6
  28. package/dist/declarative/index.d.ts +6 -6
  29. package/dist/declarative/index.js +3 -3
  30. package/dist/{index-Dog-CyOK.d.ts → index-C22fqyZQ.d.ts} +5 -5
  31. package/dist/{index-5eIhfrC1.d.cts → index-b1oTo3Lv.d.cts} +5 -5
  32. package/dist/index.cjs +31 -31
  33. package/dist/index.d.cts +6 -6
  34. package/dist/index.d.ts +6 -6
  35. package/dist/index.js +6 -6
  36. package/dist/mcp/index.d.cts +2 -2
  37. package/dist/mcp/index.d.ts +2 -2
  38. package/dist/middleware/index.d.cts +7 -7
  39. package/dist/middleware/index.d.ts +7 -7
  40. package/dist/persistence/index.cjs +5 -5
  41. package/dist/persistence/index.d.cts +3 -3
  42. package/dist/persistence/index.d.ts +3 -3
  43. package/dist/persistence/index.js +2 -2
  44. package/dist/{provider-IJnfNhCX.d.cts → provider-B807EuDV.d.cts} +6 -1
  45. package/dist/{provider-C_rgZvmX.d.ts → provider-CvU3I-Xo.d.ts} +6 -1
  46. package/dist/providers/index.cjs +6 -6
  47. package/dist/providers/index.d.cts +29 -4
  48. package/dist/providers/index.d.ts +29 -4
  49. package/dist/providers/index.js +1 -1
  50. package/dist/{registry-B-hicOxp.d.cts → registry-BCkSIe0E.d.cts} +2 -2
  51. package/dist/{registry-KMWN0p4Z.d.ts → registry-D-CmT0gk.d.ts} +2 -2
  52. package/dist/{thread-D3zaGK1Y.d.cts → thread-BzwE1OnJ.d.cts} +2 -2
  53. package/dist/{thread-COV135Ja.d.ts → thread-COljUAtD.d.ts} +2 -2
  54. package/dist/{tool-BZg_znMZ.d.cts → tool-D9Uodu9Y.d.cts} +1 -1
  55. package/dist/{tool-CSCC87OD.d.ts → tool-LPMc4QQd.d.ts} +1 -1
  56. package/dist/tools/index.d.cts +4 -4
  57. package/dist/tools/index.d.ts +4 -4
  58. package/dist/{types-Cn1g9Tg4.d.cts → types-AlvjoTyS.d.cts} +24 -1
  59. package/dist/{types-Cn1g9Tg4.d.ts → types-AlvjoTyS.d.ts} +24 -1
  60. package/dist/workflows/index.d.cts +6 -6
  61. package/dist/workflows/index.d.ts +6 -6
  62. package/package.json +1 -1
  63. package/dist/chunk-7M3EAGCA.js.map +0 -1
  64. package/dist/chunk-E4VTVUYU.js.map +0 -1
  65. package/dist/chunk-ILBKDEEL.cjs.map +0 -1
  66. package/dist/chunk-NURRGYIU.cjs.map +0 -1
  67. package/dist/chunk-U64OEHG6.cjs.map +0 -1
  68. package/dist/chunk-WEKU7735.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/persistence/store.ts","../src/persistence/memory.ts","../src/persistence/browser.ts"],"names":[],"mappings":"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","file":"chunk-IGIFX6QO.js","sourcesContent":["/**\n * Pluggable storage abstraction for persisting conversation/thread state in a\n * no-backend environment. No database server is assumed. (FR-024)\n *\n * @packageDocumentation\n */\n\nimport type { Thread } from \"../agents/thread.js\";\nimport { Thread as ThreadClass } from \"../agents/thread.js\";\n\n/** A minimal key/value store. */\nexport interface Store {\n\tget(key: string): Promise<unknown | undefined>;\n\tset(key: string, value: unknown): Promise<void>;\n\tdelete(key: string): Promise<void>;\n}\n\n/** Save and load threads via a {@link Store}. */\nexport const ThreadPersistence = {\n\t/** Persist a thread under the key `thread:<id>`. */\n\tasync save(store: Store, thread: Thread): Promise<void> {\n\t\tawait store.set(`thread:${thread.id}`, thread.toJSON());\n\t},\n\t/** Load and rehydrate a thread, or undefined if absent. */\n\tasync load(store: Store, id: string): Promise<Thread | undefined> {\n\t\tconst data = (await store.get(`thread:${id}`)) as\n\t\t\t| { id: string; messages: never[]; compacted?: boolean }\n\t\t\t| undefined;\n\t\treturn data ? ThreadClass.fromJSON(data) : undefined;\n\t},\n};\n","/**\n * In-memory store adapter. Works in every runtime; ideal as a default and for tests.\n * @packageDocumentation\n */\n\nimport type { Store } from \"./store.js\";\n\n/** Create an in-memory {@link Store}. */\nexport function createMemoryStore(): Store {\n\tconst map = new Map<string, unknown>();\n\treturn {\n\t\tasync get(key) {\n\t\t\treturn map.get(key);\n\t\t},\n\t\tasync set(key, value) {\n\t\t\tmap.set(key, value);\n\t\t},\n\t\tasync delete(key) {\n\t\t\tmap.delete(key);\n\t\t},\n\t};\n}\n","/**\n * Browser store adapters backed by `localStorage` or `IndexedDB`. Capability-gated\n * so a clear typed error is thrown if used where unavailable. (FR-024, FR-030a)\n *\n * @packageDocumentation\n */\n\nimport type { Store } from \"./store.js\";\nimport { requireCapability } from \"../core/runtime.js\";\n\n/** Options for {@link createBrowserStore}. */\nexport interface BrowserStoreOptions {\n\t/** Storage backend. Default \"local\". */\n\tbackend?: \"local\" | \"indexeddb\";\n\t/** Key namespace/prefix. Default \"afjs\". */\n\tnamespace?: string;\n}\n\n/** Create a browser-backed {@link Store}. */\nexport function createBrowserStore(options: BrowserStoreOptions = {}): Store {\n\tconst backend = options.backend ?? \"local\";\n\tconst ns = options.namespace ?? \"afjs\";\n\treturn backend === \"indexeddb\" ? indexedDbStore(ns) : localStorageStore(ns);\n}\n\nfunction localStorageStore(ns: string): Store {\n\trequireCapability(\"hasLocalStorage\", \"localStorage store\");\n\tconst ls = (globalThis as unknown as { localStorage: Storage }).localStorage;\n\tconst k = (key: string) => `${ns}:${key}`;\n\treturn {\n\t\tasync get(key) {\n\t\t\tconst raw = ls.getItem(k(key));\n\t\t\treturn raw == null ? undefined : JSON.parse(raw);\n\t\t},\n\t\tasync set(key, value) {\n\t\t\tls.setItem(k(key), JSON.stringify(value));\n\t\t},\n\t\tasync delete(key) {\n\t\t\tls.removeItem(k(key));\n\t\t},\n\t};\n}\n\nfunction indexedDbStore(ns: string): Store {\n\trequireCapability(\"hasIndexedDB\", \"IndexedDB store\");\n\tconst idb = (globalThis as unknown as { indexedDB: IDBFactory }).indexedDB;\n\n\tfunction open(): Promise<IDBDatabase> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst req = idb.open(ns, 1);\n\t\t\treq.onupgradeneeded = () => req.result.createObjectStore(\"kv\");\n\t\t\treq.onsuccess = () => resolve(req.result);\n\t\t\treq.onerror = () => reject(req.error);\n\t\t});\n\t}\n\n\tasync function tx<T>(mode: IDBTransactionMode, fn: (s: IDBObjectStore) => IDBRequest): Promise<T> {\n\t\tconst db = await open();\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst store = db.transaction(\"kv\", mode).objectStore(\"kv\");\n\t\t\tconst req = fn(store);\n\t\t\treq.onsuccess = () => resolve(req.result as T);\n\t\t\treq.onerror = () => reject(req.error);\n\t\t});\n\t}\n\n\treturn {\n\t\tasync get(key) {\n\t\t\treturn tx<unknown>(\"readonly\", (s) => s.get(key));\n\t\t},\n\t\tasync set(key, value) {\n\t\t\tawait tx(\"readwrite\", (s) => s.put(value, key));\n\t\t},\n\t\tasync delete(key) {\n\t\t\tawait tx(\"readwrite\", (s) => s.delete(key));\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"sources":["../src/persistence/store.ts","../src/persistence/memory.ts","../src/persistence/browser.ts"],"names":[],"mappings":"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","file":"chunk-PYIZ4PT3.js","sourcesContent":["/**\n * Pluggable storage abstraction for persisting conversation/thread state in a\n * no-backend environment. No database server is assumed. (FR-024)\n *\n * @packageDocumentation\n */\n\nimport type { Thread } from \"../agents/thread.js\";\nimport { Thread as ThreadClass } from \"../agents/thread.js\";\n\n/** A minimal key/value store. */\nexport interface Store {\n\tget(key: string): Promise<unknown | undefined>;\n\tset(key: string, value: unknown): Promise<void>;\n\tdelete(key: string): Promise<void>;\n}\n\n/** Save and load threads via a {@link Store}. */\nexport const ThreadPersistence = {\n\t/** Persist a thread under the key `thread:<id>`. */\n\tasync save(store: Store, thread: Thread): Promise<void> {\n\t\tawait store.set(`thread:${thread.id}`, thread.toJSON());\n\t},\n\t/** Load and rehydrate a thread, or undefined if absent. */\n\tasync load(store: Store, id: string): Promise<Thread | undefined> {\n\t\tconst data = (await store.get(`thread:${id}`)) as\n\t\t\t| { id: string; messages: never[]; compacted?: boolean }\n\t\t\t| undefined;\n\t\treturn data ? ThreadClass.fromJSON(data) : undefined;\n\t},\n};\n","/**\n * In-memory store adapter. Works in every runtime; ideal as a default and for tests.\n * @packageDocumentation\n */\n\nimport type { Store } from \"./store.js\";\n\n/** Create an in-memory {@link Store}. */\nexport function createMemoryStore(): Store {\n\tconst map = new Map<string, unknown>();\n\treturn {\n\t\tasync get(key) {\n\t\t\treturn map.get(key);\n\t\t},\n\t\tasync set(key, value) {\n\t\t\tmap.set(key, value);\n\t\t},\n\t\tasync delete(key) {\n\t\t\tmap.delete(key);\n\t\t},\n\t};\n}\n","/**\n * Browser store adapters backed by `localStorage` or `IndexedDB`. Capability-gated\n * so a clear typed error is thrown if used where unavailable. (FR-024, FR-030a)\n *\n * @packageDocumentation\n */\n\nimport type { Store } from \"./store.js\";\nimport { requireCapability } from \"../core/runtime.js\";\n\n/** Options for {@link createBrowserStore}. */\nexport interface BrowserStoreOptions {\n\t/** Storage backend. Default \"local\". */\n\tbackend?: \"local\" | \"indexeddb\";\n\t/** Key namespace/prefix. Default \"afjs\". */\n\tnamespace?: string;\n}\n\n/** Create a browser-backed {@link Store}. */\nexport function createBrowserStore(options: BrowserStoreOptions = {}): Store {\n\tconst backend = options.backend ?? \"local\";\n\tconst ns = options.namespace ?? \"afjs\";\n\treturn backend === \"indexeddb\" ? indexedDbStore(ns) : localStorageStore(ns);\n}\n\nfunction localStorageStore(ns: string): Store {\n\trequireCapability(\"hasLocalStorage\", \"localStorage store\");\n\tconst ls = (globalThis as unknown as { localStorage: Storage }).localStorage;\n\tconst k = (key: string) => `${ns}:${key}`;\n\treturn {\n\t\tasync get(key) {\n\t\t\tconst raw = ls.getItem(k(key));\n\t\t\treturn raw == null ? undefined : JSON.parse(raw);\n\t\t},\n\t\tasync set(key, value) {\n\t\t\tls.setItem(k(key), JSON.stringify(value));\n\t\t},\n\t\tasync delete(key) {\n\t\t\tls.removeItem(k(key));\n\t\t},\n\t};\n}\n\nfunction indexedDbStore(ns: string): Store {\n\trequireCapability(\"hasIndexedDB\", \"IndexedDB store\");\n\tconst idb = (globalThis as unknown as { indexedDB: IDBFactory }).indexedDB;\n\n\tfunction open(): Promise<IDBDatabase> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst req = idb.open(ns, 1);\n\t\t\treq.onupgradeneeded = () => req.result.createObjectStore(\"kv\");\n\t\t\treq.onsuccess = () => resolve(req.result);\n\t\t\treq.onerror = () => reject(req.error);\n\t\t});\n\t}\n\n\tasync function tx<T>(mode: IDBTransactionMode, fn: (s: IDBObjectStore) => IDBRequest): Promise<T> {\n\t\tconst db = await open();\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst store = db.transaction(\"kv\", mode).objectStore(\"kv\");\n\t\t\tconst req = fn(store);\n\t\t\treq.onsuccess = () => resolve(req.result as T);\n\t\t\treq.onerror = () => reject(req.error);\n\t\t});\n\t}\n\n\treturn {\n\t\tasync get(key) {\n\t\t\treturn tx<unknown>(\"readonly\", (s) => s.get(key));\n\t\t},\n\t\tasync set(key, value) {\n\t\t\tawait tx(\"readwrite\", (s) => s.put(value, key));\n\t\t},\n\t\tasync delete(key) {\n\t\t\tawait tx(\"readwrite\", (s) => s.delete(key));\n\t\t},\n\t};\n}\n"]}
@@ -60,6 +60,10 @@ function providerErrorFromStatus(status, message) {
60
60
  }
61
61
 
62
62
  // src/providers/openai-compatible.ts
63
+ var UNSAFE_TOOL_NAME_CHARS = /[^a-zA-Z0-9_-]/g;
64
+ function sanitizeToolName(name) {
65
+ return name.replace(UNSAFE_TOOL_NAME_CHARS, "_");
66
+ }
63
67
  function toOpenAIContent(parts) {
64
68
  if (parts.every((p) => p.type === "text")) {
65
69
  return parts.map((p) => p.text).join("");
@@ -68,54 +72,82 @@ function toOpenAIContent(parts) {
68
72
  (p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image_url", image_url: { url: p.data } }
69
73
  );
70
74
  }
75
+ function toWireToolCalls(toolCalls) {
76
+ return toolCalls.map((tc) => ({
77
+ id: tc.id,
78
+ type: "function",
79
+ function: {
80
+ name: sanitizeToolName(tc.name),
81
+ arguments: typeof tc.arguments === "string" ? tc.arguments : JSON.stringify(tc.arguments ?? {})
82
+ }
83
+ }));
84
+ }
71
85
  function toOpenAIMessages(messages) {
72
86
  return messages.map((m) => {
73
87
  const msg = { role: m.role, content: toOpenAIContent(m.parts) };
88
+ if (m.toolCalls && m.toolCalls.length > 0) {
89
+ msg.tool_calls = toWireToolCalls(m.toolCalls);
90
+ const hasText = m.parts.some((p) => p.type === "text" && p.text.length > 0);
91
+ if (!hasText) msg.content = null;
92
+ }
74
93
  if (m.toolCallId) msg.tool_call_id = m.toolCallId;
75
- if (m.name) msg.name = m.name;
94
+ if (m.name) msg.name = sanitizeToolName(m.name);
95
+ if (m.reasoningOpaque) msg.reasoning_opaque = m.reasoningOpaque;
76
96
  return msg;
77
97
  });
78
98
  }
99
+ function buildWireTools(specs) {
100
+ const nameMap = /* @__PURE__ */ new Map();
101
+ if (!specs || specs.length === 0) return { tools: void 0, nameMap };
102
+ const tools = specs.map((t) => {
103
+ const wireName = sanitizeToolName(t.name);
104
+ nameMap.set(wireName, t.name);
105
+ return {
106
+ type: "function",
107
+ function: { name: wireName, description: t.description, parameters: t.inputSchema }
108
+ };
109
+ });
110
+ return { tools, nameMap };
111
+ }
79
112
  function createOpenAICompatibleProvider(options) {
80
113
  const doFetch = options.fetchImpl ?? globalThis.fetch;
81
114
  const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
82
115
  const { models, defaultModel, modelOf } = resolveModels(options);
83
116
  async function authHeaders() {
84
117
  const cred = await options.getCredential();
85
- const headers = { "content-type": "application/json" };
118
+ const headers = {
119
+ "content-type": "application/json",
120
+ ...options.headers ?? {}
121
+ };
86
122
  if (cred) headers["authorization"] = `Bearer ${cred}`;
87
123
  return headers;
88
124
  }
89
- function body(req, stream) {
125
+ function body(req, stream, wire) {
90
126
  return JSON.stringify({
91
127
  model: modelOf(req.model).model,
92
128
  messages: toOpenAIMessages(req.messages),
93
129
  stream,
94
- ...req.tools && req.tools.length > 0 ? {
95
- tools: req.tools.map((t) => ({
96
- type: "function",
97
- function: { name: t.name, description: t.description, parameters: t.inputSchema }
98
- }))
99
- } : {}
130
+ ...wire.tools ? { tools: wire.tools } : {}
100
131
  });
101
132
  }
102
- function parseToolCalls(raw) {
133
+ function parseToolCalls(raw, nameMap) {
103
134
  const calls = raw?.tool_calls;
104
135
  if (!calls || calls.length === 0) return void 0;
105
136
  return calls.map((c) => ({
106
137
  id: c.id,
107
- name: c.function.name,
138
+ name: nameMap.get(c.function.name) ?? c.function.name,
108
139
  arguments: safeJson(c.function.arguments)
109
140
  }));
110
141
  }
111
142
  async function generate(req) {
112
143
  return withRetry(async () => {
144
+ const wire = buildWireTools(req.tools);
113
145
  let res;
114
146
  try {
115
147
  res = await doFetch(url, {
116
148
  method: "POST",
117
149
  headers: await authHeaders(),
118
- body: body(req, false),
150
+ body: body(req, false, wire),
119
151
  signal: req.signal
120
152
  });
121
153
  } catch (e) {
@@ -131,20 +163,47 @@ function createOpenAICompatibleProvider(options) {
131
163
  const choice = json["choices"]?.[0];
132
164
  if (!choice) throw new ProviderError("Provider returned no choices", "malformed");
133
165
  const message = choice.message;
166
+ const reasoningModel = modelOf(req.model).supportsReasoning;
167
+ let toolCalls = parseToolCalls(message, wire.nameMap);
168
+ if ((!toolCalls || toolCalls.length === 0) && choice.finish_reason === "tool_calls") {
169
+ const assembled = await assembleViaStream(req);
170
+ toolCalls = assembled.toolCalls;
171
+ if (!toolCalls || toolCalls.length === 0) {
172
+ throw new ProviderError(
173
+ "Provider signaled tool_calls but returned none (even when streamed)",
174
+ "malformed"
175
+ );
176
+ }
177
+ return {
178
+ text: message["content"] ?? assembled.text ?? "",
179
+ reasoning: reasoningModel ? message["reasoning"] ?? assembled.reasoning ?? void 0 : void 0,
180
+ reasoningOpaque: reasoningModel ? message["reasoning_opaque"] ?? assembled.reasoningOpaque ?? void 0 : void 0,
181
+ toolCalls
182
+ };
183
+ }
134
184
  return {
135
185
  text: message["content"] ?? "",
136
- reasoning: modelOf(req.model).supportsReasoning ? message["reasoning"] ?? void 0 : void 0,
137
- toolCalls: parseToolCalls(message)
186
+ reasoning: reasoningModel ? message["reasoning"] ?? void 0 : void 0,
187
+ reasoningOpaque: reasoningModel ? message["reasoning_opaque"] ?? void 0 : void 0,
188
+ toolCalls
138
189
  };
139
190
  }, options.retry);
140
191
  }
192
+ async function assembleViaStream(req) {
193
+ let final = { text: "" };
194
+ for await (const chunk of generateStream(req)) {
195
+ if (chunk.type === "done") final = chunk.response;
196
+ }
197
+ return final;
198
+ }
141
199
  async function* generateStream(req) {
200
+ const wire = buildWireTools(req.tools);
142
201
  let res;
143
202
  try {
144
203
  res = await doFetch(url, {
145
204
  method: "POST",
146
205
  headers: await authHeaders(),
147
- body: body(req, true),
206
+ body: body(req, true, wire),
148
207
  signal: req.signal
149
208
  });
150
209
  } catch (e) {
@@ -152,11 +211,14 @@ function createOpenAICompatibleProvider(options) {
152
211
  }
153
212
  if (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);
154
213
  if (!res.body) throw new ProviderError("Provider returned no stream body", "malformed");
214
+ const reasoningModel = modelOf(req.model).supportsReasoning;
155
215
  const reader = res.body.getReader();
156
216
  const decoder = new TextDecoder();
157
217
  let buffer = "";
158
218
  let text = "";
159
219
  let reasoning = "";
220
+ let reasoningOpaque = "";
221
+ const toolAccum = /* @__PURE__ */ new Map();
160
222
  for (; ; ) {
161
223
  const { value, done } = await reader.read();
162
224
  if (done) break;
@@ -170,19 +232,46 @@ function createOpenAICompatibleProvider(options) {
170
232
  if (data === "[DONE]") continue;
171
233
  const parsed = safeJson(data);
172
234
  const delta = parsed?.choices?.[0]?.delta;
173
- if (delta?.content) {
235
+ if (!delta) continue;
236
+ if (delta.content) {
174
237
  text += delta.content;
175
238
  yield { type: "text", text: delta.content };
176
239
  }
177
- if (delta?.reasoning && modelOf(req.model).supportsReasoning) {
240
+ if (delta.reasoning && reasoningModel) {
178
241
  reasoning += delta.reasoning;
179
242
  yield { type: "reasoning", text: delta.reasoning };
180
243
  }
244
+ if (delta.reasoning_opaque && reasoningModel) {
245
+ reasoningOpaque += delta.reasoning_opaque;
246
+ }
247
+ if (delta.tool_calls) {
248
+ for (const frag of delta.tool_calls) {
249
+ const idx = frag.index ?? 0;
250
+ const slot = toolAccum.get(idx) ?? { args: "" };
251
+ if (frag.id) slot.id = frag.id;
252
+ if (frag.function?.name) slot.name = frag.function.name;
253
+ if (frag.function?.arguments) slot.args += frag.function.arguments;
254
+ toolAccum.set(idx, slot);
255
+ }
256
+ }
181
257
  }
182
258
  }
259
+ const toolCalls = [...toolAccum.entries()].sort((a, b) => a[0] - b[0]).filter(([, s]) => s.name).map(([idx, s]) => ({
260
+ id: s.id ?? `call_${idx}`,
261
+ name: wire.nameMap.get(s.name) ?? s.name,
262
+ arguments: safeJson(s.args) ?? {}
263
+ }));
264
+ for (const toolCall of toolCalls) {
265
+ yield { type: "tool-call", toolCall };
266
+ }
183
267
  yield {
184
268
  type: "done",
185
- response: { text, reasoning: reasoning || void 0 }
269
+ response: {
270
+ text,
271
+ reasoning: reasoning || void 0,
272
+ reasoningOpaque: reasoningOpaque || void 0,
273
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0
274
+ }
186
275
  };
187
276
  }
188
277
  return {
@@ -204,6 +293,12 @@ function safeJson(s) {
204
293
 
205
294
  // src/providers/copilot.ts
206
295
  var DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
296
+ var COPILOT_DEFAULT_HEADERS = {
297
+ "Editor-Version": "vscode/1.95.0",
298
+ "Editor-Plugin-Version": "copilot-chat/0.20.0",
299
+ "Copilot-Integration-Id": "vscode-chat",
300
+ "Openai-Intent": "conversation-panel"
301
+ };
207
302
  function createCopilotProvider(options) {
208
303
  const inner = createOpenAICompatibleProvider({
209
304
  baseUrl: options.baseUrl ?? DEFAULT_COPILOT_BASE_URL,
@@ -211,6 +306,9 @@ function createCopilotProvider(options) {
211
306
  capabilities: options.capabilities,
212
307
  models: options.models,
213
308
  defaultModel: options.defaultModel,
309
+ // Copilot rejects calls missing its identification headers; defaults are
310
+ // applied here and remain overridable via `options.headers`.
311
+ headers: { ...COPILOT_DEFAULT_HEADERS, ...options.headers ?? {} },
214
312
  retry: options.retry,
215
313
  fetchImpl: options.fetchImpl
216
314
  });
@@ -225,5 +323,5 @@ function createCopilotProvider(options) {
225
323
  }
226
324
 
227
325
  export { createCopilotProvider, createOpenAICompatibleProvider, providerErrorFromStatus, resolveModels, withRetry };
228
- //# sourceMappingURL=chunk-7M3EAGCA.js.map
229
- //# sourceMappingURL=chunk-7M3EAGCA.js.map
326
+ //# sourceMappingURL=chunk-QD2FFISV.js.map
327
+ //# sourceMappingURL=chunk-QD2FFISV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/provider.ts","../src/providers/retry.ts","../src/providers/openai-compatible.ts","../src/providers/copilot.ts"],"names":[],"mappings":"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","file":"chunk-QD2FFISV.js","sourcesContent":["/**\n * LLM provider abstraction. Agents and workflows depend only on this interface,\n * never on a concrete provider, so new providers can be added without changing\n * agent/workflow code. (FR-007)\n *\n * Credentials are always obtained via a caller-supplied callback and are never\n * bundled, persisted, or logged by the framework. (FR-005a, FR-008)\n *\n * @packageDocumentation\n */\n\nimport type { Message, ModelCapabilities } from \"../core/types.js\";\nimport { ValidationError } from \"../core/errors.js\";\n\n/** A caller-supplied source of credentials. The framework never stores the value. */\nexport interface CredentialSource {\n\t/** Return the current credential (token/api key). May be async. */\n\tgetCredential(): string | Promise<string>;\n}\n\n/**\n * Model configuration for a provider. A provider may expose **one or more** models\n * (e.g. GitHub Copilot offers several; an OpenAI-compatible endpoint is usually one).\n * Supply either a single `capabilities` object or an array via `models`.\n */\nexport interface ModelSelectionOptions {\n\t/** Single-model shorthand. */\n\tcapabilities?: ModelCapabilities;\n\t/** One or more models this provider can use. */\n\tmodels?: ModelCapabilities[];\n\t/** Name of the default model (defaults to the first entry). */\n\tdefaultModel?: string;\n}\n\n/** Resolved model set with a default and a lookup helper. */\nexport interface ResolvedModels {\n\t/** All configured models (at least one). */\n\tmodels: ModelCapabilities[];\n\t/** The default model used when a request does not specify one. */\n\tdefaultModel: ModelCapabilities;\n\t/** Look up a model by name, or return the default when omitted. */\n\tmodelOf(name?: string): ModelCapabilities;\n}\n\n/**\n * Normalize {@link ModelSelectionOptions} into a model list, a default, and a\n * lookup helper. Throws {@link ValidationError} if no model is configured or a\n * named model is missing.\n *\n * @example\n * ```ts\n * const { defaultModel, modelOf } = resolveModels({\n * models: [\n * { model: \"gpt-4o\", maxInputTokens: 128000, maxOutputTokens: 16000 },\n * { model: \"o3-mini\", maxInputTokens: 200000, maxOutputTokens: 100000, supportsReasoning: true },\n * ],\n * defaultModel: \"gpt-4o\",\n * });\n * ```\n */\nexport function resolveModels(options: ModelSelectionOptions): ResolvedModels {\n\tconst models = options.models ?? (options.capabilities ? [options.capabilities] : []);\n\tif (models.length === 0) {\n\t\tthrow new ValidationError(\"Provider requires at least one model (set `capabilities` or `models`)\");\n\t}\n\tconst defaultModel = options.defaultModel\n\t\t? models.find((m) => m.model === options.defaultModel)\n\t\t: models[0];\n\tif (!defaultModel) {\n\t\tthrow new ValidationError(`defaultModel \"${options.defaultModel}\" is not present in models`);\n\t}\n\tconst modelOf = (name?: string): ModelCapabilities => {\n\t\tif (!name) return defaultModel;\n\t\tconst found = models.find((m) => m.model === name);\n\t\tif (!found) {\n\t\t\tthrow new ValidationError(`Model \"${name}\" is not configured for this provider`);\n\t\t}\n\t\treturn found;\n\t};\n\treturn { models, defaultModel, modelOf };\n}\n\n/** A tool description passed to the provider so the model can decide to call it. */\nexport interface ToolSpec {\n\tname: string;\n\tdescription: string;\n\tinputSchema: Record<string, unknown>;\n}\n\n/** A request to generate a model response. */\nexport interface GenerateRequest {\n\tmessages: Message[];\n\ttools?: ToolSpec[];\n\t/** Which configured model to use; defaults to the provider's default model. */\n\tmodel?: string;\n\t/** Abort signal to cancel an in-flight request. */\n\tsignal?: AbortSignal;\n}\n\n/** A tool call requested by the model. */\nexport interface ToolCall {\n\tid: string;\n\tname: string;\n\t/** Raw JSON arguments (validated by the tools module before invocation). */\n\targuments: unknown;\n}\n\n/** A complete (non-streaming) model response. */\nexport interface GenerateResponse {\n\t/** Final answer text. */\n\ttext: string;\n\t/** Reasoning/thinking content — only present for reasoning-capable models. (FR-003a) */\n\treasoning?: string;\n\t/**\n\t * Opaque reasoning blob (e.g. Claude thinking signature) to round-trip on the\n\t * next assistant turn for thinking continuity. Never logged or inspected.\n\t */\n\treasoningOpaque?: string;\n\t/** Tool calls the model wants to make, if any. */\n\ttoolCalls?: ToolCall[];\n\t/** Approximate token usage if reported by the provider. */\n\tusage?: { inputTokens?: number; outputTokens?: number };\n}\n\n/** An incremental streaming chunk. */\nexport type GenerateChunk =\n\t| { type: \"text\"; text: string }\n\t| { type: \"reasoning\"; text: string }\n\t| { type: \"tool-call\"; toolCall: ToolCall }\n\t| { type: \"done\"; response: GenerateResponse };\n\n/**\n * An LLM backend. Implementations adapt a concrete API (Copilot, OpenAI-compatible)\n * onto this uniform surface.\n */\nexport interface Provider {\n\t/** Stable provider identifier, e.g. `\"openai-compatible\"`. */\n\treadonly name: string;\n\t/** The default model's capability configuration. (FR-007a) */\n\treadonly capabilities: ModelCapabilities;\n\t/** All models this provider is configured with (one or more). */\n\treadonly models: ModelCapabilities[];\n\t/** Look up a configured model by name, or the default when omitted. */\n\tmodel(name?: string): ModelCapabilities;\n\t/** Generate a complete response. */\n\tgenerate(req: GenerateRequest): Promise<GenerateResponse>;\n\t/** Generate a streamed response. */\n\tgenerateStream(req: GenerateRequest): AsyncIterable<GenerateChunk>;\n}\n","/**\n * Exponential-backoff retry for transient provider failures. Transient errors\n * (429 with Retry-After, 5xx, network/timeout) are retried; auth/4xx fail fast.\n * (FR-008a)\n *\n * @packageDocumentation\n */\n\nimport { ProviderError } from \"../core/errors.js\";\n\n/** Retry tuning. All fields have safe defaults. */\nexport interface RetryOptions {\n\t/** Maximum retry attempts after the first try. Default 3. */\n\tmaxRetries?: number;\n\t/** Base delay in ms for backoff. Default 250. */\n\tbaseDelayMs?: number;\n\t/** Maximum delay cap in ms. Default 8000. */\n\tmaxDelayMs?: number;\n}\n\nconst DEFAULTS: Required<RetryOptions> = {\n\tmaxRetries: 3,\n\tbaseDelayMs: 250,\n\tmaxDelayMs: 8000,\n};\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((r) => setTimeout(r, ms));\n}\n\n/**\n * Run `fn`, retrying transient {@link ProviderError}s with exponential backoff\n * and jitter. Non-transient errors are rethrown immediately (fail fast).\n *\n * @param fn - The operation to attempt. It should throw a {@link ProviderError}.\n * @param opts - Retry tuning.\n * @param retryAfterMs - Optional hook returning a server-specified delay (Retry-After).\n */\nexport async function withRetry<T>(\n\tfn: () => Promise<T>,\n\topts?: RetryOptions,\n\tretryAfterMs?: (err: ProviderError) => number | undefined,\n): Promise<T> {\n\tconst cfg = { ...DEFAULTS, ...opts };\n\tlet attempt = 0;\n\n\tfor (; ;) {\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} catch (err) {\n\t\t\tconst isRetryable = err instanceof ProviderError && err.retryable;\n\t\t\tif (!isRetryable || attempt >= cfg.maxRetries) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\tconst serverDelay = retryAfterMs?.(err as ProviderError);\n\t\t\tconst backoff = Math.min(cfg.baseDelayMs * 2 ** attempt, cfg.maxDelayMs);\n\t\t\tconst jitter = Math.random() * cfg.baseDelayMs;\n\t\t\tawait sleep(serverDelay ?? backoff + jitter);\n\t\t\tattempt++;\n\t\t}\n\t}\n}\n\n/** Map an HTTP status to a {@link ProviderError} with the right retry semantics. */\nexport function providerErrorFromStatus(status: number, message: string): ProviderError {\n\tif (status === 429 || status >= 500) {\n\t\treturn new ProviderError(message, \"transient\", { status });\n\t}\n\tif (status === 401 || status === 403) {\n\t\treturn new ProviderError(message, \"auth\", { status });\n\t}\n\treturn new ProviderError(message, \"client\", { status });\n}\n","/**\n * OpenAI-compatible provider. Targets any endpoint speaking the OpenAI\n * `/chat/completions` API, including local servers such as LM Studio via a custom\n * `baseUrl`, and GitHub Copilot (see {@link createCopilotProvider}). (FR-006)\n *\n * Provider compatibility notes (handled here so callers don't have to):\n * - Tool names are sanitized to `^[a-zA-Z0-9_-]+$` on the wire (OpenAI/Copilot\n * reject dotted names like `webiq.browse`) and translated back to the registry\n * key when the model calls them.\n * - Assistant turns that requested tools emit `tool_calls` with `content: null`\n * so strict providers (e.g. Anthropic) can pair each tool result with its call.\n * - Streaming responses accumulate `delta.tool_calls[]` keyed by `index`\n * (fragments may start at a non-zero index when reasoning occupies 0/1).\n * - Some reasoning models report `finish_reason: \"tool_calls\"` from the\n * non-streaming endpoint without a `tool_calls` array; `generate` transparently\n * re-requests in streaming mode and assembles them, failing loud (typed\n * {@link ProviderError}) rather than silently stopping if none materialize.\n *\n * @packageDocumentation\n */\n\nimport type { Message, ContentPart, MessageToolCall } from \"../core/types.js\";\nimport { ProviderError } from \"../core/errors.js\";\nimport type {\n\tProvider,\n\tCredentialSource,\n\tGenerateRequest,\n\tGenerateResponse,\n\tGenerateChunk,\n\tToolCall,\n\tToolSpec,\n} from \"./provider.js\";\nimport { resolveModels, type ModelSelectionOptions } from \"./provider.js\";\nimport { withRetry, providerErrorFromStatus, type RetryOptions } from \"./retry.js\";\n\n/**\n * Options for {@link createOpenAICompatibleProvider}.\n *\n * Supply a single model via `capabilities`, or multiple via `models` (most\n * OpenAI-compatible endpoints expose one model, but multiple are supported).\n */\nexport interface OpenAICompatibleProviderOptions extends CredentialSource, ModelSelectionOptions {\n\t/** Base URL of the OpenAI-compatible API, e.g. `http://localhost:1234/v1`. */\n\tbaseUrl: string;\n\t/**\n\t * Extra request headers merged into every call (e.g. provider-required\n\t * identification headers). The `authorization` header is always set from\n\t * `getCredential()` and cannot be overridden here.\n\t */\n\theaders?: Record<string, string>;\n\t/** Retry tuning for transient failures. */\n\tretry?: RetryOptions;\n\t/** Optional custom fetch (for testing or non-standard runtimes). */\n\tfetchImpl?: typeof fetch;\n}\n\ninterface OpenAIMessage {\n\trole: string;\n\tcontent: unknown;\n\ttool_calls?: Array<{ id: string; type: \"function\"; function: { name: string; arguments: string } }>;\n\ttool_call_id?: string;\n\tname?: string;\n\treasoning_opaque?: string;\n}\n\n/** OpenAI/Copilot require tool names to match `^[a-zA-Z0-9_-]{1,128}$`. */\nconst UNSAFE_TOOL_NAME_CHARS = /[^a-zA-Z0-9_-]/g;\n\n/**\n * Sanitize a (possibly namespaced) tool name for the wire. Dotted MCP names like\n * `webiq.browse` become `webiq_browse`; the original is recovered via the\n * per-request name map when the model calls the tool.\n */\nfunction sanitizeToolName(name: string): string {\n\treturn name.replace(UNSAFE_TOOL_NAME_CHARS, \"_\");\n}\n\nfunction toOpenAIContent(parts: ContentPart[]): unknown {\n\tif (parts.every((p) => p.type === \"text\")) {\n\t\treturn parts.map((p) => (p as { text: string }).text).join(\"\");\n\t}\n\treturn parts.map((p) =>\n\t\tp.type === \"text\"\n\t\t\t? { type: \"text\", text: p.text }\n\t\t\t: { type: \"image_url\", image_url: { url: p.data } },\n\t);\n}\n\nfunction toWireToolCalls(\n\ttoolCalls: MessageToolCall[],\n): Array<{ id: string; type: \"function\"; function: { name: string; arguments: string } }> {\n\treturn toolCalls.map((tc) => ({\n\t\tid: tc.id,\n\t\ttype: \"function\",\n\t\tfunction: {\n\t\t\tname: sanitizeToolName(tc.name),\n\t\t\targuments:\n\t\t\t\ttypeof tc.arguments === \"string\" ? tc.arguments : JSON.stringify(tc.arguments ?? {}),\n\t\t},\n\t}));\n}\n\nfunction toOpenAIMessages(messages: Message[]): OpenAIMessage[] {\n\treturn messages.map((m) => {\n\t\tconst msg: OpenAIMessage = { role: m.role, content: toOpenAIContent(m.parts) };\n\t\tif (m.toolCalls && m.toolCalls.length > 0) {\n\t\t\tmsg.tool_calls = toWireToolCalls(m.toolCalls);\n\t\t\t// Strict providers require `content: null` (not \"\") on an assistant turn\n\t\t\t// that only carries tool calls.\n\t\t\tconst hasText = m.parts.some((p) => p.type === \"text\" && p.text.length > 0);\n\t\t\tif (!hasText) msg.content = null;\n\t\t}\n\t\tif (m.toolCallId) msg.tool_call_id = m.toolCallId;\n\t\tif (m.name) msg.name = sanitizeToolName(m.name);\n\t\tif (m.reasoningOpaque) msg.reasoning_opaque = m.reasoningOpaque;\n\t\treturn msg;\n\t});\n}\n\n/** Wire tool specs plus a map from sanitized name back to the registry key. */\ninterface WireTools {\n\ttools?: Array<{ type: \"function\"; function: { name: string; description: string; parameters: unknown } }>;\n\tnameMap: Map<string, string>;\n}\n\nfunction buildWireTools(specs?: ToolSpec[]): WireTools {\n\tconst nameMap = new Map<string, string>();\n\tif (!specs || specs.length === 0) return { tools: undefined, nameMap };\n\tconst tools = specs.map((t) => {\n\t\tconst wireName = sanitizeToolName(t.name);\n\t\tnameMap.set(wireName, t.name);\n\t\treturn {\n\t\t\ttype: \"function\" as const,\n\t\t\tfunction: { name: wireName, description: t.description, parameters: t.inputSchema },\n\t\t};\n\t});\n\treturn { tools, nameMap };\n}\n\n/**\n * Create an OpenAI-compatible provider (works with LM Studio, vLLM, etc.).\n *\n * @example\n * ```ts\n * const provider = createOpenAICompatibleProvider({\n * baseUrl: \"http://localhost:1234/v1\",\n * getCredential: () => process.env.LMSTUDIO_KEY ?? \"\",\n * capabilities: { model: \"local\", maxInputTokens: 262144, maxOutputTokens: 32000 },\n * });\n * ```\n */\nexport function createOpenAICompatibleProvider(\n\toptions: OpenAICompatibleProviderOptions,\n): Provider {\n\tconst doFetch = options.fetchImpl ?? globalThis.fetch;\n\tconst url = `${options.baseUrl.replace(/\\/$/, \"\")}/chat/completions`;\n\tconst { models, defaultModel, modelOf } = resolveModels(options);\n\n\tasync function authHeaders(): Promise<Record<string, string>> {\n\t\tconst cred = await options.getCredential();\n\t\t// content-type first, then caller headers (may override it), then the\n\t\t// credential-derived authorization which always wins.\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"content-type\": \"application/json\",\n\t\t\t...(options.headers ?? {}),\n\t\t};\n\t\tif (cred) headers[\"authorization\"] = `Bearer ${cred}`;\n\t\treturn headers;\n\t}\n\n\tfunction body(req: GenerateRequest, stream: boolean, wire: WireTools): string {\n\t\treturn JSON.stringify({\n\t\t\tmodel: modelOf(req.model).model,\n\t\t\tmessages: toOpenAIMessages(req.messages),\n\t\t\tstream,\n\t\t\t...(wire.tools ? { tools: wire.tools } : {}),\n\t\t});\n\t}\n\n\tfunction parseToolCalls(raw: unknown, nameMap: Map<string, string>): ToolCall[] | undefined {\n\t\tconst calls = (raw as {\n\t\t\ttool_calls?: Array<{ id: string; function: { name: string; arguments: string } }>;\n\t\t})?.tool_calls;\n\t\tif (!calls || calls.length === 0) return undefined;\n\t\treturn calls.map((c) => ({\n\t\t\tid: c.id,\n\t\t\tname: nameMap.get(c.function.name) ?? c.function.name,\n\t\t\targuments: safeJson(c.function.arguments),\n\t\t}));\n\t}\n\n\tasync function generate(req: GenerateRequest): Promise<GenerateResponse> {\n\t\treturn withRetry(async () => {\n\t\t\tconst wire = buildWireTools(req.tools);\n\t\t\tlet res: Response;\n\t\t\ttry {\n\t\t\t\tres = await doFetch(url, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: await authHeaders(),\n\t\t\t\t\tbody: body(req, false, wire),\n\t\t\t\t\tsignal: req.signal,\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tthrow new ProviderError(`Network error: ${(e as Error).message}`, \"transient\");\n\t\t\t}\n\t\t\tif (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);\n\n\t\t\tlet json: Record<string, unknown>;\n\t\t\ttry {\n\t\t\t\tjson = (await res.json()) as Record<string, unknown>;\n\t\t\t} catch {\n\t\t\t\tthrow new ProviderError(\"Malformed provider response\", \"malformed\");\n\t\t\t}\n\t\t\tconst choice = (\n\t\t\t\tjson[\"choices\"] as Array<{ message: Record<string, unknown>; finish_reason?: string }>\n\t\t\t)?.[0];\n\t\t\tif (!choice) throw new ProviderError(\"Provider returned no choices\", \"malformed\");\n\t\t\tconst message = choice.message;\n\t\t\tconst reasoningModel = modelOf(req.model).supportsReasoning;\n\t\t\tlet toolCalls = parseToolCalls(message, wire.nameMap);\n\n\t\t\t// Bug 4(b): some reasoning models report `finish_reason: \"tool_calls\"` from\n\t\t\t// the non-streaming endpoint without a `tool_calls` array. Re-request in\n\t\t\t// streaming mode and assemble them so the agent loop can proceed.\n\t\t\tif ((!toolCalls || toolCalls.length === 0) && choice.finish_reason === \"tool_calls\") {\n\t\t\t\tconst assembled = await assembleViaStream(req);\n\t\t\t\ttoolCalls = assembled.toolCalls;\n\t\t\t\tif (!toolCalls || toolCalls.length === 0) {\n\t\t\t\t\tthrow new ProviderError(\n\t\t\t\t\t\t\"Provider signaled tool_calls but returned none (even when streamed)\",\n\t\t\t\t\t\t\"malformed\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\ttext: (message[\"content\"] as string) ?? assembled.text ?? \"\",\n\t\t\t\t\treasoning: reasoningModel\n\t\t\t\t\t\t? ((message[\"reasoning\"] as string) ?? assembled.reasoning ?? undefined)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\treasoningOpaque: reasoningModel\n\t\t\t\t\t\t? ((message[\"reasoning_opaque\"] as string) ?? assembled.reasoningOpaque ?? undefined)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\ttoolCalls,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttext: (message[\"content\"] as string) ?? \"\",\n\t\t\t\treasoning: reasoningModel ? ((message[\"reasoning\"] as string) ?? undefined) : undefined,\n\t\t\t\treasoningOpaque: reasoningModel\n\t\t\t\t\t? ((message[\"reasoning_opaque\"] as string) ?? undefined)\n\t\t\t\t\t: undefined,\n\t\t\t\ttoolCalls,\n\t\t\t};\n\t\t}, options.retry);\n\t}\n\n\t/** Drive a streaming request to completion and return its assembled final response. */\n\tasync function assembleViaStream(req: GenerateRequest): Promise<GenerateResponse> {\n\t\tlet final: GenerateResponse = { text: \"\" };\n\t\tfor await (const chunk of generateStream(req)) {\n\t\t\tif (chunk.type === \"done\") final = chunk.response;\n\t\t}\n\t\treturn final;\n\t}\n\n\tasync function* generateStream(req: GenerateRequest): AsyncIterable<GenerateChunk> {\n\t\tconst wire = buildWireTools(req.tools);\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tres = await doFetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: await authHeaders(),\n\t\t\t\tbody: body(req, true, wire),\n\t\t\t\tsignal: req.signal,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tthrow new ProviderError(`Network error: ${(e as Error).message}`, \"transient\");\n\t\t}\n\t\tif (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);\n\t\tif (!res.body) throw new ProviderError(\"Provider returned no stream body\", \"malformed\");\n\n\t\tconst reasoningModel = modelOf(req.model).supportsReasoning;\n\t\tconst reader = res.body.getReader();\n\t\tconst decoder = new TextDecoder();\n\t\tlet buffer = \"\";\n\t\tlet text = \"\";\n\t\tlet reasoning = \"\";\n\t\tlet reasoningOpaque = \"\";\n\t\t// Accumulate streamed tool-call fragments keyed by their `index` (which may\n\t\t// start at a non-zero value when reasoning deltas occupy the first indices).\n\t\tconst toolAccum = new Map<number, { id?: string; name?: string; args: string }>();\n\n\t\tfor (; ;) {\n\t\t\tconst { value, done } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\tfor (const line of lines) {\n\t\t\t\tconst trimmed = line.trim();\n\t\t\t\tif (!trimmed.startsWith(\"data:\")) continue;\n\t\t\t\tconst data = trimmed.slice(5).trim();\n\t\t\t\tif (data === \"[DONE]\") continue;\n\t\t\t\tconst parsed = safeJson(data) as\n\t\t\t\t\t| {\n\t\t\t\t\t\tchoices?: Array<{\n\t\t\t\t\t\t\tdelta?: {\n\t\t\t\t\t\t\t\tcontent?: string;\n\t\t\t\t\t\t\t\treasoning?: string;\n\t\t\t\t\t\t\t\treasoning_opaque?: string;\n\t\t\t\t\t\t\t\ttool_calls?: Array<{\n\t\t\t\t\t\t\t\t\tindex?: number;\n\t\t\t\t\t\t\t\t\tid?: string;\n\t\t\t\t\t\t\t\t\tfunction?: { name?: string; arguments?: string };\n\t\t\t\t\t\t\t\t}>;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}>;\n\t\t\t\t\t}\n\t\t\t\t\t| undefined;\n\t\t\t\tconst delta = parsed?.choices?.[0]?.delta;\n\t\t\t\tif (!delta) continue;\n\t\t\t\tif (delta.content) {\n\t\t\t\t\ttext += delta.content;\n\t\t\t\t\tyield { type: \"text\", text: delta.content };\n\t\t\t\t}\n\t\t\t\tif (delta.reasoning && reasoningModel) {\n\t\t\t\t\treasoning += delta.reasoning;\n\t\t\t\t\tyield { type: \"reasoning\", text: delta.reasoning };\n\t\t\t\t}\n\t\t\t\tif (delta.reasoning_opaque && reasoningModel) {\n\t\t\t\t\treasoningOpaque += delta.reasoning_opaque;\n\t\t\t\t}\n\t\t\t\tif (delta.tool_calls) {\n\t\t\t\t\tfor (const frag of delta.tool_calls) {\n\t\t\t\t\t\tconst idx = frag.index ?? 0;\n\t\t\t\t\t\tconst slot = toolAccum.get(idx) ?? { args: \"\" };\n\t\t\t\t\t\tif (frag.id) slot.id = frag.id;\n\t\t\t\t\t\tif (frag.function?.name) slot.name = frag.function.name;\n\t\t\t\t\t\tif (frag.function?.arguments) slot.args += frag.function.arguments;\n\t\t\t\t\t\ttoolAccum.set(idx, slot);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Materialize accumulated tool calls (sorted by stream index) and surface\n\t\t// them both as chunks and in the final `done` response.\n\t\tconst toolCalls: ToolCall[] = [...toolAccum.entries()]\n\t\t\t.sort((a, b) => a[0] - b[0])\n\t\t\t.filter(([, s]) => s.name)\n\t\t\t.map(([idx, s]) => ({\n\t\t\t\tid: s.id ?? `call_${idx}`,\n\t\t\t\tname: wire.nameMap.get(s.name as string) ?? (s.name as string),\n\t\t\t\targuments: safeJson(s.args) ?? {},\n\t\t\t}));\n\t\tfor (const toolCall of toolCalls) {\n\t\t\tyield { type: \"tool-call\", toolCall };\n\t\t}\n\t\tyield {\n\t\t\ttype: \"done\",\n\t\t\tresponse: {\n\t\t\t\ttext,\n\t\t\t\treasoning: reasoning || undefined,\n\t\t\t\treasoningOpaque: reasoningOpaque || undefined,\n\t\t\t\ttoolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tname: \"openai-compatible\",\n\t\tcapabilities: defaultModel,\n\t\tmodels,\n\t\tmodel: modelOf,\n\t\tgenerate,\n\t\tgenerateStream,\n\t};\n}\n\nfunction safeJson(s: string): unknown {\n\ttry {\n\t\treturn JSON.parse(s);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","/**\n * GitHub Copilot provider. (FR-005)\n *\n * Copilot's chat API is OpenAI-compatible, so this provider configures the shared\n * OpenAI-compatible transport with Copilot's endpoint and the caller-supplied\n * credential (a Copilot/GitHub token). The token is obtained via callback and is\n * never bundled, persisted, or logged. (FR-005a)\n *\n * In a frontend-only deployment the end user supplies their own token (it stays\n * client-side); in a backend deployment the developer may supply it, or the user\n * sends it per request over SSL/TLS and the backend must not log or persist it.\n *\n * @packageDocumentation\n */\n\nimport type { Provider, CredentialSource, ModelSelectionOptions } from \"./provider.js\";\nimport type { RetryOptions } from \"./retry.js\";\nimport { createOpenAICompatibleProvider } from \"./openai-compatible.js\";\n\n/** Default Copilot-compatible chat completions base URL. */\nconst DEFAULT_COPILOT_BASE_URL = \"https://api.githubcopilot.com\";\n\n/**\n * Headers `api.githubcopilot.com` requires on every request. Omitting any of\n * these causes the API to reject the call with HTTP 400. They are sent by default\n * and can be overridden per option via `headers`.\n */\nconst COPILOT_DEFAULT_HEADERS: Record<string, string> = {\n\t\"Editor-Version\": \"vscode/1.95.0\",\n\t\"Editor-Plugin-Version\": \"copilot-chat/0.20.0\",\n\t\"Copilot-Integration-Id\": \"vscode-chat\",\n\t\"Openai-Intent\": \"conversation-panel\",\n};\n\n/**\n * Options for {@link createCopilotProvider}.\n *\n * GitHub Copilot exposes several models, so configure them via `models` (with an\n * optional `defaultModel`). A single `capabilities` object is also accepted.\n */\nexport interface CopilotProviderOptions extends CredentialSource, ModelSelectionOptions {\n\t/** Override the Copilot base URL if needed. */\n\tbaseUrl?: string;\n\t/**\n\t * Extra/override request headers. Merged over the required Copilot defaults\n\t * (`Editor-Version`, `Editor-Plugin-Version`, `Copilot-Integration-Id`,\n\t * `Openai-Intent`), so you can adjust them without losing the others.\n\t */\n\theaders?: Record<string, string>;\n\t/** Retry tuning for transient failures. */\n\tretry?: RetryOptions;\n\t/** Optional custom fetch (for testing or non-standard runtimes). */\n\tfetchImpl?: typeof fetch;\n}\n\n/**\n * Create a GitHub Copilot provider.\n *\n * @example Single model\n * ```ts\n * const provider = createCopilotProvider({\n * getCredential: () => myCopilotToken, // never logged or persisted\n * capabilities: { model: \"gpt-4o\", maxInputTokens: 128000, maxOutputTokens: 16000 },\n * });\n * ```\n *\n * @example Multiple models\n * ```ts\n * const provider = createCopilotProvider({\n * getCredential: () => myCopilotToken,\n * models: [\n * { model: \"gpt-4o\", maxInputTokens: 128000, maxOutputTokens: 16000, supportsVision: true },\n * { model: \"o3-mini\", maxInputTokens: 200000, maxOutputTokens: 100000, supportsReasoning: true },\n * ],\n * defaultModel: \"gpt-4o\",\n * });\n * // Pick a model per request: provider.generate({ messages, model: \"o3-mini\" })\n * ```\n */\nexport function createCopilotProvider(options: CopilotProviderOptions): Provider {\n\tconst inner = createOpenAICompatibleProvider({\n\t\tbaseUrl: options.baseUrl ?? DEFAULT_COPILOT_BASE_URL,\n\t\tgetCredential: options.getCredential,\n\t\tcapabilities: options.capabilities,\n\t\tmodels: options.models,\n\t\tdefaultModel: options.defaultModel,\n\t\t// Copilot rejects calls missing its identification headers; defaults are\n\t\t// applied here and remain overridable via `options.headers`.\n\t\theaders: { ...COPILOT_DEFAULT_HEADERS, ...(options.headers ?? {}) },\n\t\tretry: options.retry,\n\t\tfetchImpl: options.fetchImpl,\n\t});\n\t// Preserve the provider contract but report the Copilot name.\n\treturn {\n\t\tname: \"copilot\",\n\t\tcapabilities: inner.capabilities,\n\t\tmodels: inner.models,\n\t\tmodel: inner.model.bind(inner),\n\t\tgenerate: inner.generate.bind(inner),\n\t\tgenerateStream: inner.generateStream.bind(inner),\n\t};\n}\n"]}
@@ -62,6 +62,10 @@ function providerErrorFromStatus(status, message) {
62
62
  }
63
63
 
64
64
  // src/providers/openai-compatible.ts
65
+ var UNSAFE_TOOL_NAME_CHARS = /[^a-zA-Z0-9_-]/g;
66
+ function sanitizeToolName(name) {
67
+ return name.replace(UNSAFE_TOOL_NAME_CHARS, "_");
68
+ }
65
69
  function toOpenAIContent(parts) {
66
70
  if (parts.every((p) => p.type === "text")) {
67
71
  return parts.map((p) => p.text).join("");
@@ -70,54 +74,82 @@ function toOpenAIContent(parts) {
70
74
  (p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image_url", image_url: { url: p.data } }
71
75
  );
72
76
  }
77
+ function toWireToolCalls(toolCalls) {
78
+ return toolCalls.map((tc) => ({
79
+ id: tc.id,
80
+ type: "function",
81
+ function: {
82
+ name: sanitizeToolName(tc.name),
83
+ arguments: typeof tc.arguments === "string" ? tc.arguments : JSON.stringify(tc.arguments ?? {})
84
+ }
85
+ }));
86
+ }
73
87
  function toOpenAIMessages(messages) {
74
88
  return messages.map((m) => {
75
89
  const msg = { role: m.role, content: toOpenAIContent(m.parts) };
90
+ if (m.toolCalls && m.toolCalls.length > 0) {
91
+ msg.tool_calls = toWireToolCalls(m.toolCalls);
92
+ const hasText = m.parts.some((p) => p.type === "text" && p.text.length > 0);
93
+ if (!hasText) msg.content = null;
94
+ }
76
95
  if (m.toolCallId) msg.tool_call_id = m.toolCallId;
77
- if (m.name) msg.name = m.name;
96
+ if (m.name) msg.name = sanitizeToolName(m.name);
97
+ if (m.reasoningOpaque) msg.reasoning_opaque = m.reasoningOpaque;
78
98
  return msg;
79
99
  });
80
100
  }
101
+ function buildWireTools(specs) {
102
+ const nameMap = /* @__PURE__ */ new Map();
103
+ if (!specs || specs.length === 0) return { tools: void 0, nameMap };
104
+ const tools = specs.map((t) => {
105
+ const wireName = sanitizeToolName(t.name);
106
+ nameMap.set(wireName, t.name);
107
+ return {
108
+ type: "function",
109
+ function: { name: wireName, description: t.description, parameters: t.inputSchema }
110
+ };
111
+ });
112
+ return { tools, nameMap };
113
+ }
81
114
  function createOpenAICompatibleProvider(options) {
82
115
  const doFetch = options.fetchImpl ?? globalThis.fetch;
83
116
  const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
84
117
  const { models, defaultModel, modelOf } = resolveModels(options);
85
118
  async function authHeaders() {
86
119
  const cred = await options.getCredential();
87
- const headers = { "content-type": "application/json" };
120
+ const headers = {
121
+ "content-type": "application/json",
122
+ ...options.headers ?? {}
123
+ };
88
124
  if (cred) headers["authorization"] = `Bearer ${cred}`;
89
125
  return headers;
90
126
  }
91
- function body(req, stream) {
127
+ function body(req, stream, wire) {
92
128
  return JSON.stringify({
93
129
  model: modelOf(req.model).model,
94
130
  messages: toOpenAIMessages(req.messages),
95
131
  stream,
96
- ...req.tools && req.tools.length > 0 ? {
97
- tools: req.tools.map((t) => ({
98
- type: "function",
99
- function: { name: t.name, description: t.description, parameters: t.inputSchema }
100
- }))
101
- } : {}
132
+ ...wire.tools ? { tools: wire.tools } : {}
102
133
  });
103
134
  }
104
- function parseToolCalls(raw) {
135
+ function parseToolCalls(raw, nameMap) {
105
136
  const calls = raw?.tool_calls;
106
137
  if (!calls || calls.length === 0) return void 0;
107
138
  return calls.map((c) => ({
108
139
  id: c.id,
109
- name: c.function.name,
140
+ name: nameMap.get(c.function.name) ?? c.function.name,
110
141
  arguments: safeJson(c.function.arguments)
111
142
  }));
112
143
  }
113
144
  async function generate(req) {
114
145
  return withRetry(async () => {
146
+ const wire = buildWireTools(req.tools);
115
147
  let res;
116
148
  try {
117
149
  res = await doFetch(url, {
118
150
  method: "POST",
119
151
  headers: await authHeaders(),
120
- body: body(req, false),
152
+ body: body(req, false, wire),
121
153
  signal: req.signal
122
154
  });
123
155
  } catch (e) {
@@ -133,20 +165,47 @@ function createOpenAICompatibleProvider(options) {
133
165
  const choice = json["choices"]?.[0];
134
166
  if (!choice) throw new chunkMQ2XTH3S_cjs.ProviderError("Provider returned no choices", "malformed");
135
167
  const message = choice.message;
168
+ const reasoningModel = modelOf(req.model).supportsReasoning;
169
+ let toolCalls = parseToolCalls(message, wire.nameMap);
170
+ if ((!toolCalls || toolCalls.length === 0) && choice.finish_reason === "tool_calls") {
171
+ const assembled = await assembleViaStream(req);
172
+ toolCalls = assembled.toolCalls;
173
+ if (!toolCalls || toolCalls.length === 0) {
174
+ throw new chunkMQ2XTH3S_cjs.ProviderError(
175
+ "Provider signaled tool_calls but returned none (even when streamed)",
176
+ "malformed"
177
+ );
178
+ }
179
+ return {
180
+ text: message["content"] ?? assembled.text ?? "",
181
+ reasoning: reasoningModel ? message["reasoning"] ?? assembled.reasoning ?? void 0 : void 0,
182
+ reasoningOpaque: reasoningModel ? message["reasoning_opaque"] ?? assembled.reasoningOpaque ?? void 0 : void 0,
183
+ toolCalls
184
+ };
185
+ }
136
186
  return {
137
187
  text: message["content"] ?? "",
138
- reasoning: modelOf(req.model).supportsReasoning ? message["reasoning"] ?? void 0 : void 0,
139
- toolCalls: parseToolCalls(message)
188
+ reasoning: reasoningModel ? message["reasoning"] ?? void 0 : void 0,
189
+ reasoningOpaque: reasoningModel ? message["reasoning_opaque"] ?? void 0 : void 0,
190
+ toolCalls
140
191
  };
141
192
  }, options.retry);
142
193
  }
194
+ async function assembleViaStream(req) {
195
+ let final = { text: "" };
196
+ for await (const chunk of generateStream(req)) {
197
+ if (chunk.type === "done") final = chunk.response;
198
+ }
199
+ return final;
200
+ }
143
201
  async function* generateStream(req) {
202
+ const wire = buildWireTools(req.tools);
144
203
  let res;
145
204
  try {
146
205
  res = await doFetch(url, {
147
206
  method: "POST",
148
207
  headers: await authHeaders(),
149
- body: body(req, true),
208
+ body: body(req, true, wire),
150
209
  signal: req.signal
151
210
  });
152
211
  } catch (e) {
@@ -154,11 +213,14 @@ function createOpenAICompatibleProvider(options) {
154
213
  }
155
214
  if (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);
156
215
  if (!res.body) throw new chunkMQ2XTH3S_cjs.ProviderError("Provider returned no stream body", "malformed");
216
+ const reasoningModel = modelOf(req.model).supportsReasoning;
157
217
  const reader = res.body.getReader();
158
218
  const decoder = new TextDecoder();
159
219
  let buffer = "";
160
220
  let text = "";
161
221
  let reasoning = "";
222
+ let reasoningOpaque = "";
223
+ const toolAccum = /* @__PURE__ */ new Map();
162
224
  for (; ; ) {
163
225
  const { value, done } = await reader.read();
164
226
  if (done) break;
@@ -172,19 +234,46 @@ function createOpenAICompatibleProvider(options) {
172
234
  if (data === "[DONE]") continue;
173
235
  const parsed = safeJson(data);
174
236
  const delta = parsed?.choices?.[0]?.delta;
175
- if (delta?.content) {
237
+ if (!delta) continue;
238
+ if (delta.content) {
176
239
  text += delta.content;
177
240
  yield { type: "text", text: delta.content };
178
241
  }
179
- if (delta?.reasoning && modelOf(req.model).supportsReasoning) {
242
+ if (delta.reasoning && reasoningModel) {
180
243
  reasoning += delta.reasoning;
181
244
  yield { type: "reasoning", text: delta.reasoning };
182
245
  }
246
+ if (delta.reasoning_opaque && reasoningModel) {
247
+ reasoningOpaque += delta.reasoning_opaque;
248
+ }
249
+ if (delta.tool_calls) {
250
+ for (const frag of delta.tool_calls) {
251
+ const idx = frag.index ?? 0;
252
+ const slot = toolAccum.get(idx) ?? { args: "" };
253
+ if (frag.id) slot.id = frag.id;
254
+ if (frag.function?.name) slot.name = frag.function.name;
255
+ if (frag.function?.arguments) slot.args += frag.function.arguments;
256
+ toolAccum.set(idx, slot);
257
+ }
258
+ }
183
259
  }
184
260
  }
261
+ const toolCalls = [...toolAccum.entries()].sort((a, b) => a[0] - b[0]).filter(([, s]) => s.name).map(([idx, s]) => ({
262
+ id: s.id ?? `call_${idx}`,
263
+ name: wire.nameMap.get(s.name) ?? s.name,
264
+ arguments: safeJson(s.args) ?? {}
265
+ }));
266
+ for (const toolCall of toolCalls) {
267
+ yield { type: "tool-call", toolCall };
268
+ }
185
269
  yield {
186
270
  type: "done",
187
- response: { text, reasoning: reasoning || void 0 }
271
+ response: {
272
+ text,
273
+ reasoning: reasoning || void 0,
274
+ reasoningOpaque: reasoningOpaque || void 0,
275
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0
276
+ }
188
277
  };
189
278
  }
190
279
  return {
@@ -206,6 +295,12 @@ function safeJson(s) {
206
295
 
207
296
  // src/providers/copilot.ts
208
297
  var DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
298
+ var COPILOT_DEFAULT_HEADERS = {
299
+ "Editor-Version": "vscode/1.95.0",
300
+ "Editor-Plugin-Version": "copilot-chat/0.20.0",
301
+ "Copilot-Integration-Id": "vscode-chat",
302
+ "Openai-Intent": "conversation-panel"
303
+ };
209
304
  function createCopilotProvider(options) {
210
305
  const inner = createOpenAICompatibleProvider({
211
306
  baseUrl: options.baseUrl ?? DEFAULT_COPILOT_BASE_URL,
@@ -213,6 +308,9 @@ function createCopilotProvider(options) {
213
308
  capabilities: options.capabilities,
214
309
  models: options.models,
215
310
  defaultModel: options.defaultModel,
311
+ // Copilot rejects calls missing its identification headers; defaults are
312
+ // applied here and remain overridable via `options.headers`.
313
+ headers: { ...COPILOT_DEFAULT_HEADERS, ...options.headers ?? {} },
216
314
  retry: options.retry,
217
315
  fetchImpl: options.fetchImpl
218
316
  });
@@ -231,5 +329,5 @@ exports.createOpenAICompatibleProvider = createOpenAICompatibleProvider;
231
329
  exports.providerErrorFromStatus = providerErrorFromStatus;
232
330
  exports.resolveModels = resolveModels;
233
331
  exports.withRetry = withRetry;
234
- //# sourceMappingURL=chunk-U64OEHG6.cjs.map
235
- //# sourceMappingURL=chunk-U64OEHG6.cjs.map
332
+ //# sourceMappingURL=chunk-XMDGLQFL.cjs.map
333
+ //# sourceMappingURL=chunk-XMDGLQFL.cjs.map