@vesk/agentic 0.2.11

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 (62) hide show
  1. package/README.md +53 -0
  2. package/dist/checkpoints.d.ts +155 -0
  3. package/dist/checkpoints.d.ts.map +1 -0
  4. package/dist/checkpoints.js +394 -0
  5. package/dist/config.d.ts +57 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +399 -0
  8. package/dist/context.d.ts +21 -0
  9. package/dist/context.d.ts.map +1 -0
  10. package/dist/context.js +64 -0
  11. package/dist/dev-api.d.ts +85 -0
  12. package/dist/dev-api.d.ts.map +1 -0
  13. package/dist/dev-api.js +942 -0
  14. package/dist/index.d.ts +25 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +23 -0
  17. package/dist/loop.d.ts +156 -0
  18. package/dist/loop.d.ts.map +1 -0
  19. package/dist/loop.js +178 -0
  20. package/dist/permissions.d.ts +14 -0
  21. package/dist/permissions.d.ts.map +1 -0
  22. package/dist/permissions.js +74 -0
  23. package/dist/plugin.d.ts +38 -0
  24. package/dist/plugin.d.ts.map +1 -0
  25. package/dist/plugin.js +28 -0
  26. package/dist/providers/anthropic.d.ts +13 -0
  27. package/dist/providers/anthropic.d.ts.map +1 -0
  28. package/dist/providers/anthropic.js +100 -0
  29. package/dist/providers/google.d.ts +12 -0
  30. package/dist/providers/google.d.ts.map +1 -0
  31. package/dist/providers/google.js +87 -0
  32. package/dist/providers/ollama.d.ts +11 -0
  33. package/dist/providers/ollama.d.ts.map +1 -0
  34. package/dist/providers/ollama.js +61 -0
  35. package/dist/providers/openai.d.ts +12 -0
  36. package/dist/providers/openai.d.ts.map +1 -0
  37. package/dist/providers/openai.js +193 -0
  38. package/dist/providers/registry.d.ts +7 -0
  39. package/dist/providers/registry.d.ts.map +1 -0
  40. package/dist/providers/registry.js +33 -0
  41. package/dist/providers/types.d.ts +28 -0
  42. package/dist/providers/types.d.ts.map +1 -0
  43. package/dist/providers/types.js +15 -0
  44. package/dist/slash.d.ts +18 -0
  45. package/dist/slash.d.ts.map +1 -0
  46. package/dist/slash.js +77 -0
  47. package/dist/tools/browser.d.ts +3 -0
  48. package/dist/tools/browser.d.ts.map +1 -0
  49. package/dist/tools/browser.js +289 -0
  50. package/dist/tools/command.d.ts +14 -0
  51. package/dist/tools/command.d.ts.map +1 -0
  52. package/dist/tools/command.js +55 -0
  53. package/dist/tools/fs.d.ts +10 -0
  54. package/dist/tools/fs.d.ts.map +1 -0
  55. package/dist/tools/fs.js +142 -0
  56. package/dist/tools/vesk.d.ts +22 -0
  57. package/dist/tools/vesk.d.ts.map +1 -0
  58. package/dist/tools/vesk.js +828 -0
  59. package/dist/tools/web.d.ts +3 -0
  60. package/dist/tools/web.d.ts.map +1 -0
  61. package/dist/tools/web.js +111 -0
  62. package/package.json +47 -0
