agent-framework-js 0.1.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.
- package/AGENT_USAGE.md +207 -0
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/dist/agents/index.cjs +35 -0
- package/dist/agents/index.cjs.map +1 -0
- package/dist/agents/index.d.cts +8 -0
- package/dist/agents/index.d.ts +8 -0
- package/dist/agents/index.js +10 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/chunk-5M6ER4ZX.cjs +4 -0
- package/dist/chunk-5M6ER4ZX.cjs.map +1 -0
- package/dist/chunk-5PDJOBTD.js +38 -0
- package/dist/chunk-5PDJOBTD.js.map +1 -0
- package/dist/chunk-7ZXUIHLH.js +18 -0
- package/dist/chunk-7ZXUIHLH.js.map +1 -0
- package/dist/chunk-ACBIHS5H.js +30 -0
- package/dist/chunk-ACBIHS5H.js.map +1 -0
- package/dist/chunk-DEABART4.js +54 -0
- package/dist/chunk-DEABART4.js.map +1 -0
- package/dist/chunk-FOTCUNP5.cjs +34 -0
- package/dist/chunk-FOTCUNP5.cjs.map +1 -0
- package/dist/chunk-FSDMBWQV.cjs +20 -0
- package/dist/chunk-FSDMBWQV.cjs.map +1 -0
- package/dist/chunk-GYDY3KX5.cjs +72 -0
- package/dist/chunk-GYDY3KX5.cjs.map +1 -0
- package/dist/chunk-HGEPXJDG.js +129 -0
- package/dist/chunk-HGEPXJDG.js.map +1 -0
- package/dist/chunk-IJASUMIQ.cjs +57 -0
- package/dist/chunk-IJASUMIQ.cjs.map +1 -0
- package/dist/chunk-IU3LS5FC.cjs +10 -0
- package/dist/chunk-IU3LS5FC.cjs.map +1 -0
- package/dist/chunk-IUKD54F7.js +8 -0
- package/dist/chunk-IUKD54F7.js.map +1 -0
- package/dist/chunk-IXV4UIF5.js +79 -0
- package/dist/chunk-IXV4UIF5.js.map +1 -0
- package/dist/chunk-KEI3EALJ.cjs +10 -0
- package/dist/chunk-KEI3EALJ.cjs.map +1 -0
- package/dist/chunk-LMN75W3W.cjs +87 -0
- package/dist/chunk-LMN75W3W.cjs.map +1 -0
- package/dist/chunk-MCLVWCOB.js +3 -0
- package/dist/chunk-MCLVWCOB.js.map +1 -0
- package/dist/chunk-MQ2XTH3S.cjs +87 -0
- package/dist/chunk-MQ2XTH3S.cjs.map +1 -0
- package/dist/chunk-QJ5XHA6S.cjs +95 -0
- package/dist/chunk-QJ5XHA6S.cjs.map +1 -0
- package/dist/chunk-RD5YUB2E.js +190 -0
- package/dist/chunk-RD5YUB2E.js.map +1 -0
- package/dist/chunk-RZ43WNHR.js +8 -0
- package/dist/chunk-RZ43WNHR.js.map +1 -0
- package/dist/chunk-RZP2ZUUX.cjs +252 -0
- package/dist/chunk-RZP2ZUUX.cjs.map +1 -0
- package/dist/chunk-T2GHJ5R4.js +241 -0
- package/dist/chunk-T2GHJ5R4.js.map +1 -0
- package/dist/chunk-TAMJ5HSR.cjs +137 -0
- package/dist/chunk-TAMJ5HSR.cjs.map +1 -0
- package/dist/chunk-TLACSVEZ.cjs +201 -0
- package/dist/chunk-TLACSVEZ.cjs.map +1 -0
- package/dist/chunk-UVWQWOLO.js +196 -0
- package/dist/chunk-UVWQWOLO.js.map +1 -0
- package/dist/chunk-V472N2PK.js +91 -0
- package/dist/chunk-V472N2PK.js.map +1 -0
- package/dist/chunk-V6O6SYAN.cjs +43 -0
- package/dist/chunk-V6O6SYAN.cjs.map +1 -0
- package/dist/chunk-VLSVL5N2.js +48 -0
- package/dist/chunk-VLSVL5N2.js.map +1 -0
- package/dist/chunk-WSMYH2IN.cjs +86 -0
- package/dist/chunk-WSMYH2IN.cjs.map +1 -0
- package/dist/chunk-XPXTXOYQ.js +81 -0
- package/dist/chunk-XPXTXOYQ.js.map +1 -0
- package/dist/chunk-YBFLWRO5.cjs +194 -0
- package/dist/chunk-YBFLWRO5.cjs.map +1 -0
- package/dist/chunk-YCBDEEAV.js +82 -0
- package/dist/chunk-YCBDEEAV.js.map +1 -0
- package/dist/chunk-YH5746OF.js +69 -0
- package/dist/chunk-YH5746OF.js.map +1 -0
- package/dist/chunk-YKZJRE32.cjs +50 -0
- package/dist/chunk-YKZJRE32.cjs.map +1 -0
- package/dist/declarative/index.cjs +19 -0
- package/dist/declarative/index.cjs.map +1 -0
- package/dist/declarative/index.d.cts +60 -0
- package/dist/declarative/index.d.ts +60 -0
- package/dist/declarative/index.js +10 -0
- package/dist/declarative/index.js.map +1 -0
- package/dist/errors-CjVz4W_5.d.cts +68 -0
- package/dist/errors-CjVz4W_5.d.ts +68 -0
- package/dist/index-C2vzfbBz.d.cts +57 -0
- package/dist/index-C2vzfbBz.d.ts +57 -0
- package/dist/index-D7-znzrc.d.ts +153 -0
- package/dist/index-DdYZeNIu.d.cts +153 -0
- package/dist/index.cjs +236 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.cjs +19 -0
- package/dist/mcp/index.cjs.map +1 -0
- package/dist/mcp/index.d.cts +72 -0
- package/dist/mcp/index.d.ts +72 -0
- package/dist/mcp/index.js +6 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/middleware/index.cjs +17 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +29 -0
- package/dist/middleware/index.d.ts +29 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/observability/index.cjs +29 -0
- package/dist/observability/index.cjs.map +1 -0
- package/dist/observability/index.d.cts +1 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +4 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/persistence/index.cjs +24 -0
- package/dist/persistence/index.cjs.map +1 -0
- package/dist/persistence/index.d.cts +51 -0
- package/dist/persistence/index.d.ts +51 -0
- package/dist/persistence/index.js +7 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/provider-CMAymr1b.d.cts +82 -0
- package/dist/provider-osAtfZ7x.d.ts +82 -0
- package/dist/providers/index.cjs +26 -0
- package/dist/providers/index.cjs.map +1 -0
- package/dist/providers/index.d.cts +107 -0
- package/dist/providers/index.d.ts +107 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/registry-CpO0yH5v.d.ts +57 -0
- package/dist/registry-D4fThGiN.d.cts +57 -0
- package/dist/skill-DfNChtJN.d.cts +45 -0
- package/dist/skill-DfNChtJN.d.ts +45 -0
- package/dist/skills/index.cjs +20 -0
- package/dist/skills/index.cjs.map +1 -0
- package/dist/skills/index.d.cts +30 -0
- package/dist/skills/index.d.ts +30 -0
- package/dist/skills/index.js +3 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/thread-CWVzTyti.d.ts +51 -0
- package/dist/thread-Dfo9LLf7.d.cts +51 -0
- package/dist/tool-BZg_znMZ.d.cts +50 -0
- package/dist/tool-CSCC87OD.d.ts +50 -0
- package/dist/tools/index.cjs +27 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +21 -0
- package/dist/tools/index.d.ts +21 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types-Cn1g9Tg4.d.cts +63 -0
- package/dist/types-Cn1g9Tg4.d.ts +63 -0
- package/dist/workflows/index.cjs +50 -0
- package/dist/workflows/index.cjs.map +1 -0
- package/dist/workflows/index.d.cts +182 -0
- package/dist/workflows/index.d.ts +182 -0
- package/dist/workflows/index.js +5 -0
- package/dist/workflows/index.js.map +1 -0
- package/package.json +145 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkMQ2XTH3S_cjs = require('./chunk-MQ2XTH3S.cjs');
|
|
4
|
+
var Ajv = require('ajv');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
|
|
9
|
+
|
|
10
|
+
var ajv = new Ajv__default.default({ allErrors: true, strict: false });
|
|
11
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
12
|
+
function compile(schema) {
|
|
13
|
+
let fn = cache.get(schema);
|
|
14
|
+
if (!fn) {
|
|
15
|
+
fn = ajv.compile(schema);
|
|
16
|
+
cache.set(schema, fn);
|
|
17
|
+
}
|
|
18
|
+
return fn;
|
|
19
|
+
}
|
|
20
|
+
function validateArgs(schema, args) {
|
|
21
|
+
const validate = compile(schema);
|
|
22
|
+
if (!validate(args)) {
|
|
23
|
+
const messages = (validate.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message}`).join("; ");
|
|
24
|
+
throw new chunkMQ2XTH3S_cjs.ValidationError(`Invalid arguments: ${messages}`, { errors: validate.errors });
|
|
25
|
+
}
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/tools/registry.ts
|
|
30
|
+
function namespacedName(tool) {
|
|
31
|
+
if (!tool.source || tool.source === "local") return tool.name;
|
|
32
|
+
return tool.name.startsWith(`${tool.source}.`) ? tool.name : `${tool.source}.${tool.name}`;
|
|
33
|
+
}
|
|
34
|
+
var ToolRegistry = class {
|
|
35
|
+
tools = /* @__PURE__ */ new Map();
|
|
36
|
+
disabled = /* @__PURE__ */ new Set();
|
|
37
|
+
disabledServers = /* @__PURE__ */ new Set();
|
|
38
|
+
constructor(tools = []) {
|
|
39
|
+
for (const t of tools) this.register(t);
|
|
40
|
+
}
|
|
41
|
+
/** Register a tool under its namespaced name. */
|
|
42
|
+
register(tool) {
|
|
43
|
+
this.tools.set(namespacedName(tool), tool);
|
|
44
|
+
}
|
|
45
|
+
/** Enable a tool by namespaced name. */
|
|
46
|
+
enable(name) {
|
|
47
|
+
this.disabled.delete(name);
|
|
48
|
+
}
|
|
49
|
+
/** Disable a single tool by namespaced name. (FR-012a) */
|
|
50
|
+
disable(name) {
|
|
51
|
+
this.disabled.add(name);
|
|
52
|
+
}
|
|
53
|
+
/** Disable every tool from an MCP server id. (FR-012a) */
|
|
54
|
+
disableServer(serverId) {
|
|
55
|
+
this.disabledServers.add(serverId);
|
|
56
|
+
}
|
|
57
|
+
/** Re-enable every tool from an MCP server id. */
|
|
58
|
+
enableServer(serverId) {
|
|
59
|
+
this.disabledServers.delete(serverId);
|
|
60
|
+
}
|
|
61
|
+
isEnabled(name, tool) {
|
|
62
|
+
if (tool.enabled === false) return false;
|
|
63
|
+
if (this.disabled.has(name)) return false;
|
|
64
|
+
if (tool.source && tool.source !== "local" && this.disabledServers.has(tool.source)) return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/** List currently enabled tools (those presented to the agent). */
|
|
68
|
+
list() {
|
|
69
|
+
return [...this.tools.entries()].filter(([name, tool]) => this.isEnabled(name, tool)).map(([, tool]) => tool);
|
|
70
|
+
}
|
|
71
|
+
/** Provider-facing specs for enabled tools. */
|
|
72
|
+
specs() {
|
|
73
|
+
return [...this.tools.entries()].filter(([name, tool]) => this.isEnabled(name, tool)).map(([name, tool]) => ({
|
|
74
|
+
name,
|
|
75
|
+
description: tool.description,
|
|
76
|
+
inputSchema: tool.inputSchema
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate and invoke a tool. Never throws for tool-level failures; instead
|
|
81
|
+
* returns a {@link ToolResult} with a typed error so the agent can self-correct.
|
|
82
|
+
* (FR-011a, FR-012, FR-012c)
|
|
83
|
+
*
|
|
84
|
+
* @param name - Namespaced tool name.
|
|
85
|
+
* @param args - Raw arguments from the model.
|
|
86
|
+
* @param timeoutMs - Optional per-call timeout.
|
|
87
|
+
*/
|
|
88
|
+
async invoke(name, args, timeoutMs) {
|
|
89
|
+
const tool = this.tools.get(name);
|
|
90
|
+
if (!tool || !this.isEnabled(name, tool)) {
|
|
91
|
+
return {
|
|
92
|
+
name,
|
|
93
|
+
error: new chunkMQ2XTH3S_cjs.ToolError(`Tool "${name}" not found or disabled`, "not-found", name)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
validateArgs(tool.inputSchema, args);
|
|
98
|
+
} catch (e) {
|
|
99
|
+
const ve = e;
|
|
100
|
+
return {
|
|
101
|
+
name,
|
|
102
|
+
error: new chunkMQ2XTH3S_cjs.ToolError(ve.message, "invalid-arguments", name, ve.details)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const value = await runWithTimeout(() => tool.run(args), timeoutMs, name);
|
|
107
|
+
return { name, value };
|
|
108
|
+
} catch (e) {
|
|
109
|
+
if (e instanceof chunkMQ2XTH3S_cjs.ToolError) return { name, error: e };
|
|
110
|
+
return {
|
|
111
|
+
name,
|
|
112
|
+
error: new chunkMQ2XTH3S_cjs.ToolError(e.message, "run-failure", name)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
async function runWithTimeout(fn, timeoutMs, name) {
|
|
118
|
+
if (!timeoutMs || timeoutMs <= 0) return fn();
|
|
119
|
+
let timer;
|
|
120
|
+
const timeout = new Promise((_, reject) => {
|
|
121
|
+
timer = setTimeout(
|
|
122
|
+
() => reject(new chunkMQ2XTH3S_cjs.ToolError(`Tool "${name}" timed out after ${timeoutMs}ms`, "timeout", name)),
|
|
123
|
+
timeoutMs
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
try {
|
|
127
|
+
return await Promise.race([fn(), timeout]);
|
|
128
|
+
} finally {
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
exports.ToolRegistry = ToolRegistry;
|
|
134
|
+
exports.namespacedName = namespacedName;
|
|
135
|
+
exports.validateArgs = validateArgs;
|
|
136
|
+
//# sourceMappingURL=chunk-TAMJ5HSR.cjs.map
|
|
137
|
+
//# sourceMappingURL=chunk-TAMJ5HSR.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools/validate.ts","../src/tools/registry.ts"],"names":["Ajv","ValidationError","ToolError"],"mappings":"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","file":"chunk-TAMJ5HSR.cjs","sourcesContent":["/**\n * JSON Schema validation for tool arguments, backed by Ajv. Invalid arguments are\n * rejected before a tool runs and reported as a typed error the agent can act on.\n * (FR-011)\n *\n * @packageDocumentation\n */\n\nimport Ajv, { type ValidateFunction } from \"ajv\";\nimport type { JSONSchema } from \"../core/types.js\";\nimport { ValidationError } from \"../core/errors.js\";\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\nconst cache = new WeakMap<JSONSchema, ValidateFunction>();\n\nfunction compile(schema: JSONSchema): ValidateFunction {\n\tlet fn = cache.get(schema);\n\tif (!fn) {\n\t\tfn = ajv.compile(schema);\n\t\tcache.set(schema, fn);\n\t}\n\treturn fn;\n}\n\n/**\n * Validate `args` against `schema`. Returns the args on success; throws a\n * {@link ValidationError} listing the failures otherwise.\n */\nexport function validateArgs<T = unknown>(schema: JSONSchema, args: unknown): T {\n\tconst validate = compile(schema);\n\tif (!validate(args)) {\n\t\tconst messages = (validate.errors ?? [])\n\t\t\t.map((e) => `${e.instancePath || \"(root)\"} ${e.message}`)\n\t\t\t.join(\"; \");\n\t\tthrow new ValidationError(`Invalid arguments: ${messages}`, { errors: validate.errors });\n\t}\n\treturn args as T;\n}\n","/**\n * Tool registry: registration, namespacing, granular enable/disable, and\n * validated invocation. Tools are addressed namespaced by source (`server.tool`)\n * so collisions across MCP servers and local code are impossible. (FR-012a, FR-014a)\n *\n * @packageDocumentation\n */\n\nimport type { Tool } from \"./tool.js\";\nimport type { ToolSpec } from \"../providers/provider.js\";\nimport { validateArgs } from \"./validate.js\";\nimport { ToolError, ValidationError } from \"../core/errors.js\";\n\n/** Result of invoking a tool. */\nexport interface ToolResult {\n\t/** The tool's (namespaced) name. */\n\tname: string;\n\t/** The returned value on success. */\n\tvalue?: unknown;\n\t/** A typed error on failure (fed back to the model for self-correction). */\n\terror?: ToolError;\n}\n\n/** Compute the namespaced address for a tool. */\nexport function namespacedName(tool: Pick<Tool, \"name\" | \"source\">): string {\n\tif (!tool.source || tool.source === \"local\") return tool.name;\n\t// Avoid double-prefixing if already namespaced.\n\treturn tool.name.startsWith(`${tool.source}.`) ? tool.name : `${tool.source}.${tool.name}`;\n}\n\n/** A registry of tools available to an agent. */\nexport class ToolRegistry {\n\tprivate readonly tools = new Map<string, Tool>();\n\tprivate readonly disabled = new Set<string>();\n\tprivate readonly disabledServers = new Set<string>();\n\n\tconstructor(tools: Tool[] = []) {\n\t\tfor (const t of tools) this.register(t);\n\t}\n\n\t/** Register a tool under its namespaced name. */\n\tregister(tool: Tool): void {\n\t\tthis.tools.set(namespacedName(tool), tool);\n\t}\n\n\t/** Enable a tool by namespaced name. */\n\tenable(name: string): void {\n\t\tthis.disabled.delete(name);\n\t}\n\n\t/** Disable a single tool by namespaced name. (FR-012a) */\n\tdisable(name: string): void {\n\t\tthis.disabled.add(name);\n\t}\n\n\t/** Disable every tool from an MCP server id. (FR-012a) */\n\tdisableServer(serverId: string): void {\n\t\tthis.disabledServers.add(serverId);\n\t}\n\n\t/** Re-enable every tool from an MCP server id. */\n\tenableServer(serverId: string): void {\n\t\tthis.disabledServers.delete(serverId);\n\t}\n\n\tprivate isEnabled(name: string, tool: Tool): boolean {\n\t\tif (tool.enabled === false) return false;\n\t\tif (this.disabled.has(name)) return false;\n\t\tif (tool.source && tool.source !== \"local\" && this.disabledServers.has(tool.source)) return false;\n\t\treturn true;\n\t}\n\n\t/** List currently enabled tools (those presented to the agent). */\n\tlist(): Tool[] {\n\t\treturn [...this.tools.entries()]\n\t\t\t.filter(([name, tool]) => this.isEnabled(name, tool))\n\t\t\t.map(([, tool]) => tool);\n\t}\n\n\t/** Provider-facing specs for enabled tools. */\n\tspecs(): ToolSpec[] {\n\t\treturn [...this.tools.entries()]\n\t\t\t.filter(([name, tool]) => this.isEnabled(name, tool))\n\t\t\t.map(([name, tool]) => ({\n\t\t\t\tname,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tinputSchema: tool.inputSchema,\n\t\t\t}));\n\t}\n\n\t/**\n\t * Validate and invoke a tool. Never throws for tool-level failures; instead\n\t * returns a {@link ToolResult} with a typed error so the agent can self-correct.\n\t * (FR-011a, FR-012, FR-012c)\n\t *\n\t * @param name - Namespaced tool name.\n\t * @param args - Raw arguments from the model.\n\t * @param timeoutMs - Optional per-call timeout.\n\t */\n\tasync invoke(name: string, args: unknown, timeoutMs?: number): Promise<ToolResult> {\n\t\tconst tool = this.tools.get(name);\n\t\tif (!tool || !this.isEnabled(name, tool)) {\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\terror: new ToolError(`Tool \"${name}\" not found or disabled`, \"not-found\", name),\n\t\t\t};\n\t\t}\n\t\ttry {\n\t\t\tvalidateArgs(tool.inputSchema, args);\n\t\t} catch (e) {\n\t\t\tconst ve = e as ValidationError;\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\terror: new ToolError(ve.message, \"invalid-arguments\", name, ve.details),\n\t\t\t};\n\t\t}\n\t\ttry {\n\t\t\tconst value = await runWithTimeout(() => tool.run(args), timeoutMs, name);\n\t\t\treturn { name, value };\n\t\t} catch (e) {\n\t\t\tif (e instanceof ToolError) return { name, error: e };\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\terror: new ToolError((e as Error).message, \"run-failure\", name),\n\t\t\t};\n\t\t}\n\t}\n}\n\nasync function runWithTimeout<T>(\n\tfn: () => Promise<T>,\n\ttimeoutMs: number | undefined,\n\tname: string,\n): Promise<T> {\n\tif (!timeoutMs || timeoutMs <= 0) return fn();\n\tlet timer: ReturnType<typeof setTimeout>;\n\tconst timeout = new Promise<never>((_, reject) => {\n\t\ttimer = setTimeout(\n\t\t\t() => reject(new ToolError(`Tool \"${name}\" timed out after ${timeoutMs}ms`, \"timeout\", name)),\n\t\t\ttimeoutMs,\n\t\t);\n\t});\n\ttry {\n\t\treturn await Promise.race([fn(), timeout]);\n\t} finally {\n\t\tclearTimeout(timer!);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkMQ2XTH3S_cjs = require('./chunk-MQ2XTH3S.cjs');
|
|
4
|
+
|
|
5
|
+
// src/providers/retry.ts
|
|
6
|
+
var DEFAULTS = {
|
|
7
|
+
maxRetries: 3,
|
|
8
|
+
baseDelayMs: 250,
|
|
9
|
+
maxDelayMs: 8e3
|
|
10
|
+
};
|
|
11
|
+
function sleep(ms) {
|
|
12
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
13
|
+
}
|
|
14
|
+
async function withRetry(fn, opts, retryAfterMs) {
|
|
15
|
+
const cfg = { ...DEFAULTS, ...opts };
|
|
16
|
+
let attempt = 0;
|
|
17
|
+
for (; ; ) {
|
|
18
|
+
try {
|
|
19
|
+
return await fn();
|
|
20
|
+
} catch (err) {
|
|
21
|
+
const isRetryable = err instanceof chunkMQ2XTH3S_cjs.ProviderError && err.retryable;
|
|
22
|
+
if (!isRetryable || attempt >= cfg.maxRetries) {
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
const serverDelay = retryAfterMs?.(err);
|
|
26
|
+
const backoff = Math.min(cfg.baseDelayMs * 2 ** attempt, cfg.maxDelayMs);
|
|
27
|
+
const jitter = Math.random() * cfg.baseDelayMs;
|
|
28
|
+
await sleep(serverDelay ?? backoff + jitter);
|
|
29
|
+
attempt++;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function providerErrorFromStatus(status, message) {
|
|
34
|
+
if (status === 429 || status >= 500) {
|
|
35
|
+
return new chunkMQ2XTH3S_cjs.ProviderError(message, "transient", { status });
|
|
36
|
+
}
|
|
37
|
+
if (status === 401 || status === 403) {
|
|
38
|
+
return new chunkMQ2XTH3S_cjs.ProviderError(message, "auth", { status });
|
|
39
|
+
}
|
|
40
|
+
return new chunkMQ2XTH3S_cjs.ProviderError(message, "client", { status });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/providers/openai-compatible.ts
|
|
44
|
+
function toOpenAIContent(parts) {
|
|
45
|
+
if (parts.every((p) => p.type === "text")) {
|
|
46
|
+
return parts.map((p) => p.text).join("");
|
|
47
|
+
}
|
|
48
|
+
return parts.map(
|
|
49
|
+
(p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image_url", image_url: { url: p.data } }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
function toOpenAIMessages(messages) {
|
|
53
|
+
return messages.map((m) => {
|
|
54
|
+
const msg = { role: m.role, content: toOpenAIContent(m.parts) };
|
|
55
|
+
if (m.toolCallId) msg.tool_call_id = m.toolCallId;
|
|
56
|
+
if (m.name) msg.name = m.name;
|
|
57
|
+
return msg;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function createOpenAICompatibleProvider(options) {
|
|
61
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
62
|
+
const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
63
|
+
async function authHeaders() {
|
|
64
|
+
const cred = await options.getCredential();
|
|
65
|
+
const headers = { "content-type": "application/json" };
|
|
66
|
+
if (cred) headers["authorization"] = `Bearer ${cred}`;
|
|
67
|
+
return headers;
|
|
68
|
+
}
|
|
69
|
+
function body(req, stream) {
|
|
70
|
+
return JSON.stringify({
|
|
71
|
+
model: options.capabilities.model,
|
|
72
|
+
messages: toOpenAIMessages(req.messages),
|
|
73
|
+
stream,
|
|
74
|
+
...req.tools && req.tools.length > 0 ? {
|
|
75
|
+
tools: req.tools.map((t) => ({
|
|
76
|
+
type: "function",
|
|
77
|
+
function: { name: t.name, description: t.description, parameters: t.inputSchema }
|
|
78
|
+
}))
|
|
79
|
+
} : {}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function parseToolCalls(raw) {
|
|
83
|
+
const calls = raw?.tool_calls;
|
|
84
|
+
if (!calls || calls.length === 0) return void 0;
|
|
85
|
+
return calls.map((c) => ({
|
|
86
|
+
id: c.id,
|
|
87
|
+
name: c.function.name,
|
|
88
|
+
arguments: safeJson(c.function.arguments)
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
async function generate(req) {
|
|
92
|
+
return withRetry(async () => {
|
|
93
|
+
let res;
|
|
94
|
+
try {
|
|
95
|
+
res = await doFetch(url, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: await authHeaders(),
|
|
98
|
+
body: body(req, false),
|
|
99
|
+
signal: req.signal
|
|
100
|
+
});
|
|
101
|
+
} catch (e) {
|
|
102
|
+
throw new chunkMQ2XTH3S_cjs.ProviderError(`Network error: ${e.message}`, "transient");
|
|
103
|
+
}
|
|
104
|
+
if (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);
|
|
105
|
+
let json;
|
|
106
|
+
try {
|
|
107
|
+
json = await res.json();
|
|
108
|
+
} catch {
|
|
109
|
+
throw new chunkMQ2XTH3S_cjs.ProviderError("Malformed provider response", "malformed");
|
|
110
|
+
}
|
|
111
|
+
const choice = json["choices"]?.[0];
|
|
112
|
+
if (!choice) throw new chunkMQ2XTH3S_cjs.ProviderError("Provider returned no choices", "malformed");
|
|
113
|
+
const message = choice.message;
|
|
114
|
+
return {
|
|
115
|
+
text: message["content"] ?? "",
|
|
116
|
+
reasoning: options.capabilities.supportsReasoning ? message["reasoning"] ?? void 0 : void 0,
|
|
117
|
+
toolCalls: parseToolCalls(message)
|
|
118
|
+
};
|
|
119
|
+
}, options.retry);
|
|
120
|
+
}
|
|
121
|
+
async function* generateStream(req) {
|
|
122
|
+
let res;
|
|
123
|
+
try {
|
|
124
|
+
res = await doFetch(url, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: await authHeaders(),
|
|
127
|
+
body: body(req, true),
|
|
128
|
+
signal: req.signal
|
|
129
|
+
});
|
|
130
|
+
} catch (e) {
|
|
131
|
+
throw new chunkMQ2XTH3S_cjs.ProviderError(`Network error: ${e.message}`, "transient");
|
|
132
|
+
}
|
|
133
|
+
if (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);
|
|
134
|
+
if (!res.body) throw new chunkMQ2XTH3S_cjs.ProviderError("Provider returned no stream body", "malformed");
|
|
135
|
+
const reader = res.body.getReader();
|
|
136
|
+
const decoder = new TextDecoder();
|
|
137
|
+
let buffer = "";
|
|
138
|
+
let text = "";
|
|
139
|
+
let reasoning = "";
|
|
140
|
+
for (; ; ) {
|
|
141
|
+
const { value, done } = await reader.read();
|
|
142
|
+
if (done) break;
|
|
143
|
+
buffer += decoder.decode(value, { stream: true });
|
|
144
|
+
const lines = buffer.split("\n");
|
|
145
|
+
buffer = lines.pop() ?? "";
|
|
146
|
+
for (const line of lines) {
|
|
147
|
+
const trimmed = line.trim();
|
|
148
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
149
|
+
const data = trimmed.slice(5).trim();
|
|
150
|
+
if (data === "[DONE]") continue;
|
|
151
|
+
const parsed = safeJson(data);
|
|
152
|
+
const delta = parsed?.choices?.[0]?.delta;
|
|
153
|
+
if (delta?.content) {
|
|
154
|
+
text += delta.content;
|
|
155
|
+
yield { type: "text", text: delta.content };
|
|
156
|
+
}
|
|
157
|
+
if (delta?.reasoning && options.capabilities.supportsReasoning) {
|
|
158
|
+
reasoning += delta.reasoning;
|
|
159
|
+
yield { type: "reasoning", text: delta.reasoning };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
yield {
|
|
164
|
+
type: "done",
|
|
165
|
+
response: { text, reasoning: reasoning || void 0 }
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return { name: "openai-compatible", capabilities: options.capabilities, generate, generateStream };
|
|
169
|
+
}
|
|
170
|
+
function safeJson(s) {
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(s);
|
|
173
|
+
} catch {
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/providers/copilot.ts
|
|
179
|
+
var DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
|
|
180
|
+
function createCopilotProvider(options) {
|
|
181
|
+
const inner = createOpenAICompatibleProvider({
|
|
182
|
+
baseUrl: options.baseUrl ?? DEFAULT_COPILOT_BASE_URL,
|
|
183
|
+
getCredential: options.getCredential,
|
|
184
|
+
capabilities: options.capabilities,
|
|
185
|
+
retry: options.retry,
|
|
186
|
+
fetchImpl: options.fetchImpl
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
name: "copilot",
|
|
190
|
+
capabilities: inner.capabilities,
|
|
191
|
+
generate: inner.generate.bind(inner),
|
|
192
|
+
generateStream: inner.generateStream.bind(inner)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
exports.createCopilotProvider = createCopilotProvider;
|
|
197
|
+
exports.createOpenAICompatibleProvider = createOpenAICompatibleProvider;
|
|
198
|
+
exports.providerErrorFromStatus = providerErrorFromStatus;
|
|
199
|
+
exports.withRetry = withRetry;
|
|
200
|
+
//# sourceMappingURL=chunk-TLACSVEZ.cjs.map
|
|
201
|
+
//# sourceMappingURL=chunk-TLACSVEZ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/retry.ts","../src/providers/openai-compatible.ts","../src/providers/copilot.ts"],"names":["ProviderError"],"mappings":"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","file":"chunk-TLACSVEZ.cjs","sourcesContent":["/**\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`. (FR-006)\n *\n * @packageDocumentation\n */\n\nimport type { ModelCapabilities, Message, ContentPart } 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} from \"./provider.js\";\nimport { withRetry, providerErrorFromStatus, type RetryOptions } from \"./retry.js\";\n\n/** Options for {@link createOpenAICompatibleProvider}. */\nexport interface OpenAICompatibleProviderOptions extends CredentialSource {\n\t/** Base URL of the OpenAI-compatible API, e.g. `http://localhost:1234/v1`. */\n\tbaseUrl: string;\n\t/** Per-model capability configuration. */\n\tcapabilities: ModelCapabilities;\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}\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 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.toolCallId) msg.tool_call_id = m.toolCallId;\n\t\tif (m.name) msg.name = m.name;\n\t\treturn msg;\n\t});\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\n\tasync function authHeaders(): Promise<Record<string, string>> {\n\t\tconst cred = await options.getCredential();\n\t\tconst headers: Record<string, string> = { \"content-type\": \"application/json\" };\n\t\tif (cred) headers[\"authorization\"] = `Bearer ${cred}`;\n\t\treturn headers;\n\t}\n\n\tfunction body(req: GenerateRequest, stream: boolean): string {\n\t\treturn JSON.stringify({\n\t\t\tmodel: options.capabilities.model,\n\t\t\tmessages: toOpenAIMessages(req.messages),\n\t\t\tstream,\n\t\t\t...(req.tools && req.tools.length > 0\n\t\t\t\t? {\n\t\t\t\t\ttools: req.tools.map((t) => ({\n\t\t\t\t\t\ttype: \"function\",\n\t\t\t\t\t\tfunction: { name: t.name, description: t.description, parameters: t.inputSchema },\n\t\t\t\t\t})),\n\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t}\n\n\tfunction parseToolCalls(raw: unknown): ToolCall[] | undefined {\n\t\tconst calls = (raw as { tool_calls?: Array<{ id: string; function: { name: string; arguments: string } }> })\n\t\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: 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\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),\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 = (json[\"choices\"] as Array<{ message: Record<string, unknown> }>)?.[0];\n\t\t\tif (!choice) throw new ProviderError(\"Provider returned no choices\", \"malformed\");\n\t\t\tconst message = choice.message;\n\t\t\treturn {\n\t\t\t\ttext: (message[\"content\"] as string) ?? \"\",\n\t\t\t\treasoning: options.capabilities.supportsReasoning\n\t\t\t\t\t? ((message[\"reasoning\"] as string) ?? undefined)\n\t\t\t\t\t: undefined,\n\t\t\t\ttoolCalls: parseToolCalls(message),\n\t\t\t};\n\t\t}, options.retry);\n\t}\n\n\tasync function* generateStream(req: GenerateRequest): AsyncIterable<GenerateChunk> {\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),\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 reader = res.body.getReader();\n\t\tconst decoder = new TextDecoder();\n\t\tlet buffer = \"\";\n\t\tlet text = \"\";\n\t\tlet reasoning = \"\";\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| { choices?: Array<{ delta?: { content?: string; reasoning?: string } }> }\n\t\t\t\t\t| undefined;\n\t\t\t\tconst delta = parsed?.choices?.[0]?.delta;\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 && options.capabilities.supportsReasoning) {\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}\n\t\t}\n\t\tyield {\n\t\t\ttype: \"done\",\n\t\t\tresponse: { text, reasoning: reasoning || undefined },\n\t\t};\n\t}\n\n\treturn { name: \"openai-compatible\", capabilities: options.capabilities, generate, generateStream };\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 { ModelCapabilities } from \"../core/types.js\";\nimport type { Provider, CredentialSource } 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/** Options for {@link createCopilotProvider}. */\nexport interface CopilotProviderOptions extends CredentialSource {\n\t/** Per-model capability configuration. */\n\tcapabilities: ModelCapabilities;\n\t/** Override the Copilot base URL if needed. */\n\tbaseUrl?: 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\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 */\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\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\tgenerate: inner.generate.bind(inner),\n\t\tgenerateStream: inner.generateStream.bind(inner),\n\t};\n}\n"]}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ProviderError } from './chunk-IXV4UIF5.js';
|
|
2
|
+
|
|
3
|
+
// src/providers/retry.ts
|
|
4
|
+
var DEFAULTS = {
|
|
5
|
+
maxRetries: 3,
|
|
6
|
+
baseDelayMs: 250,
|
|
7
|
+
maxDelayMs: 8e3
|
|
8
|
+
};
|
|
9
|
+
function sleep(ms) {
|
|
10
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
11
|
+
}
|
|
12
|
+
async function withRetry(fn, opts, retryAfterMs) {
|
|
13
|
+
const cfg = { ...DEFAULTS, ...opts };
|
|
14
|
+
let attempt = 0;
|
|
15
|
+
for (; ; ) {
|
|
16
|
+
try {
|
|
17
|
+
return await fn();
|
|
18
|
+
} catch (err) {
|
|
19
|
+
const isRetryable = err instanceof ProviderError && err.retryable;
|
|
20
|
+
if (!isRetryable || attempt >= cfg.maxRetries) {
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
const serverDelay = retryAfterMs?.(err);
|
|
24
|
+
const backoff = Math.min(cfg.baseDelayMs * 2 ** attempt, cfg.maxDelayMs);
|
|
25
|
+
const jitter = Math.random() * cfg.baseDelayMs;
|
|
26
|
+
await sleep(serverDelay ?? backoff + jitter);
|
|
27
|
+
attempt++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function providerErrorFromStatus(status, message) {
|
|
32
|
+
if (status === 429 || status >= 500) {
|
|
33
|
+
return new ProviderError(message, "transient", { status });
|
|
34
|
+
}
|
|
35
|
+
if (status === 401 || status === 403) {
|
|
36
|
+
return new ProviderError(message, "auth", { status });
|
|
37
|
+
}
|
|
38
|
+
return new ProviderError(message, "client", { status });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/providers/openai-compatible.ts
|
|
42
|
+
function toOpenAIContent(parts) {
|
|
43
|
+
if (parts.every((p) => p.type === "text")) {
|
|
44
|
+
return parts.map((p) => p.text).join("");
|
|
45
|
+
}
|
|
46
|
+
return parts.map(
|
|
47
|
+
(p) => p.type === "text" ? { type: "text", text: p.text } : { type: "image_url", image_url: { url: p.data } }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function toOpenAIMessages(messages) {
|
|
51
|
+
return messages.map((m) => {
|
|
52
|
+
const msg = { role: m.role, content: toOpenAIContent(m.parts) };
|
|
53
|
+
if (m.toolCallId) msg.tool_call_id = m.toolCallId;
|
|
54
|
+
if (m.name) msg.name = m.name;
|
|
55
|
+
return msg;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function createOpenAICompatibleProvider(options) {
|
|
59
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
60
|
+
const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
61
|
+
async function authHeaders() {
|
|
62
|
+
const cred = await options.getCredential();
|
|
63
|
+
const headers = { "content-type": "application/json" };
|
|
64
|
+
if (cred) headers["authorization"] = `Bearer ${cred}`;
|
|
65
|
+
return headers;
|
|
66
|
+
}
|
|
67
|
+
function body(req, stream) {
|
|
68
|
+
return JSON.stringify({
|
|
69
|
+
model: options.capabilities.model,
|
|
70
|
+
messages: toOpenAIMessages(req.messages),
|
|
71
|
+
stream,
|
|
72
|
+
...req.tools && req.tools.length > 0 ? {
|
|
73
|
+
tools: req.tools.map((t) => ({
|
|
74
|
+
type: "function",
|
|
75
|
+
function: { name: t.name, description: t.description, parameters: t.inputSchema }
|
|
76
|
+
}))
|
|
77
|
+
} : {}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function parseToolCalls(raw) {
|
|
81
|
+
const calls = raw?.tool_calls;
|
|
82
|
+
if (!calls || calls.length === 0) return void 0;
|
|
83
|
+
return calls.map((c) => ({
|
|
84
|
+
id: c.id,
|
|
85
|
+
name: c.function.name,
|
|
86
|
+
arguments: safeJson(c.function.arguments)
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
async function generate(req) {
|
|
90
|
+
return withRetry(async () => {
|
|
91
|
+
let res;
|
|
92
|
+
try {
|
|
93
|
+
res = await doFetch(url, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: await authHeaders(),
|
|
96
|
+
body: body(req, false),
|
|
97
|
+
signal: req.signal
|
|
98
|
+
});
|
|
99
|
+
} catch (e) {
|
|
100
|
+
throw new ProviderError(`Network error: ${e.message}`, "transient");
|
|
101
|
+
}
|
|
102
|
+
if (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);
|
|
103
|
+
let json;
|
|
104
|
+
try {
|
|
105
|
+
json = await res.json();
|
|
106
|
+
} catch {
|
|
107
|
+
throw new ProviderError("Malformed provider response", "malformed");
|
|
108
|
+
}
|
|
109
|
+
const choice = json["choices"]?.[0];
|
|
110
|
+
if (!choice) throw new ProviderError("Provider returned no choices", "malformed");
|
|
111
|
+
const message = choice.message;
|
|
112
|
+
return {
|
|
113
|
+
text: message["content"] ?? "",
|
|
114
|
+
reasoning: options.capabilities.supportsReasoning ? message["reasoning"] ?? void 0 : void 0,
|
|
115
|
+
toolCalls: parseToolCalls(message)
|
|
116
|
+
};
|
|
117
|
+
}, options.retry);
|
|
118
|
+
}
|
|
119
|
+
async function* generateStream(req) {
|
|
120
|
+
let res;
|
|
121
|
+
try {
|
|
122
|
+
res = await doFetch(url, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: await authHeaders(),
|
|
125
|
+
body: body(req, true),
|
|
126
|
+
signal: req.signal
|
|
127
|
+
});
|
|
128
|
+
} catch (e) {
|
|
129
|
+
throw new ProviderError(`Network error: ${e.message}`, "transient");
|
|
130
|
+
}
|
|
131
|
+
if (!res.ok) throw providerErrorFromStatus(res.status, `Provider returned ${res.status}`);
|
|
132
|
+
if (!res.body) throw new ProviderError("Provider returned no stream body", "malformed");
|
|
133
|
+
const reader = res.body.getReader();
|
|
134
|
+
const decoder = new TextDecoder();
|
|
135
|
+
let buffer = "";
|
|
136
|
+
let text = "";
|
|
137
|
+
let reasoning = "";
|
|
138
|
+
for (; ; ) {
|
|
139
|
+
const { value, done } = await reader.read();
|
|
140
|
+
if (done) break;
|
|
141
|
+
buffer += decoder.decode(value, { stream: true });
|
|
142
|
+
const lines = buffer.split("\n");
|
|
143
|
+
buffer = lines.pop() ?? "";
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
const trimmed = line.trim();
|
|
146
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
147
|
+
const data = trimmed.slice(5).trim();
|
|
148
|
+
if (data === "[DONE]") continue;
|
|
149
|
+
const parsed = safeJson(data);
|
|
150
|
+
const delta = parsed?.choices?.[0]?.delta;
|
|
151
|
+
if (delta?.content) {
|
|
152
|
+
text += delta.content;
|
|
153
|
+
yield { type: "text", text: delta.content };
|
|
154
|
+
}
|
|
155
|
+
if (delta?.reasoning && options.capabilities.supportsReasoning) {
|
|
156
|
+
reasoning += delta.reasoning;
|
|
157
|
+
yield { type: "reasoning", text: delta.reasoning };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
yield {
|
|
162
|
+
type: "done",
|
|
163
|
+
response: { text, reasoning: reasoning || void 0 }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return { name: "openai-compatible", capabilities: options.capabilities, generate, generateStream };
|
|
167
|
+
}
|
|
168
|
+
function safeJson(s) {
|
|
169
|
+
try {
|
|
170
|
+
return JSON.parse(s);
|
|
171
|
+
} catch {
|
|
172
|
+
return void 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/providers/copilot.ts
|
|
177
|
+
var DEFAULT_COPILOT_BASE_URL = "https://api.githubcopilot.com";
|
|
178
|
+
function createCopilotProvider(options) {
|
|
179
|
+
const inner = createOpenAICompatibleProvider({
|
|
180
|
+
baseUrl: options.baseUrl ?? DEFAULT_COPILOT_BASE_URL,
|
|
181
|
+
getCredential: options.getCredential,
|
|
182
|
+
capabilities: options.capabilities,
|
|
183
|
+
retry: options.retry,
|
|
184
|
+
fetchImpl: options.fetchImpl
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
name: "copilot",
|
|
188
|
+
capabilities: inner.capabilities,
|
|
189
|
+
generate: inner.generate.bind(inner),
|
|
190
|
+
generateStream: inner.generateStream.bind(inner)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { createCopilotProvider, createOpenAICompatibleProvider, providerErrorFromStatus, withRetry };
|
|
195
|
+
//# sourceMappingURL=chunk-UVWQWOLO.js.map
|
|
196
|
+
//# sourceMappingURL=chunk-UVWQWOLO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/retry.ts","../src/providers/openai-compatible.ts","../src/providers/copilot.ts"],"names":[],"mappings":"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","file":"chunk-UVWQWOLO.js","sourcesContent":["/**\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`. (FR-006)\n *\n * @packageDocumentation\n */\n\nimport type { ModelCapabilities, Message, ContentPart } 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} from \"./provider.js\";\nimport { withRetry, providerErrorFromStatus, type RetryOptions } from \"./retry.js\";\n\n/** Options for {@link createOpenAICompatibleProvider}. */\nexport interface OpenAICompatibleProviderOptions extends CredentialSource {\n\t/** Base URL of the OpenAI-compatible API, e.g. `http://localhost:1234/v1`. */\n\tbaseUrl: string;\n\t/** Per-model capability configuration. */\n\tcapabilities: ModelCapabilities;\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}\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 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.toolCallId) msg.tool_call_id = m.toolCallId;\n\t\tif (m.name) msg.name = m.name;\n\t\treturn msg;\n\t});\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\n\tasync function authHeaders(): Promise<Record<string, string>> {\n\t\tconst cred = await options.getCredential();\n\t\tconst headers: Record<string, string> = { \"content-type\": \"application/json\" };\n\t\tif (cred) headers[\"authorization\"] = `Bearer ${cred}`;\n\t\treturn headers;\n\t}\n\n\tfunction body(req: GenerateRequest, stream: boolean): string {\n\t\treturn JSON.stringify({\n\t\t\tmodel: options.capabilities.model,\n\t\t\tmessages: toOpenAIMessages(req.messages),\n\t\t\tstream,\n\t\t\t...(req.tools && req.tools.length > 0\n\t\t\t\t? {\n\t\t\t\t\ttools: req.tools.map((t) => ({\n\t\t\t\t\t\ttype: \"function\",\n\t\t\t\t\t\tfunction: { name: t.name, description: t.description, parameters: t.inputSchema },\n\t\t\t\t\t})),\n\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t}\n\n\tfunction parseToolCalls(raw: unknown): ToolCall[] | undefined {\n\t\tconst calls = (raw as { tool_calls?: Array<{ id: string; function: { name: string; arguments: string } }> })\n\t\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: 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\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),\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 = (json[\"choices\"] as Array<{ message: Record<string, unknown> }>)?.[0];\n\t\t\tif (!choice) throw new ProviderError(\"Provider returned no choices\", \"malformed\");\n\t\t\tconst message = choice.message;\n\t\t\treturn {\n\t\t\t\ttext: (message[\"content\"] as string) ?? \"\",\n\t\t\t\treasoning: options.capabilities.supportsReasoning\n\t\t\t\t\t? ((message[\"reasoning\"] as string) ?? undefined)\n\t\t\t\t\t: undefined,\n\t\t\t\ttoolCalls: parseToolCalls(message),\n\t\t\t};\n\t\t}, options.retry);\n\t}\n\n\tasync function* generateStream(req: GenerateRequest): AsyncIterable<GenerateChunk> {\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),\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 reader = res.body.getReader();\n\t\tconst decoder = new TextDecoder();\n\t\tlet buffer = \"\";\n\t\tlet text = \"\";\n\t\tlet reasoning = \"\";\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| { choices?: Array<{ delta?: { content?: string; reasoning?: string } }> }\n\t\t\t\t\t| undefined;\n\t\t\t\tconst delta = parsed?.choices?.[0]?.delta;\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 && options.capabilities.supportsReasoning) {\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}\n\t\t}\n\t\tyield {\n\t\t\ttype: \"done\",\n\t\t\tresponse: { text, reasoning: reasoning || undefined },\n\t\t};\n\t}\n\n\treturn { name: \"openai-compatible\", capabilities: options.capabilities, generate, generateStream };\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 { ModelCapabilities } from \"../core/types.js\";\nimport type { Provider, CredentialSource } 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/** Options for {@link createCopilotProvider}. */\nexport interface CopilotProviderOptions extends CredentialSource {\n\t/** Per-model capability configuration. */\n\tcapabilities: ModelCapabilities;\n\t/** Override the Copilot base URL if needed. */\n\tbaseUrl?: 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\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 */\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\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\tgenerate: inner.generate.bind(inner),\n\t\tgenerateStream: inner.generateStream.bind(inner),\n\t};\n}\n"]}
|