@lowire/loop 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -1 +1 @@
1
- ## tiny loop
1
+ ## lowire agentic loop
package/index.d.ts CHANGED
@@ -14,5 +14,6 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- export * from './lib/loop';
18
17
  export * from './lib/types';
18
+ export * from './lib/loop';
19
+ export * from './lib/mcp';
package/index.js CHANGED
@@ -14,4 +14,5 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- module.exports = require('./lib/loop');
17
+ module.exports.Loop = require('./lib/loop').Loop;
18
+ module.exports.createMcpTools = require('./lib/mcp').createMcpTools;
package/index.mjs CHANGED
@@ -15,3 +15,4 @@
15
15
  */
16
16
 
17
17
  export * from './lib/loop.js';
18
+ export * from './lib/mcp.js';
package/lib/cache.d.ts CHANGED
@@ -14,5 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import type * as types from './types';
17
- import type { LoopOptions } from './loop';
18
- export declare function cachedComplete(provider: types.Provider, conversation: types.Conversation, options: LoopOptions): ReturnType<types.Provider['complete']>;
17
+ type ReplayCaches = {
18
+ input: types.ReplayCache;
19
+ output: types.ReplayCache;
20
+ secrets: Record<string, string>;
21
+ };
22
+ export declare function cachedComplete(provider: types.Provider, conversation: types.Conversation, caches: ReplayCaches | undefined, options: types.CompletionOptions): ReturnType<types.Provider['complete']>;
23
+ export {};
package/lib/cache.js CHANGED
@@ -20,23 +20,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.cachedComplete = cachedComplete;
22
22
  const crypto_1 = __importDefault(require("crypto"));