@@ -0,0 +1,100 @@
1
+ const ANTHROPIC_FALLBACK_MODELS = ['claude-sonnet-4-6', 'claude-opus-4-6', 'claude-haiku-4-5'];
2
+ export async function listModels(options = {}) {
3
+ const baseUrl = options.baseUrl ?? 'https://api.anthropic.com/v1';
4
+ try {
5
+ const res = await fetch(`${baseUrl}/models`, {
6
+ headers: {
7
+ 'x-api-key': options.apiKey ?? '',
8
+ 'anthropic-version': '2023-06-01',
9
+ },
10
+ });
11
+ if (!res.ok)
12
+ return [...ANTHROPIC_FALLBACK_MODELS];
13
+ const data = (await res.json());
14
+ if (Array.isArray(data.data) && data.data.length > 0) {
15
+ const ids = data.data.map((m) => m.id).filter((v) => typeof v === 'string' && v.length > 0);
16
+ if (ids.length > 0)
17
+ return ids;
18
+ }
19
+ return [...ANTHROPIC_FALLBACK_MODELS];
20
+ }
21
+ catch {
22
+ return [...ANTHROPIC_FALLBACK_MODELS];
23
+ }
24
+ }
25
+ export function anthropicProvider(options) {
26
+ const { apiKey, model = 'claude-sonnet-4-6', maxTokens = 1024, baseUrl = 'https://api.anthropic.com/v1' } = options;
27
+ return {
28
+ listModels: (opts = {}) => listModels({ apiKey: opts.apiKey ?? apiKey, baseUrl: opts.baseUrl ?? baseUrl }),
29
+ async complete({ messages, tools }) {
30
+ const { system, messages: anthropicMessages } = toAnthropic(messages);
31
+ const res = await fetch(`${baseUrl}/messages`, {
32
+ method: 'POST',
33
+ headers: {
34
+ 'content-type': 'application/json',
35
+ 'x-api-key': apiKey,
36
+ 'anthropic-version': '2023-06-01',
37
+ },
38
+ body: JSON.stringify({
39
+ model,
40
+ max_tokens: maxTokens,
41
+ system,
42
+ messages: anthropicMessages,
43
+ tools: tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.parameters })),
44
+ }),
45
+ });
46
+ if (!res.ok)
47
+ throw new Error(`Anthropic request failed: ${res.status} ${await res.text()}`);
48
+ const data = (await res.json());
49
+ const toolUses = data.content.filter((b) => b.type === 'tool_use');
50
+ if (toolUses.length > 0) {
51
+ return {
52
+ kind: 'tool_calls',
53
+ toolCalls: toolUses.map((b) => ({ id: b.id, name: b.name, arguments: b.input })),
54
+ };
55
+ }
56
+ const text = data.content
57
+ .filter((b) => b.type === 'text')
58
+ .map((b) => b.text)
59
+ .join('');
60
+ return { kind: 'message', content: text };
61
+ },
62
+ };
63
+ }
64
+ function toAnthropic(messages) {
65
+ let system;
66
+ const out = [];
67
+ for (const m of messages) {
68
+ if (m.role === 'system') {
69
+ system = m.content;
70
+ continue;
71
+ }
72
+ if (m.role === 'user') {
73
+ out.push({ role: 'user', content: m.content });
74
+ continue;
75
+ }
76
+ if (m.role === 'assistant') {
77
+ if (m.toolCalls && m.toolCalls.length > 0) {
78
+ const blocks = [];
79
+ if (m.content)
80
+ blocks.push({ type: 'text', text: m.content });
81
+ for (const c of m.toolCalls)
82
+ blocks.push({ type: 'tool_use', id: c.id, name: c.name, input: c.arguments });
83
+ out.push({ role: 'assistant', content: blocks });
84
+ }
85
+ else {
86
+ out.push({ role: 'assistant', content: m.content ?? '' });
87
+ }
88
+ continue;
89
+ }
90
+ const block = { type: 'tool_result', tool_use_id: m.toolCallId, content: m.content };
91
+ const last = out[out.length - 1];
92
+ if (last && last.role === 'user' && Array.isArray(last.content)) {
93
+ last.content.push(block);
94
+ }
95
+ else {
96
+ out.push({ role: 'user', content: [block] });
97
+ }
98
+ }
99
+ return { system, messages: out };
100
+ }
@@ -0,0 +1,12 @@
1
+ import type { Provider } from '../loop.js';
2
+ export interface GoogleOptions {
3
+ apiKey: string;
4
+ model?: string;
5
+ baseUrl?: string;
6
+ }
7
+ export declare function listModels(options?: {
8
+ apiKey?: string;
9
+ baseUrl?: string;
10
+ }): Promise<string[]>;
11
+ export declare function googleProvider(options: GoogleOptions): Provider;
12
+ //# sourceMappingURL=google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkD,QAAQ,EAAY,MAAM,YAAY,CAAC;AAErG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,UAAU,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBvG;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ,CA6B/D"}
@@ -0,0 +1,87 @@
1
+ const GOOGLE_FALLBACK_MODELS = ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'];
2
+ export async function listModels(options = {}) {
3
+ const baseUrl = options.baseUrl ?? 'https://generativelanguage.googleapis.com';
4
+ try {
5
+ const res = await fetch(`${baseUrl}/v1beta/models?key=${encodeURIComponent(options.apiKey ?? '')}`);
6
+ if (!res.ok)
7
+ return [...GOOGLE_FALLBACK_MODELS];
8
+ const data = (await res.json());
9
+ if (Array.isArray(data.models) && data.models.length > 0) {
10
+ const ids = data.models
11
+ .map((m) => m.name)
12
+ .filter((v) => typeof v === 'string' && v.length > 0)
13
+ .map((n) => (n.startsWith('models/') ? n.slice(7) : n));
14
+ if (ids.length > 0)
15
+ return ids;
16
+ }
17
+ return [...GOOGLE_FALLBACK_MODELS];
18
+ }
19
+ catch {
20
+ return [...GOOGLE_FALLBACK_MODELS];
21
+ }
22
+ }
23
+ export function googleProvider(options) {
24
+ const { apiKey, model = 'gemini-2.0-flash', baseUrl = 'https://generativelanguage.googleapis.com' } = options;
25
+ return {
26
+ listModels: (opts = {}) => listModels({ apiKey: opts.apiKey ?? apiKey, baseUrl: opts.baseUrl ?? baseUrl }),
27
+ async complete({ messages, tools }) {
28
+ const { systemInstruction, contents } = toGoogle(messages);
29
+ const body = { contents, systemInstruction };
30
+ if (tools.length > 0) {
31
+ body.tools = [{ functionDeclarations: tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })) }];
32
+ }
33
+ const res = await fetch(`${baseUrl}/v1beta/models/${model}:generateContent?key=${encodeURIComponent(apiKey)}`, {
34
+ method: 'POST',
35
+ headers: { 'content-type': 'application/json' },
36
+ body: JSON.stringify(body),
37
+ });
38
+ if (!res.ok)
39
+ throw new Error(`Google request failed: ${res.status} ${await res.text()}`);
40
+ const data = (await res.json());
41
+ const candidate = data.candidates?.[0];
42
+ const parts = candidate?.content?.parts ?? [];
43
+ const calls = [];
44
+ let text = '';
45
+ for (const p of parts) {
46
+ if (p.functionCall)
47
+ calls.push({ id: `${p.functionCall.name}-${calls.length}`, name: p.functionCall.name, arguments: p.functionCall.args ?? {} });
48
+ if (p.text)
49
+ text += p.text;
50
+ }
51
+ if (calls.length > 0)
52
+ return { kind: 'tool_calls', toolCalls: calls };
53
+ return { kind: 'message', content: text };
54
+ },
55
+ };
56
+ }
57
+ function toGoogle(messages) {
58
+ let systemInstruction;
59
+ const contents = [];
60
+ for (const m of messages) {
61
+ if (m.role === 'system') {
62
+ systemInstruction = { parts: [{ text: m.content }] };
63
+ continue;
64
+ }
65
+ if (m.role === 'user') {
66
+ contents.push({ role: 'user', parts: [{ text: m.content }] });
67
+ continue;
68
+ }
69
+ if (m.role === 'assistant') {
70
+ if (m.toolCalls && m.toolCalls.length > 0) {
71
+ const parts = [];
72
+ if (m.content)
73
+ parts.push({ text: m.content });
74
+ for (const c of m.toolCalls)
75
+ parts.push({ functionCall: { name: c.name, args: c.arguments } });
76
+ contents.push({ role: 'model', parts });
77
+ }
78
+ else {
79
+ contents.push({ role: 'model', parts: [{ text: m.content ?? '' }] });
80
+ }
81
+ continue;
82
+ }
83
+ // tool -> user with functionResponse
84
+ contents.push({ role: 'user', parts: [{ functionResponse: { name: m.name, response: { content: m.content } } }] });
85
+ }
86
+ return { systemInstruction, contents };
87
+ }
@@ -0,0 +1,11 @@
1
+ import type { Provider } from '../loop.js';
2
+ export interface OllamaOptions {
3
+ model?: string;
4
+ baseUrl?: string;
5
+ }
6
+ export declare function listModels(options?: {
7
+ apiKey?: string;
8
+ baseUrl?: string;
9
+ }): Promise<string[]>;
10
+ export declare function ollamaProvider(options?: OllamaOptions): Provider;
11
+ //# sourceMappingURL=ollama.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/providers/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkD,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3F,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,UAAU,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBvG;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,aAAkB,GAAG,QAAQ,CA8BpE"}
@@ -0,0 +1,61 @@
1
+ const OLLAMA_FALLBACK_MODELS = ['llama3.1', 'mistral', 'gemma2'];
2
+ export async function listModels(options = {}) {
3
+ const baseUrl = options.baseUrl ?? 'http://localhost:11434';
4
+ try {
5
+ const res = await fetch(`${baseUrl}/api/tags`);
6
+ if (!res.ok)
7
+ return [...OLLAMA_FALLBACK_MODELS];
8
+ const data = (await res.json());
9
+ if (Array.isArray(data.models) && data.models.length > 0) {
10
+ const ids = data.models
11
+ .map((m) => m.name ?? m.model ?? '')
12
+ .filter((v) => typeof v === 'string' && v.length > 0);
13
+ if (ids.length > 0)
14
+ return ids;
15
+ }
16
+ return [...OLLAMA_FALLBACK_MODELS];
17
+ }
18
+ catch {
19
+ return [...OLLAMA_FALLBACK_MODELS];
20
+ }
21
+ }
22
+ export function ollamaProvider(options = {}) {
23
+ const { model = 'llama3.1', baseUrl = 'http://localhost:11434' } = options;
24
+ return {
25
+ listModels: (opts = {}) => listModels({ baseUrl: opts.baseUrl ?? baseUrl }),
26
+ async complete({ messages, tools }) {
27
+ const res = await fetch(`${baseUrl}/api/chat`, {
28
+ method: 'POST',
29
+ headers: { 'content-type': 'application/json' },
30
+ body: JSON.stringify({
31
+ model,
32
+ messages: messages.map(toOllamaMessage),
33
+ tools: tools.length > 0 ? tools : undefined,
34
+ stream: false,
35
+ }),
36
+ });
37
+ if (!res.ok)
38
+ throw new Error(`Ollama request failed: ${res.status} ${await res.text()}`);
39
+ const data = (await res.json());
40
+ if (data.message?.tool_calls && data.message.tool_calls.length > 0) {
41
+ return {
42
+ kind: 'tool_calls',
43
+ toolCalls: data.message.tool_calls.map((c) => ({
44
+ id: `${c.function.name}-${Math.random().toString(36).slice(2, 6)}`,
45
+ name: c.function.name,
46
+ arguments: c.function.arguments,
47
+ })),
48
+ };
49
+ }
50
+ return { kind: 'message', content: data.message?.content ?? '' };
51
+ },
52
+ };
53
+ }
54
+ function toOllamaMessage(m) {
55
+ if (m.role === 'tool')
56
+ return { role: 'tool', content: m.content, tool_call_id: m.toolCallId };
57
+ if (m.role === 'assistant' && m.toolCalls) {
58
+ return { role: 'assistant', content: m.content ?? '', tool_calls: m.toolCalls.map((c) => ({ function: { name: c.name, arguments: c.arguments } })) };
59
+ }
60
+ return { role: m.role, content: m.content };
61
+ }
@@ -0,0 +1,12 @@
1
+ import type { Provider } from '../loop.js';
2
+ export interface OpenAiOptions {
3
+ apiKey: string;
4
+ model?: string;
5
+ baseUrl?: string;
6
+ }
7
+ export declare function listModels(options?: {
8
+ apiKey?: string;
9
+ baseUrl?: string;
10
+ }): Promise<string[]>;
11
+ export declare function openAiProvider(options: OpenAiOptions): Provider;
12
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkD,QAAQ,EAAyB,MAAM,YAAY,CAAC;AAElH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,UAAU,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBvG;AAMD,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ,CAgI/D"}
@@ -0,0 +1,193 @@
1
+ const OPENAI_FALLBACK_MODELS = ['gpt-4o-mini', 'gpt-4o', 'gpt-3.5-turbo'];
2
+ export async function listModels(options = {}) {
3
+ const baseUrl = options.baseUrl ?? 'https://api.openai.com/v1';
4
+ try {
5
+ const res = await fetch(`${baseUrl}/models`, {
6
+ headers: { authorization: `Bearer ${options.apiKey ?? ''}` },
7
+ });
8
+ if (!res.ok)
9
+ return [...OPENAI_FALLBACK_MODELS];
10
+ const data = (await res.json());
11
+ if (Array.isArray(data.data) && data.data.length > 0) {
12
+ const ids = data.data.map((m) => m.id).filter((v) => typeof v === 'string' && v.length > 0);
13
+ if (ids.length > 0)
14
+ return ids;
15
+ }
16
+ return [...OPENAI_FALLBACK_MODELS];
17
+ }
18
+ catch {
19
+ return [...OPENAI_FALLBACK_MODELS];
20
+ }
21
+ }
22
+ function sanitizeToolName(name) {
23
+ return name.replace(/\./g, '__').replace(/[^a-zA-Z0-9_-]/g, '_');
24
+ }
25
+ export function openAiProvider(options) {
26
+ const { apiKey, model = 'gpt-4o-mini', baseUrl = 'https://api.openai.com/v1' } = options;
27
+ return {
28
+ listModels: (opts = {}) => listModels({ apiKey: opts.apiKey ?? apiKey, baseUrl: opts.baseUrl ?? baseUrl }),
29
+ async complete({ messages, tools }) {
30
+ // Sanitize tool names for OpenAI (only alphanum _ - allowed)
31
+ const nameMap = new Map();
32
+ const sanitizedTools = tools.map((t) => {
33
+ const sane = sanitizeToolName(t.name);
34
+ nameMap.set(sane, t.name);
35
+ return { name: sane, description: t.description, parameters: t.parameters };
36
+ });
37
+ const res = await fetch(`${baseUrl}/chat/completions`, {
38
+ method: 'POST',
39
+ headers: {
40
+ 'content-type': 'application/json',
41
+ authorization: `Bearer ${apiKey}`,
42
+ },
43
+ body: JSON.stringify({
44
+ model,
45
+ messages: messages.map(toOpenAiMessage),
46
+ tools: sanitizedTools.length > 0 ? sanitizedTools.map((t) => ({ type: 'function', function: t })) : undefined,
47
+ }),
48
+ });
49
+ if (!res.ok)
50
+ throw new Error(`OpenAI request failed: ${res.status} ${await res.text()}`);
51
+ const data = (await res.json());
52
+ const msg = data.choices[0]?.message;
53
+ if (msg?.tool_calls && msg.tool_calls.length > 0) {
54
+ return {
55
+ kind: 'tool_calls',
56
+ toolCalls: msg.tool_calls.map((c) => ({
57
+ id: c.id,
58
+ name: nameMap.get(c.function.name) || c.function.name,
59
+ arguments: JSON.parse(c.function.arguments),
60
+ })),
61
+ };
62
+ }
63
+ return { kind: 'message', content: msg?.content ?? '' };
64
+ },
65
+ async *completeStream({ messages, tools }) {
66
+ // Sanitize tool names for OpenAI (only alphanum _ - allowed)
67
+ const nameMap = new Map();
68
+ const sanitizedTools = tools.map((t) => {
69
+ const sane = sanitizeToolName(t.name);
70
+ nameMap.set(sane, t.name);
71
+ return { name: sane, description: t.description, parameters: t.parameters };
72
+ });
73
+ const res = await fetch(`${baseUrl}/chat/completions`, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'content-type': 'application/json',
77
+ authorization: `Bearer ${apiKey}`,
78
+ },
79
+ body: JSON.stringify({
80
+ model,
81
+ messages: messages.map(toOpenAiMessage),
82
+ tools: sanitizedTools.length > 0 ? sanitizedTools.map((t) => ({ type: 'function', function: t })) : undefined,
83
+ stream: true,
84
+ }),
85
+ });
86
+ if (!res.ok)
87
+ throw new Error(`OpenAI request failed: ${res.status} ${await res.text()}`);
88
+ if (!res.body)
89
+ throw new Error('OpenAI stream: no response body');
90
+ const decoder = new TextDecoder();
91
+ const reader = res.body.getReader();
92
+ let buffer = '';
93
+ let textContent = '';
94
+ const toolBuffer = [];
95
+ try {
96
+ while (true) {
97
+ const { done, value } = await reader.read();
98
+ if (done)
99
+ break;
100
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, '\n');
101
+ let nl = buffer.indexOf('\n');
102
+ while (nl !== -1) {
103
+ const line = buffer.slice(0, nl).replace(/\r$/, '');
104
+ buffer = buffer.slice(nl + 1);
105
+ if (line.startsWith('data: ')) {
106
+ const payload = line.slice(6);
107
+ if (payload === '[DONE]')
108
+ break;
109
+ let chunk;
110
+ try {
111
+ chunk = JSON.parse(payload);
112
+ }
113
+ catch {
114
+ nl = buffer.indexOf('\n');
115
+ continue;
116
+ }
117
+ const delta = chunk.choices?.[0]?.delta;
118
+ if (!delta) {
119
+ nl = buffer.indexOf('\n');
120
+ continue;
121
+ }
122
+ if (typeof delta.content === 'string' && delta.content.length > 0) {
123
+ textContent += delta.content;
124
+ yield { kind: 'delta', content: delta.content };
125
+ }
126
+ if (Array.isArray(delta.tool_calls)) {
127
+ for (const tc of delta.tool_calls) {
128
+ const at = toolBuffer[tc.index] ?? { id: '', index: tc.index, name: '', arguments: '' };
129
+ if (tc.id)
130
+ at.id = tc.id;
131
+ if (tc.function?.name)
132
+ at.name += tc.function.name;
133
+ if (tc.function?.arguments)
134
+ at.arguments += tc.function.arguments;
135
+ toolBuffer[tc.index] = at;
136
+ }
137
+ }
138
+ }
139
+ nl = buffer.indexOf('\n');
140
+ }
141
+ }
142
+ }
143
+ finally {
144
+ reader.releaseLock();
145
+ }
146
+ const toolCalls = toolBuffer
147
+ .filter((t) => t.name.length > 0)
148
+ .map((t) => ({
149
+ id: t.id || 'call_' + t.index,
150
+ name: nameMap.get(t.name) || t.name,
151
+ arguments: parseToolArguments(t.arguments),
152
+ }));
153
+ if (toolCalls.length > 0) {
154
+ yield { kind: 'tool_calls', toolCalls };
155
+ }
156
+ else {
157
+ yield { kind: 'message', content: textContent };
158
+ }
159
+ },
160
+ };
161
+ }
162
+ function parseToolArguments(json) {
163
+ if (!json)
164
+ return {};
165
+ try {
166
+ return JSON.parse(json);
167
+ }
168
+ catch {
169
+ return {};
170
+ }
171
+ }
172
+ function toOpenAiMessage(m) {
173
+ switch (m.role) {
174
+ case 'tool':
175
+ return { role: 'tool', tool_call_id: m.toolCallId, content: m.content };
176
+ case 'assistant':
177
+ return {
178
+ role: 'assistant',
179
+ content: m.content,
180
+ ...(m.toolCalls
181
+ ? {
182
+ tool_calls: m.toolCalls.map((c) => ({
183
+ id: c.id,
184
+ type: 'function',
185
+ function: { name: c.name, arguments: JSON.stringify(c.arguments) },
186
+ })),
187
+ }
188
+ : {}),
189
+ };
190
+ default:
191
+ return { role: m.role, content: m.content };
192
+ }
193
+ }
@@ -0,0 +1,7 @@
1
+ import type { Provider } from '../loop.js';
2
+ import type { ProviderConfig, ProviderFactory, ProviderName } from './types.js';
3
+ export declare function registerProvider(name: ProviderName, factory: ProviderFactory): void;
4
+ export declare function getProviderFactory(name: ProviderName): ProviderFactory | undefined;
5
+ export declare function createProvider(config: ProviderConfig): Provider;
6
+ export declare function listProviders(): ProviderName[];
7
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAIhF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAEnF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,GAAG,SAAS,CAElF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAwB/D;AAED,wBAAgB,aAAa,IAAI,YAAY,EAAE,CAE9C"}
@@ -0,0 +1,33 @@
1
+ const registry = new Map();
2
+ export function registerProvider(name, factory) {
3
+ registry.set(name, factory);
4
+ }
5
+ export function getProviderFactory(name) {
6
+ return registry.get(name);
7
+ }
8
+ export function createProvider(config) {
9
+ // Lazy dispatch — avoids importing SDKs or provider modules when not needed.
10
+ // Each provider module is imported only when its name is requested.
11
+ const name = config.provider;
12
+ const factory = registry.get(name);
13
+ if (factory)
14
+ return factory(config);
15
+ // Built-in lazy factories (import dynamically to keep bundle lean)
16
+ // Synchronous fallback: create directly without registry for core providers.
17
+ // This avoids async import for the common case; callers needing lazy can
18
+ // pre-register via registerProvider.
19
+ if (name === 'openai') {
20
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
21
+ // Use dynamic import via function to avoid top-level side effects.
22
+ // For sync path we instantiate inline via require-like dynamic.
23
+ // But to keep zero deps and simple, we import synchronously here
24
+ // via a helper that will be replaced by the actual provider module.
25
+ // Fallback: throw with guidance if not registered; caller should import
26
+ // the specific provider first.
27
+ throw new Error(`Provider "${name}" not registered. Import " @vesk/agentic/src/providers/openai.js" and call registerProvider("openai", openAiProvider) or use createProviderWithImport.`);
28
+ }
29
+ throw new Error(`Unknown provider "${String(name)}". Supported: ${['openai', 'anthropic', 'google', 'ollama'].join(', ')}`);
30
+ }
31
+ export function listProviders() {
32
+ return [...registry.keys()];
33
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Provider types and config — fetch-only, zero deps.
3
+ */
4
+ import type { CompletionRequest, CompletionResponse } from '../loop.js';
5
+ import type { Provider as BaseProvider } from '../loop.js';
6
+ export interface ListModelsOptions {
7
+ apiKey?: string;
8
+ baseUrl?: string;
9
+ }
10
+ export interface Provider extends BaseProvider {
11
+ listModels?(options: ListModelsOptions): Promise<string[]>;
12
+ }
13
+ export interface ProviderWithModels extends Provider {
14
+ complete(request: CompletionRequest): Promise<CompletionResponse>;
15
+ listModels?(options: ListModelsOptions): Promise<string[]>;
16
+ }
17
+ export type ProviderName = 'openai' | 'anthropic' | 'google' | 'ollama' | 'opencode' | 'opencode-go' | 'openrouter' | 'loopers' | 'custom';
18
+ export interface ProviderConfig {
19
+ provider: ProviderName;
20
+ model: string;
21
+ apiKey: string;
22
+ baseUrl?: string;
23
+ maxTokens?: number;
24
+ }
25
+ export declare const SUPPORTED_PROVIDERS: ProviderName[];
26
+ export declare function describeProvider(name: ProviderName): string;
27
+ export type ProviderFactory = (config: ProviderConfig) => Provider;
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAS,SAAQ,YAAY;IAC5C,UAAU,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ;IAClD,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClE,UAAU,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5D;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3I,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,YAAY,EAA8G,CAAC;AAE7J,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAc3D;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,QAAQ,CAAC"}
@@ -0,0 +1,15 @@
1
+ export const SUPPORTED_PROVIDERS = ['openai', 'anthropic', 'google', 'ollama', 'opencode', 'opencode-go', 'openrouter', 'loopers', 'custom'];
2
+ export function describeProvider(name) {
3
+ switch (name) {
4
+ case 'openai': return 'OpenAI (api.openai.com)';
5
+ case 'anthropic': return 'Anthropic Claude (api.anthropic.com)';
6
+ case 'google': return 'Google Gemini (generativelanguage.googleapis.com)';
7
+ case 'ollama': return 'Ollama (localhost:11434, no key)';
8
+ case 'opencode': return 'OpenCode Zen (opencode.ai/zen/v1)';
9
+ case 'opencode-go': return 'OpenCode Go (opencode.ai/zen/go/v1)';
10
+ case 'openrouter': return 'OpenRouter (openrouter.ai/api/v1)';
11
+ case 'loopers': return 'Loopers (localhost:8080, lp-xxx)';
12
+ case 'custom': return 'Custom (any OpenAI-compatible baseUrl)';
13
+ default: return String(name);
14
+ }
15
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Slash command parser for devtool agentic tab.
3
+ * Pure string ops, no regex.
4
+ */
5
+ export type SlashCmd = 'provider' | 'model' | 'models' | 'tools' | 'commands' | 'tool' | 'mode' | 'clear' | 'help' | 'history' | 'rollback' | 'config' | 'checkpoint';
6
+ export declare const SLASH_COMMANDS: Array<{
7
+ name: string;
8
+ description: string;
9
+ usage: string;
10
+ }>;
11
+ export declare function parseSlash(input: string): {
12
+ cmd: SlashCmd;
13
+ args: string[];
14
+ raw: string;
15
+ } | null;
16
+ export declare function helpText(): string;
17
+ export declare function isSlash(input: string): boolean;
18
+ //# sourceMappingURL=slash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash.d.ts","sourceRoot":"","sources":["../src/slash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAChB,UAAU,GACV,OAAO,GACP,QAAQ,GACR,OAAO,GACP,UAAU,GACV,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,UAAU,GACV,QAAQ,GACR,YAAY,CAAC;AAEjB,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CActF,CAAC;AAEF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0B/F;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAIjC;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C"}