@agentuity/pi 1.0.36 → 2.0.16
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 +3 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +337 -177
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
- package/src/index.ts +428 -206
- package/dist/client.d.ts +0 -11
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -116
- package/dist/client.js.map +0 -1
- package/dist/handlers.d.ts +0 -19
- package/dist/handlers.d.ts.map +0 -1
- package/dist/handlers.js +0 -57
- package/dist/handlers.js.map +0 -1
- package/dist/protocol.d.ts +0 -68
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -3
- package/dist/protocol.js.map +0 -1
- package/src/client.ts +0 -144
- package/src/handlers.ts +0 -82
- package/src/protocol.ts +0 -95
package/src/index.ts
CHANGED
|
@@ -1,243 +1,465 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentuity AI Gateway Custom Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Registers models from the Agentuity AI Gateway using the appropriate API type
|
|
5
|
+
* based on model ID patterns. Models are loaded dynamically from the gateway's /models endpoint.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* Use /model to switch to aigateway models
|
|
9
|
+
*/
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { delimiter, join } from 'node:path';
|
|
13
|
+
import { createMinimalLogger, StructuredError } from '@agentuity/core';
|
|
14
|
+
import {
|
|
15
|
+
AIGatewayService,
|
|
16
|
+
type AIGatewayModel,
|
|
17
|
+
type AIGatewayModels,
|
|
18
|
+
} from '@agentuity/core/aigateway';
|
|
19
|
+
import { createServerFetchAdapter } from '@agentuity/server';
|
|
1
20
|
import type {
|
|
2
|
-
AgentToolResult,
|
|
3
21
|
ExtensionAPI,
|
|
4
|
-
ExtensionContext,
|
|
5
22
|
ExtensionCommandContext,
|
|
23
|
+
ProviderModelConfig,
|
|
6
24
|
} from '@mariozechner/pi-coding-agent';
|
|
7
|
-
import type { TSchema } from '@sinclair/typebox';
|
|
8
|
-
import { HubClient } from './client.ts';
|
|
9
|
-
import { processActions } from './handlers.ts';
|
|
10
|
-
import type { HubResponse, InitMessage } from './protocol.ts';
|
|
11
|
-
|
|
12
|
-
const HUB_URL_ENV = 'AGENTUITY_CODER_HUB_URL';
|
|
13
|
-
|
|
14
|
-
// All Pi events we subscribe to (order matters — session_start is handled separately)
|
|
15
|
-
const PROXY_EVENTS = [
|
|
16
|
-
'session_shutdown',
|
|
17
|
-
'session_before_switch',
|
|
18
|
-
'session_switch',
|
|
19
|
-
'session_before_fork',
|
|
20
|
-
'session_fork',
|
|
21
|
-
'session_before_compact',
|
|
22
|
-
'session_compact',
|
|
23
|
-
'before_agent_start',
|
|
24
|
-
'agent_start',
|
|
25
|
-
'agent_end',
|
|
26
|
-
'turn_start',
|
|
27
|
-
'turn_end',
|
|
28
|
-
'tool_call',
|
|
29
|
-
'tool_result',
|
|
30
|
-
'tool_execution_start',
|
|
31
|
-
'tool_execution_update',
|
|
32
|
-
'tool_execution_end',
|
|
33
|
-
'message_start',
|
|
34
|
-
'message_update',
|
|
35
|
-
'message_end',
|
|
36
|
-
'input',
|
|
37
|
-
'model_select',
|
|
38
|
-
'context',
|
|
39
|
-
] as const;
|
|
40
|
-
|
|
41
|
-
// Generic event handler type for the iteration loop
|
|
42
|
-
type GenericEventHandler = (
|
|
43
|
-
event: string,
|
|
44
|
-
handler: (event: unknown, ctx: ExtensionContext) => Promise<unknown>
|
|
45
|
-
) => void;
|
|
46
|
-
|
|
47
|
-
function log(msg: string): void {
|
|
48
|
-
console.error(`[agentuity-pi] ${msg}`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function agentuityCoderHub(pi: ExtensionAPI) {
|
|
52
|
-
const hubUrl = process.env[HUB_URL_ENV];
|
|
53
|
-
|
|
54
|
-
// No-op if not configured
|
|
55
|
-
if (!hubUrl) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
log(`Hub URL: ${hubUrl}`);
|
|
60
|
-
|
|
61
|
-
const client = new HubClient();
|
|
62
25
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
26
|
+
export type KnownApi =
|
|
27
|
+
| 'openai-completions'
|
|
28
|
+
| 'mistral-conversations'
|
|
29
|
+
| 'openai-responses'
|
|
30
|
+
| 'azure-openai-responses'
|
|
31
|
+
| 'openai-codex-responses'
|
|
32
|
+
| 'anthropic-messages'
|
|
33
|
+
| 'bedrock-converse-stream'
|
|
34
|
+
| 'google-generative-ai'
|
|
35
|
+
| 'google-gemini-cli'
|
|
36
|
+
| 'google-vertex';
|
|
37
|
+
|
|
38
|
+
const KNOWN_APIS = new Set<string>([
|
|
39
|
+
'openai-completions',
|
|
40
|
+
'mistral-conversations',
|
|
41
|
+
'openai-responses',
|
|
42
|
+
'azure-openai-responses',
|
|
43
|
+
'openai-codex-responses',
|
|
44
|
+
'anthropic-messages',
|
|
45
|
+
'bedrock-converse-stream',
|
|
46
|
+
'google-generative-ai',
|
|
47
|
+
'google-gemini-cli',
|
|
48
|
+
'google-vertex',
|
|
49
|
+
] satisfies KnownApi[]);
|
|
50
|
+
|
|
51
|
+
const AIGatewayModelFetchError = StructuredError('AIGatewayModelFetchError')<{
|
|
52
|
+
cause?: unknown;
|
|
53
|
+
}>();
|
|
54
|
+
|
|
55
|
+
type AgentuityOrganization = {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type AgentuityWhoami = {
|
|
61
|
+
organizations?: AgentuityOrganization[];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
type AgentuityRegion = {
|
|
65
|
+
region: string;
|
|
66
|
+
description: string;
|
|
67
|
+
default?: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function parseFirstJsonObject(value: string): unknown {
|
|
71
|
+
const start = value.indexOf('{');
|
|
72
|
+
if (start === -1) {
|
|
73
|
+
throw new SyntaxError('No JSON object found');
|
|
74
|
+
}
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
let depth = 0;
|
|
77
|
+
let inString = false;
|
|
78
|
+
let escaped = false;
|
|
79
|
+
for (let i = start; i < value.length; i++) {
|
|
80
|
+
const char = value[i];
|
|
81
|
+
if (escaped) {
|
|
82
|
+
escaped = false;
|
|
83
|
+
continue;
|
|
76
84
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
_signal: AbortSignal | undefined,
|
|
95
|
-
_onUpdate: unknown,
|
|
96
|
-
ctx: ExtensionContext
|
|
97
|
-
) {
|
|
98
|
-
log(`Tool execute: ${toolDef.name}`);
|
|
99
|
-
const id = client.nextId();
|
|
100
|
-
let response: HubResponse;
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
response = await client.send({
|
|
104
|
-
id,
|
|
105
|
-
type: 'tool',
|
|
106
|
-
name: toolDef.name,
|
|
107
|
-
toolCallId,
|
|
108
|
-
params: params as Record<string, unknown>,
|
|
109
|
-
});
|
|
110
|
-
} catch {
|
|
111
|
-
return {
|
|
112
|
-
content: [
|
|
113
|
-
{
|
|
114
|
-
type: 'text' as const,
|
|
115
|
-
text: 'Error: Hub connection lost',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
details: {},
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const result = await processActions(response.actions, ctx);
|
|
123
|
-
|
|
124
|
-
if (result.returnValue !== undefined) {
|
|
125
|
-
return result.returnValue as AgentToolResult<unknown>;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
content: [{ type: 'text' as const, text: 'Done' }],
|
|
130
|
-
details: {},
|
|
131
|
-
};
|
|
132
|
-
},
|
|
133
|
-
});
|
|
85
|
+
if (char === '\\') {
|
|
86
|
+
escaped = true;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (char === '"') {
|
|
90
|
+
inString = !inString;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (inString) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (char === '{') {
|
|
97
|
+
depth++;
|
|
98
|
+
} else if (char === '}') {
|
|
99
|
+
depth--;
|
|
100
|
+
if (depth === 0) {
|
|
101
|
+
return JSON.parse(value.slice(start, i + 1));
|
|
134
102
|
}
|
|
135
103
|
}
|
|
104
|
+
}
|
|
136
105
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
name: cmdDef.name,
|
|
153
|
-
args,
|
|
154
|
-
});
|
|
155
|
-
} catch {
|
|
156
|
-
ctx.ui.notify('Hub connection lost', 'error');
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
await processActions(response.actions, ctx);
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
}
|
|
106
|
+
throw new SyntaxError('Unterminated JSON object');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseJson(value: string): unknown {
|
|
110
|
+
const trimmed = value.trim();
|
|
111
|
+
if (trimmed.startsWith('[')) {
|
|
112
|
+
return JSON.parse(trimmed);
|
|
113
|
+
}
|
|
114
|
+
return parseFirstJsonObject(trimmed);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getEnv(...keys: string[]): string | undefined {
|
|
118
|
+
for (const key of keys) {
|
|
119
|
+
if (process.env[key]) {
|
|
120
|
+
return process.env[key];
|
|
164
121
|
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
165
124
|
|
|
166
|
-
|
|
125
|
+
function normalizeCredential(value: unknown): string | undefined {
|
|
126
|
+
if (value === undefined || value === null) {
|
|
127
|
+
return undefined;
|
|
167
128
|
}
|
|
129
|
+
const normalized = String(value).trim();
|
|
130
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
131
|
+
}
|
|
168
132
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
data: Record<string, unknown>,
|
|
173
|
-
ctx: ExtensionContext
|
|
174
|
-
): Promise<unknown> {
|
|
175
|
-
if (!client.connected) return undefined;
|
|
133
|
+
function isKnownApi(api: unknown): api is KnownApi {
|
|
134
|
+
return typeof api === 'string' && KNOWN_APIS.has(api);
|
|
135
|
+
}
|
|
176
136
|
|
|
177
|
-
|
|
178
|
-
|
|
137
|
+
function getRegion(): string {
|
|
138
|
+
return getEnv('AGENTUITY_REGION') ?? 'usc';
|
|
139
|
+
}
|
|
179
140
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
141
|
+
function getBaseUrl(): string {
|
|
142
|
+
const region = getRegion();
|
|
143
|
+
return `https://aigateway-${region}.agentuity.cloud`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function getAgentuityCliPath(): string | undefined {
|
|
147
|
+
const path = process.env.PATH?.split(delimiter) ?? [];
|
|
148
|
+
for (const dir of path) {
|
|
149
|
+
const fn = join(dir, 'agentuity');
|
|
150
|
+
if (existsSync(fn)) {
|
|
151
|
+
return fn;
|
|
189
152
|
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
190
155
|
|
|
191
|
-
|
|
156
|
+
function fetchOrganizations(): AgentuityOrganization[] {
|
|
157
|
+
const agentuity = getAgentuityCliPath();
|
|
158
|
+
if (!agentuity) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
192
161
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
162
|
+
const res = execFileSync(agentuity, ['auth', 'whoami', '--json']);
|
|
163
|
+
const whoami = parseJson(res.toString()) as AgentuityWhoami;
|
|
164
|
+
return (whoami.organizations ?? []).filter(
|
|
165
|
+
(org): org is AgentuityOrganization =>
|
|
166
|
+
typeof org?.id === 'string' &&
|
|
167
|
+
org.id.length > 0 &&
|
|
168
|
+
typeof org?.name === 'string' &&
|
|
169
|
+
org.name.length > 0
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function fetchRegions(): AgentuityRegion[] {
|
|
174
|
+
const agentuity = getAgentuityCliPath();
|
|
175
|
+
if (!agentuity) {
|
|
176
|
+
return [];
|
|
196
177
|
}
|
|
197
178
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
179
|
+
const res = execFileSync(agentuity, ['cloud', 'region', 'list', '--json']);
|
|
180
|
+
const regions = parseJson(res.toString()) as AgentuityRegion[];
|
|
181
|
+
return (Array.isArray(regions) ? regions : []).filter(
|
|
182
|
+
(region): region is AgentuityRegion =>
|
|
183
|
+
typeof region?.region === 'string' &&
|
|
184
|
+
region.region.length > 0 &&
|
|
185
|
+
typeof region?.description === 'string' &&
|
|
186
|
+
region.description.length > 0
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getCurrentOrgId(): string | undefined {
|
|
191
|
+
return normalizeCredential(
|
|
192
|
+
getEnv(
|
|
193
|
+
'AGENTUITY_AIGATEWAY_ORGID',
|
|
194
|
+
'AGENTUITY_ORGID',
|
|
195
|
+
'AGENTUITY_CLOUD_ORG_ID',
|
|
196
|
+
'AGENTUITY_ORG_ID'
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function fetchModels(): Promise<AIGatewayModels> {
|
|
202
|
+
const baseUrl = getBaseUrl();
|
|
203
|
+
let apiKey = normalizeCredential(
|
|
204
|
+
getEnv(
|
|
205
|
+
'AGENTUITY_CODER_API_KEY',
|
|
206
|
+
'AGENTUITY_SDK_KEY',
|
|
207
|
+
'AGENTUITY_CLI_API_KEY',
|
|
208
|
+
'AGENTUITY_CLI_KEY'
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
let orgId = normalizeCredential(
|
|
212
|
+
getEnv('AGENTUITY_ORGID', 'AGENTUITY_CLOUD_ORG_ID', 'AGENTUITY_ORG_ID')
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
if (!apiKey) {
|
|
216
|
+
let found = false;
|
|
217
|
+
const fn = getAgentuityCliPath();
|
|
218
|
+
if (fn) {
|
|
219
|
+
try {
|
|
220
|
+
const res = execFileSync(fn, ['auth', 'apikey', '--json']);
|
|
221
|
+
const apiKeyResult = parseJson(res.toString()) as { apiKey: string };
|
|
222
|
+
apiKey = normalizeCredential(apiKeyResult.apiKey);
|
|
223
|
+
found = true;
|
|
224
|
+
if (!orgId) {
|
|
225
|
+
const ores = execFileSync(fn, ['auth', 'org', 'current']);
|
|
226
|
+
orgId = normalizeCredential(ores);
|
|
227
|
+
if (!orgId) {
|
|
228
|
+
return {};
|
|
209
229
|
}
|
|
210
230
|
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new AIGatewayModelFetchError({
|
|
233
|
+
message: 'Failed to fetch models from AI Gateway',
|
|
234
|
+
cause: error,
|
|
235
|
+
});
|
|
211
236
|
}
|
|
212
237
|
}
|
|
213
|
-
|
|
238
|
+
if (!found) {
|
|
239
|
+
console.warn(
|
|
240
|
+
'AGENTUITY_SDK_KEY, AGENTUITY_CLI_API_KEY or AGENTUITY_CLI_KEY not set and cannot find the agentuity cli, cannot fetch models from AI Gateway'
|
|
241
|
+
);
|
|
242
|
+
return {};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!apiKey) {
|
|
247
|
+
console.warn('Cannot determine the API key, cannot fetch models from AI Gateway');
|
|
248
|
+
return {};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
process.env.AGENTUITY_AIGATEWAY_KEY = apiKey;
|
|
252
|
+
if (orgId) {
|
|
253
|
+
process.env.AGENTUITY_AIGATEWAY_ORGID = orgId;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const service = new AIGatewayService(
|
|
258
|
+
baseUrl,
|
|
259
|
+
createServerFetchAdapter({ headers: {} }, createMinimalLogger())
|
|
260
|
+
);
|
|
261
|
+
return await service.listModels();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
throw new AIGatewayModelFetchError({
|
|
264
|
+
message: 'Failed to fetch models from AI Gateway',
|
|
265
|
+
cause: error,
|
|
266
|
+
});
|
|
214
267
|
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function sanitizeModalities(modalities: string[] | undefined): ('text' | 'image')[] {
|
|
271
|
+
const sanitized = (modalities ?? []).filter(
|
|
272
|
+
(modality): modality is 'text' | 'image' => modality === 'text' || modality === 'image'
|
|
273
|
+
);
|
|
274
|
+
return sanitized.length > 0 ? sanitized : ['text'];
|
|
275
|
+
}
|
|
215
276
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
277
|
+
function toPiModel(m: AIGatewayModel): ProviderModelConfig {
|
|
278
|
+
return {
|
|
279
|
+
id: m.id,
|
|
280
|
+
name: m.name,
|
|
281
|
+
reasoning: m.reasoning ?? false,
|
|
282
|
+
input: sanitizeModalities(m.input_modalities),
|
|
283
|
+
contextWindow: m.context_window ?? 40000,
|
|
284
|
+
maxTokens: m.max_output_tokens ?? 64000,
|
|
285
|
+
cost: {
|
|
286
|
+
input: m.pricing?.input ?? 0,
|
|
287
|
+
output: m.pricing?.output ?? 0,
|
|
288
|
+
cacheRead: m.pricing?.cached_input ?? 0,
|
|
289
|
+
cacheWrite: 0,
|
|
290
|
+
},
|
|
291
|
+
compat: {
|
|
292
|
+
supportsDeveloperRole: false,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
219
296
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
297
|
+
async function registerAIGatewayProviders(pi: ExtensionAPI) {
|
|
298
|
+
const models = await fetchModels();
|
|
299
|
+
const baseUrl = getBaseUrl();
|
|
223
300
|
|
|
224
|
-
|
|
225
|
-
|
|
301
|
+
const allModels: AIGatewayModel[] = [];
|
|
302
|
+
for (const providerModels of Object.values(models)) {
|
|
303
|
+
if (providerModels) {
|
|
304
|
+
allModels.push(...providerModels);
|
|
226
305
|
}
|
|
227
|
-
}
|
|
306
|
+
}
|
|
307
|
+
if (allModels.length === 0) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const modelsByApi = new Map<KnownApi, ProviderModelConfig[]>();
|
|
312
|
+
|
|
313
|
+
for (const m of allModels) {
|
|
314
|
+
const apiType = m.api;
|
|
315
|
+
if (!isKnownApi(apiType)) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const existing = modelsByApi.get(apiType) ?? [];
|
|
319
|
+
existing.push(toPiModel(m));
|
|
320
|
+
modelsByApi.set(apiType, existing);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const headers: Record<string, string> = {};
|
|
324
|
+
if (process.env.AGENTUITY_AIGATEWAY_ORGID) {
|
|
325
|
+
headers['x-agentuity-orgid'] = process.env.AGENTUITY_AIGATEWAY_ORGID;
|
|
326
|
+
}
|
|
228
327
|
|
|
229
|
-
for (const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
328
|
+
for (const [apiType, providerModels] of modelsByApi) {
|
|
329
|
+
const apitok = apiType.split('-');
|
|
330
|
+
const name = apitok.length >= 2 ? apitok.slice(0, 2).join('-') : apitok[0];
|
|
331
|
+
const providerName = `agentuity/${name}`;
|
|
332
|
+
pi.registerProvider(providerName, {
|
|
333
|
+
baseUrl,
|
|
334
|
+
apiKey: 'AGENTUITY_AIGATEWAY_KEY',
|
|
335
|
+
headers,
|
|
336
|
+
authHeader: true,
|
|
337
|
+
api: apiType,
|
|
338
|
+
models: providerModels,
|
|
233
339
|
});
|
|
234
340
|
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function registerRegionCommand(pi: ExtensionAPI): void {
|
|
344
|
+
pi.registerCommand('region', {
|
|
345
|
+
description: 'Select active Agentuity region',
|
|
346
|
+
handler: async (_args: string, ctx: ExtensionCommandContext) => {
|
|
347
|
+
if (!ctx.hasUI) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
let regions: AgentuityRegion[];
|
|
352
|
+
try {
|
|
353
|
+
regions = fetchRegions();
|
|
354
|
+
} catch (error) {
|
|
355
|
+
ctx.ui.notify(`Failed to load Agentuity regions: ${String(error)}`, 'error');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (regions.length === 0) {
|
|
360
|
+
ctx.ui.notify('No Agentuity regions found.', 'warning');
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const currentRegion = getRegion();
|
|
365
|
+
const labels = regions.map((region) => {
|
|
366
|
+
const marker = region.region === currentRegion ? '* ' : '';
|
|
367
|
+
const defaultLabel = region.default ? ' default' : '';
|
|
368
|
+
return `${marker}${region.description} (${region.region})${defaultLabel}`;
|
|
369
|
+
});
|
|
370
|
+
const selected = await ctx.ui.select('Select Agentuity Region', labels);
|
|
371
|
+
if (!selected) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const selectedIndex = labels.indexOf(selected);
|
|
376
|
+
const region = regions[selectedIndex];
|
|
377
|
+
if (!region) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
235
380
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
381
|
+
process.env.AGENTUITY_REGION = region.region;
|
|
382
|
+
ctx.ui.notify(`Using Agentuity region: ${region.description}`, 'info');
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
await registerAIGatewayProviders(pi);
|
|
386
|
+
ctx.ui.notify('Agentuity AI Gateway models refreshed.', 'info');
|
|
387
|
+
} catch (error) {
|
|
388
|
+
ctx.ui.notify(`Failed to refresh AI Gateway models: ${String(error)}`, 'error');
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function registerOrganizationCommand(pi: ExtensionAPI): void {
|
|
395
|
+
pi.registerCommand('organization', {
|
|
396
|
+
description: 'Select active Agentuity organization',
|
|
397
|
+
handler: async (_args: string, ctx: ExtensionCommandContext) => {
|
|
398
|
+
if (!ctx.hasUI) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let organizations: AgentuityOrganization[];
|
|
403
|
+
try {
|
|
404
|
+
organizations = fetchOrganizations();
|
|
405
|
+
} catch (error) {
|
|
406
|
+
ctx.ui.notify(`Failed to load Agentuity organizations: ${String(error)}`, 'error');
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (organizations.length === 0) {
|
|
411
|
+
ctx.ui.notify('No Agentuity organizations found for the current CLI login.', 'warning');
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const currentOrgId = getCurrentOrgId();
|
|
416
|
+
const labels = organizations.map((org) => {
|
|
417
|
+
const marker = org.id === currentOrgId ? '* ' : '';
|
|
418
|
+
return `${marker}${org.name} (${org.id})`;
|
|
419
|
+
});
|
|
420
|
+
const selected = await ctx.ui.select('Select Agentuity Organization', labels);
|
|
421
|
+
if (!selected) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const selectedIndex = labels.indexOf(selected);
|
|
426
|
+
const organization = organizations[selectedIndex];
|
|
427
|
+
if (!organization) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
process.env.AGENTUITY_AIGATEWAY_ORGID = organization.id;
|
|
432
|
+
process.env.AGENTUITY_ORGID = organization.id;
|
|
433
|
+
process.env.AGENTUITY_CLOUD_ORG_ID = organization.id;
|
|
434
|
+
process.env.AGENTUITY_ORG_ID = organization.id;
|
|
435
|
+
|
|
436
|
+
ctx.ui.notify(`Using Agentuity organization: ${organization.name}`, 'info');
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
await registerAIGatewayProviders(pi);
|
|
440
|
+
ctx.ui.notify('Agentuity AI Gateway models refreshed.', 'info');
|
|
441
|
+
} catch (error) {
|
|
442
|
+
ctx.ui.notify(`Failed to refresh AI Gateway models: ${String(error)}`, 'error');
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export async function setupAIGateway(pi: ExtensionAPI) {
|
|
449
|
+
registerOrganizationCommand(pi);
|
|
450
|
+
registerRegionCommand(pi);
|
|
451
|
+
let showedOrganizationPrompt = false;
|
|
452
|
+
pi.on('session_start', (_event, ctx) => {
|
|
453
|
+
if (showedOrganizationPrompt || !ctx.hasUI || getCurrentOrgId()) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
showedOrganizationPrompt = true;
|
|
457
|
+
ctx.ui.notify(
|
|
458
|
+
'Use /organization to select an Agentuity organization for AI Gateway models.',
|
|
459
|
+
'info'
|
|
460
|
+
);
|
|
240
461
|
});
|
|
462
|
+
await registerAIGatewayProviders(pi);
|
|
241
463
|
}
|
|
242
464
|
|
|
243
|
-
export default
|
|
465
|
+
export default setupAIGateway;
|
package/dist/client.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { InitMessage, HubRequest, HubResponse } from './protocol.ts';
|
|
2
|
-
export declare class HubClient {
|
|
3
|
-
private ws;
|
|
4
|
-
private pending;
|
|
5
|
-
connect(url: string): Promise<InitMessage>;
|
|
6
|
-
nextId(): string;
|
|
7
|
-
send(request: HubRequest): Promise<HubResponse>;
|
|
8
|
-
close(): void;
|
|
9
|
-
get connected(): boolean;
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQ1E,qBAAa,SAAS;IACrB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAOX;IACE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAuFhD,MAAM,IAAI,MAAM;IAIV,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IAwBrD,KAAK,IAAI,IAAI;IAOb,IAAI,SAAS,IAAI,OAAO,CAEvB;CACD"}
|