23
- async function cachedComplete(provider, conversation, options) {
24
- const caches = options.caches;
25
- const secrets = options.secrets || {};
23
+ async function cachedComplete(provider, conversation, caches, options) {
26
24
  if (!caches)
27
25
  return await provider.complete(conversation, options);
26
+ const secrets = caches.secrets || {};
28
27
  const c = hideSecrets(conversation, secrets);
29
28
  const key = calculateSha1(JSON.stringify(c));
30
- if (!process.env.TL_NO_CACHE && caches.before[key]) {
31
- caches.after[key] = caches.before[key];
32
- return unhideSecrets(caches.before[key] ?? caches.after[key], secrets);
29
+ if (!process.env.LOWIRE_NO_CACHE && caches.input[key]) {
30
+ caches.output[key] = caches.input[key];
31
+ return unhideSecrets(caches.input[key] ?? caches.output[key], secrets);
33
32
  }
34
- if (!process.env.TL_NO_CACHE && caches.after[key])
35
- return unhideSecrets(caches.after[key], secrets);
36
- if (process.env.TL_FORCE_CACHE)
33
+ if (!process.env.LOWIRE_NO_CACHE && caches.output[key])
34
+ return unhideSecrets(caches.output[key], secrets);
35
+ if (process.env.LOWIRE_FORCE_CACHE)
37
36
  throw new Error('Cache missing but TL_FORCE_CACHE is set' + JSON.stringify(conversation, null, 2));
38
37
  const result = await provider.complete(conversation, options);
39
- caches.after[key] = hideSecrets(result, secrets);
38
+ caches.output[key] = hideSecrets(result, secrets);
40
39
  return result;
41
40
  }
42
41
  function hideSecrets(conversation, secrets) {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export declare function jsx(tag: string, props: Record<string, any> | null): string;
17
+ export declare const jsxs: typeof jsx;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Microsoft Corporation.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.jsxs = void 0;
19
+ exports.jsx = jsx;
20
+ function jsx(tag, props) {
21
+ const { children, ...rest } = props || {};
22
+ const attrs = Object.entries(rest);
23
+ const childArray = (Array.isArray(children) ? children.flat() : (children ? [children] : [])).filter(a => a && !!a.trim());
24
+ const lines = [`${tag}:`];
25
+ for (const [k, v] of attrs)
26
+ lines.push(` ${k}: ${v}`);
27
+ for (const child of childArray) {
28
+ const indented = child.split('\n').map((line) => ` ${line}`).join('\n');
29
+ lines.push(indented);
30
+ }
31
+ return lines.join('\n');
32
+ }
33
+ exports.jsxs = jsx;
package/lib/loop.d.ts CHANGED
@@ -14,34 +14,25 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import type * as types from './types';
17
- export type Logger = (category: string, text: string, details?: string) => void;
18
- type Sizes = {
19
- headers: number;
20
- messages: number;
21
- toolsResults: number;
22
- };
23
17
  export type LoopOptions = types.CompletionOptions & {
24
18
  tools?: types.Tool[];
25
19
  callTool?: types.ToolCallback;
26
20
  maxTurns?: number;
27
21
  resultSchema?: types.Schema;
28
- logger?: Logger;
29
- caches?: types.ReplayCaches;
30
- secrets?: Record<string, string>;
31
- onBeforeTurn?: (parms: {
32
- turn: number;
33
- conversation: types.Conversation;
34
- sizes: Sizes;
35
- totalUsage: types.Usage;
36
- }) => Promise<'stop' | undefined | void>;
22
+ cache?: {
23
+ messages: types.ReplayCache;
24
+ secrets: Record<string, string>;
25
+ };
26
+ summarize?: boolean;
37
27
  };
38
28
  export declare class Loop {
39
29
  private _provider;
40
30
  private _loopOptions;
31
+ private _cacheOutput;
41
32
  constructor(loopName: 'openai' | 'github' | 'anthropic' | 'google', options: LoopOptions);
42
33
  run<T>(task: string, runOptions?: Omit<LoopOptions, 'model'> & {
43
34
  model?: string;
44
- }): Promise<T | undefined>;
45
- private _sizes;
35
+ }): Promise<T>;
36
+ private _summarizeConversation;
37
+ cache(): types.ReplayCache;
46
38
  }
47
- export {};
package/lib/loop.js CHANGED
@@ -18,23 +18,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.Loop = void 0;
19
19
  const registry_1 = require("./providers/registry");
20
20
  const cache_1 = require("./cache");
21
+ const summary_1 = require("./summary");
21
22
  class Loop {
22
23
  _provider;
23
24
  _loopOptions;
25
+ _cacheOutput = {};
24
26
  constructor(loopName, options) {
25
27
  this._provider = (0, registry_1.getProvider)(loopName);
26
28
  this._loopOptions = options;
27
29
  }
28
30
  async run(task, runOptions = {}) {
29
31
  const options = { ...this._loopOptions, ...runOptions };
30
- const allTools = [
31
- ...(options.tools || []),
32
- {
33
- name: 'report_result',
34
- description: 'Report the result of the task.',
35
- inputSchema: options.resultSchema ?? defaultResultSchema,
36
- },
37
- ];
32
+ const allTools = [...options.tools || []];
33
+ allTools.push({
34
+ name: 'report_result',
35
+ description: 'Report the result of the task.',
36
+ inputSchema: options.resultSchema ?? defaultResultSchema,
37
+ });
38
38
  const conversation = {
39
39
  systemPrompt,
40
40
  messages: [
@@ -42,98 +42,86 @@ class Loop {
42
42
  ],
43
43
  tools: allTools,
44
44
  };
45
- const log = options.logger ?? (() => { });
45
+ const debug = options.debug;
46
46
  const totalUsage = { input: 0, output: 0 };
47
- log('loop:loop', `Starting ${this._provider.name} loop`, task);
47
+ debug?.('lowire:loop')(`Starting ${this._provider.name} loop`, task);
48
48
  const maxTurns = options.maxTurns || 100;
49
49
  for (let turn = 0; turn < maxTurns; ++turn) {
50
- log('loop:turn', `${turn + 1} of (max ${maxTurns})`);
51
- const status = await options.onBeforeTurn?.({ turn, conversation, sizes: this._sizes(conversation), totalUsage });
52
- if (status === 'stop')
53
- return undefined;
54
- const { result: assistantMessage, usage } = await (0, cache_1.cachedComplete)(this._provider, conversation, options);
50
+ debug?.('lowire:loop')(`Turn ${turn + 1} of (max ${maxTurns})`);
51
+ const caches = options.cache ? {
52
+ input: options.cache.messages,
53
+ output: this._cacheOutput,
54
+ secrets: options.cache.secrets
55
+ } : undefined;
56
+ const summarizedConversation = options.summarize ? this._summarizeConversation(task, conversation, options) : conversation;
57
+ debug?.('lowire:loop')(`Request`, JSON.stringify({ ...summarizedConversation, tools: `${summarizedConversation.tools.length} tools` }, null, 2));
58
+ const { result: assistantMessage, usage } = await (0, cache_1.cachedComplete)(this._provider, summarizedConversation, caches, options);
59
+ const text = assistantMessage.content.filter(part => part.type === 'text').map(part => part.text).join('\n');
60
+ debug?.('lowire:loop')('Usage', `input: ${usage.input}, output: ${usage.output}`);
61
+ debug?.('lowire:loop')('Assistant', text, JSON.stringify(assistantMessage.content, null, 2));
55
62
  totalUsage.input += usage.input;
56
63
  totalUsage.output += usage.output;
57
64
  conversation.messages.push(assistantMessage);
58
- const text = assistantMessage.content.filter(part => part.type === 'text').map(part => part.text).join('\n');
59
65
  const toolCalls = assistantMessage.content.filter(part => part.type === 'tool_call');
60
- log('loop:usage', `input: ${usage.input}, output: ${usage.output}`);
61
- log('loop:assistant', text, JSON.stringify(assistantMessage.content, null, 2));
62
66
  if (toolCalls.length === 0) {
63
- conversation.messages.push({
64
- role: 'user',
65
- content: `Tool call expected. Call the "report_result" tool when the task is complete.`,
66
- });
67
+ assistantMessage.toolError = 'Error: tool call is expected in every assistant message. Call the "report_result" tool when the task is complete.';
67
68
  continue;
68
69
  }
69
- const toolResults = [];
70
70
  for (const toolCall of toolCalls) {
71
- const { name, arguments: args, id } = toolCall;
72
- log('loop:call-tool', name, JSON.stringify(args, null, 2));
71
+ const { name, arguments: args } = toolCall;
72
+ debug?.('lowire:loop')('Call tool', name, JSON.stringify(args, null, 2));
73
73
  if (name === 'report_result')
74
74
  return args;
75
75
  try {
76
76
  const result = await options.callTool({
77
77
  name,
78
- arguments: args,
78
+ arguments: {
79
+ ...args,
80
+ _meta: {
81
+ 'dev.lowire/history': true,
82
+ 'dev.lowire/state': true,
83
+ }
84
+ }
79
85
  });
80
86
  const text = result.content.filter(part => part.type === 'text').map(part => part.text).join('\n');
81
- log('loop:tool-result', text, JSON.stringify(result, null, 2));
82
- toolResults.push({
83
- toolName: name,
84
- toolCallId: id,
85
- result,
86
- });
87
+ debug?.('lowire:loop')('Tool result', text, JSON.stringify(result, null, 2));
88
+ toolCall.result = result;
87
89
  }
88
90
  catch (error) {
89
91
  const errorMessage = `Error while executing tool "${name}": ${error instanceof Error ? error.message : String(error)}\n\nPlease try to recover and complete the task.`;
90
- log('loop:tool-error', errorMessage, String(error));
91
- toolResults.push({
92
- toolName: name,
93
- toolCallId: id,
94
- result: {
95
- content: [{ type: 'text', text: errorMessage }],
96
- isError: true,
97
- }
98
- });
92
+ debug?.('lowire:loop')('Tool error', errorMessage, String(error));
93
+ toolCall.result = {
94
+ content: [{ type: 'text', text: errorMessage }],
95
+ isError: true,
96
+ };
99
97
  // Skip remaining tool calls for this iteration
100
98
  for (const remainingToolCall of toolCalls.slice(toolCalls.indexOf(toolCall) + 1)) {
101
- toolResults.push({
102
- toolName: remainingToolCall.name,
103
- toolCallId: remainingToolCall.id,
104
- result: {
105
- content: [{ type: 'text', text: `This tool call is skipped due to previous error.` }],
106
- isError: true,
107
- }
108
- });
99
+ remainingToolCall.result = {
100
+ content: [{ type: 'text', text: `This tool call is skipped due to previous error.` }],
101
+ isError: true,
102
+ };
109
103
  }
110
104
  break;
111
105
  }
112
106
  }
113
- for (const toolResult of toolResults) {
114
- conversation.messages.push({
115
- role: 'tool_result',
116
- ...toolResult,
117
- });
118
- }
119
107
  }
108
+ if (options.summarize)
109
+ return this._summarizeConversation(task, conversation, options);
120
110
  throw new Error('Failed to perform step, max attempts reached');
121
111
  }
122
- _sizes(conversation) {
123
- const headers = conversation.systemPrompt.length + JSON.stringify(conversation.tools).length;
124
- const messages = JSON.stringify(conversation.messages).length;
125
- let toolsResults = 0;
126
- for (const message of conversation.messages) {
127
- if (message.role !== 'tool_result')
128
- continue;
129
- toolsResults += JSON.stringify(message.result).length;
130
- }
112
+ _summarizeConversation(task, conversation, options) {
113
+ const { summary, lastMessage } = (0, summary_1.summarizeConversation)(task, conversation, options);
131
114
  return {
132
- headers,
133
- messages,
134
- toolsResults,
115
+ ...conversation,
116
+ messages: [
117
+ { role: 'user', content: summary },
118
+ ...lastMessage ? [lastMessage] : [],
119
+ ],
135
120
  };
136
121
  }
122
+ cache() {
123
+ return this._cacheOutput;
124
+ }
137
125
  }
138
126
  exports.Loop = Loop;
139
127
  const defaultResultSchema = {
@@ -14,20 +14,21 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import type * as types from '../types';
17
- export type Endpoint = {
18
- baseUrl: string;
19
- apiKey: string;
20
- headers: Record<string, string>;
17
+ export type McpServer = {
18
+ command: string;
19
+ args?: string[];
20
+ cwd?: string;
21
+ stderr?: 'pipe' | 'inherit' | 'ignore';
22
+ env?: Record<string, string>;
21
23
  };
22
- export declare class OpenAICompletions implements types.Provider {
23
- readonly name: string;
24
- private _endpoint;
25
- endpoint(): Promise<Endpoint>;
26
- connect(): Promise<Endpoint>;
27
- complete(conversation: types.Conversation, options: types.CompletionOptions & {
28
- injectIntent?: boolean;
29
- }): Promise<{
30
- result: types.AssistantMessage;
31
- usage: types.Usage;
32
- }>;
33
- }
24
+ type ToolFilter = (string | RegExp)[];
25
+ type ToolSupport = {
26
+ close: () => Promise<void>;
27
+ tools: types.Tool[];
28
+ callTool: types.ToolCallback;
29
+ };
30
+ export declare function createMcpTools(servers: Record<string, McpServer>, options?: {
31
+ rootDir?: string;
32
+ toolFilter?: ToolFilter;
33
+ }): Promise<ToolSupport>;
34
+ export {};
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Microsoft Corporation.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ var __importDefault = (this && this.__importDefault) || function (mod) {
51
+ return (mod && mod.__esModule) ? mod : { "default": mod };
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.createMcpTools = createMcpTools;
55
+ const url_1 = __importDefault(require("url"));
56
+ async function connectToMcpServer(server, options) {
57
+ const { Client } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/index.js')));
58
+ const { StdioClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/stdio.js')));
59
+ const { ListRootsRequestSchema } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
60
+ const transport = new StdioClientTransport(server);
61
+ const capabilities = {};
62
+ if (options?.rootDir)
63
+ capabilities['roots'] = {};
64
+ const client = new Client({ name: 'test', version: '1.0.0' }, { capabilities });
65
+ if (options?.rootDir) {
66
+ client.setRequestHandler(ListRootsRequestSchema, async () => {
67
+ return {
68
+ roots: [{ name: 'workspace', uri: url_1.default.pathToFileURL(options.rootDir).toString() }],
69
+ };
70
+ });
71
+ }
72
+ await client.connect(transport);
73
+ return client;
74
+ }
75
+ async function mcpTools(server, options) {
76
+ const client = await connectToMcpServer(server, options);
77
+ const { tools } = await client.listTools();
78
+ const filteredTools = options?.toolFilter ? tools.filter(tool => options.toolFilter.some(filter => typeof filter === 'string' ? tool.name === filter : filter.test(tool.name))) : tools;
79
+ const callTool = async (params) => {
80
+ return await client.callTool({ name: params.name, arguments: params.arguments });
81
+ };
82
+ return { tools: filteredTools, callTool, close: () => client.close() };
83
+ }
84
+ async function createMcpTools(servers, options) {
85
+ const allTools = [];
86
+ const callTools = new Map();
87
+ const closes = [];
88
+ for (const [name, server] of Object.entries(servers)) {
89
+ const { tools, callTool, close } = await mcpTools(server, options);
90
+ for (const tool of tools) {
91
+ const fullName = `${name}__${tool.name}`;
92
+ allTools.push({ ...tool, name: fullName });
93
+ callTools.set(fullName, params => callTool({ name: tool.name, arguments: params.arguments }));
94
+ }
95
+ closes.push(close);
96
+ }
97
+ return {
98
+ close: async () => {
99
+ await Promise.all(closes.map(c => c()));
100
+ },
101
+ tools: allTools,
102
+ callTool: async (params) => {
103
+ const callTool = callTools.get(params.name);
104
+ if (!callTool)
105
+ throw new Error(`Tool not found: ${params.name}`);
106
+ return callTool(params);
107
+ }
108
+ };
109
+ }
@@ -24,13 +24,13 @@ class Anthropic {
24
24
  max_tokens: options.maxTokens ?? 32768,
25
25
  temperature: options.temperature,
26
26
  system: systemPrompt(conversation.systemPrompt),
27
- messages: conversation.messages.map(toAnthropicMessagePart),
27
+ messages: conversation.messages.map(toAnthropicMessageParts).flat(),
28
28
  tools: conversation.tools.map(toAnthropicTool),
29
29
  thinking: options.reasoning ? {
30
30
  type: 'enabled',
31
31
  budget_tokens: options.maxTokens ? Math.round(options.maxTokens / 10) : 1024,
32
32
  } : undefined,
33
- });
33
+ }, options);
34
34
  const result = toAssistantMessage(response);
35
35
  const usage = {
36
36
  input: response.usage.input_tokens,
@@ -40,20 +40,26 @@ class Anthropic {
40
40
  }
41
41
  }
42
42
  exports.Anthropic = Anthropic;
43
- async function create(body) {
43
+ async function create(createParams, options) {
44
44
  const headers = {
45
45
  'Content-Type': 'application/json',
46
46
  'x-api-key': process.env.ANTHROPIC_API_KEY,
47
47
  'anthropic-version': '2023-06-01',
48
48
  };
49
+ const debugBody = { ...createParams, tools: `${createParams.tools?.length ?? 0} tools` };
50
+ options.debug?.('lowire:anthropic')('Request:', JSON.stringify(debugBody, null, 2));
49
51
  const response = await fetch(`https://api.anthropic.com/v1/messages`, {
50
52
  method: 'POST',
51
53
  headers,
52
- body: JSON.stringify(body)
54
+ body: JSON.stringify(createParams)
53
55
  });
54
- if (!response.ok)
56
+ if (!response.ok) {
57
+ options.debug?.('lowire:anthropic')('Response:', response.status);
55
58
  throw new Error(`API error: ${response.status} ${response.statusText} ${await response.text()}`);
56
- return await response.json();
59
+ }
60
+ const responseBody = await response.json();
61
+ options.debug?.('lowire:anthropic')('Response:', JSON.stringify(responseBody, null, 2));
62
+ return responseBody;
57
63
  }
58
64
  function toContentPart(block) {
59
65
  if (block.type === 'text') {
@@ -113,6 +119,7 @@ function toAnthropicTool(tool) {
113
119
  }
114
120
  function toAnthropicAssistantMessageParam(message) {
115
121
  const content = [];
122
+ const toolResults = [];
116
123
  for (const part of message.content) {
117
124
  if (part.type === 'text') {
118
125
  content.push({ ...part, citations: [] });
@@ -125,6 +132,8 @@ function toAnthropicAssistantMessageParam(message) {
125
132
  name: part.name,
126
133
  input: part.arguments
127
134
  });
135
+ if (part.result)
136
+ toolResults.push(toAnthropicToolResultMessage(part, part.result));
128
137
  continue;
129
138
  }
130
139
  if (part.type === 'thinking') {
@@ -136,34 +145,41 @@ function toAnthropicAssistantMessageParam(message) {
136
145
  continue;
137
146
  }
138
147
  }
139
- return {
140
- role: 'assistant',
141
- content
142
- };
148
+ if (message.toolError) {
149
+ toolResults.push({
150
+ role: 'user',
151
+ content: [{
152
+ type: 'text',
153
+ text: message.toolError,
154
+ }]
155
+ });
156
+ }
157
+ return [{
158
+ role: 'assistant',
159
+ content
160
+ }, ...toolResults];
143
161
  }
144
- function toAnthropicToolResultMessage(message) {
162
+ function toAnthropicToolResultMessage(call, result) {
145
163
  const toolResult = {
146
164
  type: 'tool_result',
147
- tool_use_id: message.toolCallId,
148
- content: message.result.content.map(toAnthropicResultParam),
149
- is_error: message.result.isError,
165
+ tool_use_id: call.id,
166
+ content: result.content.map(toAnthropicResultParam),
167
+ is_error: result.isError,
150
168
  };
151
169
  return {
152
170
  role: 'user',
153
171
  content: [toolResult]
154
172
  };
155
173
  }
156
- function toAnthropicMessagePart(message) {
174
+ function toAnthropicMessageParts(message) {
157
175
  if (message.role === 'user') {
158
- return {
159
- role: 'user',
160
- content: message.content
161
- };
176
+ return [{
177
+ role: 'user',
178
+ content: message.content
179
+ }];
162
180
  }
163
181
  if (message.role === 'assistant')
164
182
  return toAnthropicAssistantMessageParam(message);
165
- if (message.role === 'tool_result')
166
- return toAnthropicToolResultMessage(message);
167
183
  throw new Error(`Unsupported message role: ${message.role}`);
168
184
  }
169
185
  const systemPrompt = (prompt) => `
@@ -13,21 +13,21 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { OpenAICompletions } from './openaiCompletions';
17
- import type { Endpoint } from './openaiCompletions';
18
16
  import type * as types from '../types';
17
+ export declare class Github implements types.Provider {
18
+ readonly name: string;
19
+ private _apiKey;
20
+ private _bearer;
21
+ complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
22
+ result: types.AssistantMessage;
23
+ usage: types.Usage;
24
+ }>;
25
+ }
19
26
  export declare const kEditorHeaders: {
20
27
  'Editor-Version': string;
21
28
  'Editor-Plugin-Version': string;
22
29
  'User-Agent': string;
23
30
  Accept: string;
24
31
  'Content-Type': string;
32
+ 'Copilot-Vision-Request': string;
25
33
  };
26
- export declare class Github extends OpenAICompletions {
27
- readonly name = "copilot";
28
- connect(): Promise<Endpoint>;
29
- complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
30
- result: types.AssistantMessage;
31
- usage: types.Usage;
32
- }>;
33
- }