@tangle-network/sdk-provider-cli-base 0.13.17 → 0.13.18
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/dist/adapters/amp/index.d.mts +250 -0
- package/dist/adapters/amp/index.mjs +233 -0
- package/dist/adapters/factory-droids/index.d.mts +200 -0
- package/dist/adapters/factory-droids/index.mjs +356 -0
- package/dist/adapters/grok/index.d.mts +27 -0
- package/dist/adapters/grok/index.mjs +56 -0
- package/dist/adapters/hermes/index.d.mts +148 -0
- package/dist/adapters/hermes/index.mjs +244 -0
- package/dist/adapters/nanoclaw/index.d.mts +50 -0
- package/dist/adapters/nanoclaw/index.mjs +116 -0
- package/dist/adapters/openclaw/index.d.mts +68 -0
- package/dist/adapters/openclaw/index.mjs +240 -0
- package/dist/adapters/prime/index.d.mts +126 -0
- package/dist/adapters/prime/index.mjs +446 -0
- package/dist/conformance/index.d.mts +1 -1
- package/dist/index-BpO3fvbf.d.mts +1254 -0
- package/dist/index.d.mts +3 -1254
- package/dist/index.mjs +1 -4585
- package/dist/{prompt-input-BHn_1z69.d.mts → prompt-input-CD7okX8T.d.mts} +1 -1
- package/dist/src-Fn8Pu1xq.mjs +4586 -0
- package/package.json +35 -6
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { At as toolStartEvents, Dt as textDeltaEvents, Et as streamPartBase, F as jsonEventParser, G as isLocalMcpConfig, H as CliProviderAdapterBase, J as enabledInteractionKinds, K as localMcpArgv, Tt as sessionObservedEvents, V as recordCliTokenUsage, et as withTurnInteractions, k as McpInteractionSupport, kt as toolResultEvents, q as localMcpEnv, wt as reasoningEvents } from "../../src-Fn8Pu1xq.mjs";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { InteractionKind } from "@tangle-network/agent-interface";
|
|
4
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { renameSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
//#region src/adapters/factory-droids/event-mapper.ts
|
|
8
|
+
/**
|
|
9
|
+
* Type guard for Factory Droids events.
|
|
10
|
+
*/
|
|
11
|
+
function isFactoryDroidsEvent(value) {
|
|
12
|
+
if (typeof value !== "object" || value === null) return false;
|
|
13
|
+
return typeof value.type === "string";
|
|
14
|
+
}
|
|
15
|
+
function mapToolResultEvent(event, state) {
|
|
16
|
+
return toolResultEvents(state, {
|
|
17
|
+
id: event.tool_use_id,
|
|
18
|
+
content: event.content,
|
|
19
|
+
isError: Boolean(event.is_error)
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Maps a Factory Droids system event to StreamEvents.
|
|
24
|
+
*/
|
|
25
|
+
function mapSystemEvent(event) {
|
|
26
|
+
return [{
|
|
27
|
+
type: "status",
|
|
28
|
+
status: "processing",
|
|
29
|
+
detail: event.content
|
|
30
|
+
}];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Maps a Factory Droids error event to a failed terminal status carrying
|
|
34
|
+
* droid's own message. droid exits 0 after an error event, so a warning left
|
|
35
|
+
* the turn as "completed without user-visible output" and lost the cause.
|
|
36
|
+
*/
|
|
37
|
+
function mapErrorEvent(event) {
|
|
38
|
+
return [{
|
|
39
|
+
type: "status",
|
|
40
|
+
status: "failed",
|
|
41
|
+
detail: event.message ?? event.content ?? `Factory Droids error ${event.code ?? "FACTORY_DROIDS_ERROR"}`
|
|
42
|
+
}];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Maps a Factory Droids result event to StreamEvents.
|
|
46
|
+
*/
|
|
47
|
+
function mapResultEvent(event, state) {
|
|
48
|
+
recordCliTokenUsage(state, event);
|
|
49
|
+
return [{
|
|
50
|
+
type: "status",
|
|
51
|
+
status: "completed"
|
|
52
|
+
}];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* droid ≥0.159 `message` event — assistant text accumulates into the buffer
|
|
56
|
+
* exactly like the older `assistant` event (each event is a full turn, so turns
|
|
57
|
+
* are newline-joined); the `user` echo of our own prompt is ignored.
|
|
58
|
+
*/
|
|
59
|
+
function mapMessageEvent(event, state) {
|
|
60
|
+
if (event.role !== "assistant" || !event.text) return [];
|
|
61
|
+
const previousLength = state.textBuffer.length;
|
|
62
|
+
state.textBuffer += previousLength > 0 ? `\n${event.text}` : event.text;
|
|
63
|
+
state.textPartId ??= randomUUID();
|
|
64
|
+
const textPart = {
|
|
65
|
+
...streamPartBase(state, state.textPartId),
|
|
66
|
+
type: "text",
|
|
67
|
+
text: state.textBuffer
|
|
68
|
+
};
|
|
69
|
+
const events = [];
|
|
70
|
+
if (previousLength === 0) events.push({
|
|
71
|
+
type: "model-processing",
|
|
72
|
+
phase: "generating"
|
|
73
|
+
});
|
|
74
|
+
events.push({
|
|
75
|
+
type: "message.part.updated",
|
|
76
|
+
part: textPart
|
|
77
|
+
});
|
|
78
|
+
return events;
|
|
79
|
+
}
|
|
80
|
+
/** droid ≥0.159 `tool_call` event — same role as the older `tool_use`. */
|
|
81
|
+
function mapToolCallEvent(event, state) {
|
|
82
|
+
return toolStartEvents(state, {
|
|
83
|
+
id: event.toolId ?? event.id ?? randomUUID(),
|
|
84
|
+
name: event.toolName,
|
|
85
|
+
input: event.parameters ?? {}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** droid ≥0.159 terminal `completion` event — same role as the older `result`. */
|
|
89
|
+
function mapCompletionEvent(event, state) {
|
|
90
|
+
const events = [];
|
|
91
|
+
if (state.textBuffer.length === 0 && event.finalText) {
|
|
92
|
+
state.textBuffer = event.finalText;
|
|
93
|
+
state.textPartId ??= randomUUID();
|
|
94
|
+
events.push({
|
|
95
|
+
type: "message.part.updated",
|
|
96
|
+
part: {
|
|
97
|
+
...streamPartBase(state, state.textPartId),
|
|
98
|
+
type: "text",
|
|
99
|
+
text: state.textBuffer
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return [...events, ...mapResultEvent({
|
|
104
|
+
type: "result",
|
|
105
|
+
text: event.finalText ?? "",
|
|
106
|
+
session_id: event.session_id,
|
|
107
|
+
usage: event.usage
|
|
108
|
+
}, state)];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Maps a Factory Droids event to StreamEvent(s).
|
|
112
|
+
*
|
|
113
|
+
* Uses message.part.updated as the primary event type for all content updates,
|
|
114
|
+
* matching the OpenCode provider's event format.
|
|
115
|
+
*
|
|
116
|
+
* @param event - The Factory Droids event
|
|
117
|
+
* @param state - The execution state (mutated by this function)
|
|
118
|
+
* @returns Array of StreamEvents
|
|
119
|
+
*/
|
|
120
|
+
function mapFactoryDroidsEvent(event, state) {
|
|
121
|
+
return [...observeSession(event, state), ...mapEvent(event, state)];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* droid names its session on every stream event, from the first `system`
|
|
125
|
+
* init on. Report it at the first event, not at the final result, so a turn
|
|
126
|
+
* killed mid-run still names the session file it was writing.
|
|
127
|
+
*/
|
|
128
|
+
function observeSession(event, state) {
|
|
129
|
+
const sessionId = event.session_id;
|
|
130
|
+
if (typeof sessionId !== "string" || !sessionId) return [];
|
|
131
|
+
if (state.sessionId === sessionId) return [];
|
|
132
|
+
return sessionObservedEvents(state, "factory-droids", sessionId);
|
|
133
|
+
}
|
|
134
|
+
function mapEvent(event, state) {
|
|
135
|
+
switch (event.type) {
|
|
136
|
+
case "assistant": return textDeltaEvents(state, event.content);
|
|
137
|
+
case "thinking": return reasoningEvents(state, event.content, { signalPhase: true });
|
|
138
|
+
case "tool_use": return toolStartEvents(state, event);
|
|
139
|
+
case "tool_result": return mapToolResultEvent(event, state);
|
|
140
|
+
case "system": return mapSystemEvent(event);
|
|
141
|
+
case "error": return mapErrorEvent(event);
|
|
142
|
+
case "result": return mapResultEvent(event, state);
|
|
143
|
+
case "message": return mapMessageEvent(event, state);
|
|
144
|
+
case "tool_call": return mapToolCallEvent(event, state);
|
|
145
|
+
case "completion": return mapCompletionEvent(event, state);
|
|
146
|
+
default: return [{
|
|
147
|
+
type: "raw",
|
|
148
|
+
backend: "factory-droids",
|
|
149
|
+
event
|
|
150
|
+
}];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/adapters/factory-droids/adapter.ts
|
|
155
|
+
const DEFAULT_MODEL = "claude-opus-4-5-20251101";
|
|
156
|
+
/**
|
|
157
|
+
* A caller-owned OpenAI-compatible endpoint (`baseUrl` + `apiKey`) runs as a
|
|
158
|
+
* droid custom model. droid reads it from a runtime settings file passed with
|
|
159
|
+
* `--settings`; the file names the key by this env var, which droid expands,
|
|
160
|
+
* so the key never lands on disk. Factory's own account is not used. The
|
|
161
|
+
* custom model's id is `custom:<model>`, the id droid records in its session.
|
|
162
|
+
*/
|
|
163
|
+
const ENDPOINT_API_KEY_ENV = "TANGLE_DROID_ENDPOINT_API_KEY";
|
|
164
|
+
const ENDPOINT_SETTINGS_PREFIX = ".tangle-droid-endpoint-";
|
|
165
|
+
/** Name of the interaction MCP server registered into `.factory/mcp.json`. */
|
|
166
|
+
const DROID_INTERACTION_MCP_NAME = "tangle-interaction";
|
|
167
|
+
/**
|
|
168
|
+
* Factory Droids provider adapter.
|
|
169
|
+
*
|
|
170
|
+
* Implements the SdkProviderAdapter interface for the Factory Droids CLI tool.
|
|
171
|
+
* Extends CliProviderAdapterBase with Factory Droids-specific event parsing and CLI arguments.
|
|
172
|
+
*/
|
|
173
|
+
var FactoryDroidsProviderAdapter = class extends CliProviderAdapterBase {
|
|
174
|
+
provider = "factory-droids";
|
|
175
|
+
defaultCliPath = "droid";
|
|
176
|
+
/**
|
|
177
|
+
* Shared interaction round-trip for questions. droid auto-discovers
|
|
178
|
+
* MCP tools, so the agent calls `ask_user` itself; permission
|
|
179
|
+
* stays governed by droid's own `--auto` / `--skip-permissions-unsafe`.
|
|
180
|
+
*/
|
|
181
|
+
activeInteractions;
|
|
182
|
+
configuredInteractionKinds(interactions = this.config.interactions) {
|
|
183
|
+
return enabledInteractionKinds(interactions, ["question"]).map(() => InteractionKind.Question);
|
|
184
|
+
}
|
|
185
|
+
getCapabilities() {
|
|
186
|
+
const interactions = this.configuredInteractionKinds();
|
|
187
|
+
return {
|
|
188
|
+
...super.getCapabilities(),
|
|
189
|
+
...interactions.length > 0 ? { interactions } : {}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Host a per-execution interaction MCP server when `interactions` is on and
|
|
194
|
+
* register it into the SAME `$HOME/.factory/mcp.json` that `droid exec` reads
|
|
195
|
+
* (HOME resolved exactly as the base adapter's exec env resolves it, so the
|
|
196
|
+
* write lands where droid looks). The entry is removed and every pending
|
|
197
|
+
* interaction failed-closed in `finally` when the turn unwinds.
|
|
198
|
+
*/
|
|
199
|
+
async execute(input, services, onEvent) {
|
|
200
|
+
const turnConfig = withTurnInteractions(this.config, input);
|
|
201
|
+
const kinds = this.configuredInteractionKinds(turnConfig.interactions);
|
|
202
|
+
if (kinds.length === 0) return super.execute(input, services, onEvent);
|
|
203
|
+
const sessionId = input.sessionId ?? input.interactionBinding?.sessionId ?? randomUUID();
|
|
204
|
+
const workspaceRoot = input.workspaceRoot ?? this.storagePath;
|
|
205
|
+
const home = this.applyWritableHomeDefaults(this.buildExecEnv(this.config), workspaceRoot).HOME ?? workspaceRoot;
|
|
206
|
+
const interactions = new McpInteractionSupport(DROID_INTERACTION_MCP_NAME, kinds);
|
|
207
|
+
this.activeInteractions = interactions;
|
|
208
|
+
const server = await interactions.start(sessionId, this.interactionExecutionBinding(input, sessionId), onEvent);
|
|
209
|
+
await upsertDroidMcpEntry(home, DROID_INTERACTION_MCP_NAME, {
|
|
210
|
+
type: "http",
|
|
211
|
+
url: server.url,
|
|
212
|
+
headers: { Authorization: `Bearer ${server.token}` }
|
|
213
|
+
});
|
|
214
|
+
try {
|
|
215
|
+
return await super.execute(input, services, onEvent);
|
|
216
|
+
} finally {
|
|
217
|
+
await removeDroidMcpEntry(home, DROID_INTERACTION_MCP_NAME).catch(() => {});
|
|
218
|
+
this.activeInteractions = void 0;
|
|
219
|
+
await interactions.stop(sessionId);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async respondToInteraction(response) {
|
|
223
|
+
this.activeInteractions?.respond(response);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Initialize the adapter with API key validation.
|
|
227
|
+
* Factory Droids requires an API key to function.
|
|
228
|
+
*/
|
|
229
|
+
async afterInitialize(config) {
|
|
230
|
+
if (!(config.model?.apiKey ?? this.config.apiKey ?? process.env.FACTORY_API_KEY)) throw new Error("API key is required for Factory Droids. Set FACTORY_API_KEY environment variable or provide apiKey in config.");
|
|
231
|
+
}
|
|
232
|
+
buildExecArgs(params) {
|
|
233
|
+
const args = ["exec"];
|
|
234
|
+
const model = params.model ?? params.config.model ?? DEFAULT_MODEL;
|
|
235
|
+
const baseUrl = params.config.baseUrl?.trim();
|
|
236
|
+
if (baseUrl) {
|
|
237
|
+
const home = this.config.runtimeHome?.trim() || params.workspaceRoot;
|
|
238
|
+
args.push("--settings", writeEndpointSettings(home, {
|
|
239
|
+
model,
|
|
240
|
+
baseUrl
|
|
241
|
+
}), "--model", `custom:${model}`);
|
|
242
|
+
} else args.push("--model", model);
|
|
243
|
+
if (params.config.bypassPermissions !== false) args.push("--skip-permissions-unsafe");
|
|
244
|
+
else if (params.config.autonomyLevel) args.push("--auto", params.config.autonomyLevel);
|
|
245
|
+
args.push("--output-format", "stream-json");
|
|
246
|
+
args.push("--cwd", params.workspaceRoot);
|
|
247
|
+
if (params.systemPrompt) args.push("--append-system-prompt", params.systemPrompt);
|
|
248
|
+
if (params.config.extraFlags) args.push(...params.config.extraFlags);
|
|
249
|
+
if (params.sessionId) args.push("--session-id", params.sessionId);
|
|
250
|
+
args.push(params.message);
|
|
251
|
+
return {
|
|
252
|
+
command: this.getCliPath(),
|
|
253
|
+
args,
|
|
254
|
+
useStdin: false
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
buildExecEnv(config) {
|
|
258
|
+
const env = this.agentEnvironmentValues();
|
|
259
|
+
if (config.baseUrl?.trim()) {
|
|
260
|
+
if (!config.apiKey) throw new Error("Factory Droids on a caller endpoint (baseUrl) needs that endpoint's apiKey.");
|
|
261
|
+
env[ENDPOINT_API_KEY_ENV] = config.apiKey;
|
|
262
|
+
delete env.FACTORY_API_KEY;
|
|
263
|
+
return env;
|
|
264
|
+
}
|
|
265
|
+
const apiKey = config.apiKey ?? process.env.FACTORY_API_KEY;
|
|
266
|
+
if (apiKey) env.FACTORY_API_KEY = apiKey;
|
|
267
|
+
return env;
|
|
268
|
+
}
|
|
269
|
+
createEventParser(state, onEvent) {
|
|
270
|
+
return jsonEventParser(this.logger, onEvent, {
|
|
271
|
+
label: "Factory Droids",
|
|
272
|
+
accept: isFactoryDroidsEvent,
|
|
273
|
+
map: (event) => mapFactoryDroidsEvent(event, state)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
buildMcpAddArgs(name, config) {
|
|
277
|
+
const args = [
|
|
278
|
+
"mcp",
|
|
279
|
+
"add",
|
|
280
|
+
name
|
|
281
|
+
];
|
|
282
|
+
if (isLocalMcpConfig(config)) {
|
|
283
|
+
args.push("--type", "stdio", ...localMcpArgv(config));
|
|
284
|
+
for (const [key, value] of Object.entries(localMcpEnv(config) ?? {})) args.push("--env", `${key}=${value}`);
|
|
285
|
+
} else {
|
|
286
|
+
args.push("--type", "http", config.url);
|
|
287
|
+
for (const [key, value] of Object.entries(config.headers ?? {})) args.push("--header", `${key}: ${value}`);
|
|
288
|
+
}
|
|
289
|
+
return args;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Read–merge–write a single HTTP server into `<home>/.factory/mcp.json`. droid
|
|
294
|
+
* reads this file at `droid exec` startup; the shape is the standard
|
|
295
|
+
* `{ mcpServers: { <name>: { type, url, headers } } }` (verified against
|
|
296
|
+
* `droid mcp add --type http`). Merges into any existing config so a tenant's
|
|
297
|
+
* own servers survive; overwrites our own entry idempotently across turns.
|
|
298
|
+
*/
|
|
299
|
+
async function upsertDroidMcpEntry(home, name, entry) {
|
|
300
|
+
const dir = join(home, ".factory");
|
|
301
|
+
const file = join(dir, "mcp.json");
|
|
302
|
+
await mkdir(dir, { recursive: true });
|
|
303
|
+
const doc = await readMcpDoc(file);
|
|
304
|
+
doc.mcpServers[name] = entry;
|
|
305
|
+
await writeFile(file, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
306
|
+
}
|
|
307
|
+
/** Remove our entry from `<home>/.factory/mcp.json` (best-effort teardown). */
|
|
308
|
+
async function removeDroidMcpEntry(home, name) {
|
|
309
|
+
const file = join(home, ".factory", "mcp.json");
|
|
310
|
+
const doc = await readMcpDoc(file);
|
|
311
|
+
if (!(name in doc.mcpServers)) return;
|
|
312
|
+
delete doc.mcpServers[name];
|
|
313
|
+
await writeFile(file, `${JSON.stringify(doc, null, 2)}\n`, "utf8");
|
|
314
|
+
}
|
|
315
|
+
async function readMcpDoc(file) {
|
|
316
|
+
try {
|
|
317
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
318
|
+
const servers = parsed?.mcpServers;
|
|
319
|
+
return {
|
|
320
|
+
...parsed,
|
|
321
|
+
mcpServers: servers && typeof servers === "object" ? servers : {}
|
|
322
|
+
};
|
|
323
|
+
} catch {
|
|
324
|
+
return { mcpServers: {} };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Write the runtime settings that declare the caller's endpoint as droid's
|
|
329
|
+
* custom model, and return the file's path. The key stays an env reference.
|
|
330
|
+
*/
|
|
331
|
+
function writeEndpointSettings(home, endpoint) {
|
|
332
|
+
const path = join(home, `${ENDPOINT_SETTINGS_PREFIX}${createHash("sha256").update(`${endpoint.baseUrl}\n${endpoint.model}`).digest("hex").slice(0, 16)}.settings.json`);
|
|
333
|
+
const candidate = join(home, `.${randomUUID()}.droid-settings`);
|
|
334
|
+
try {
|
|
335
|
+
writeFileSync(candidate, `${JSON.stringify({ customModels: [{
|
|
336
|
+
id: `custom:${endpoint.model}`,
|
|
337
|
+
displayName: "tangle-endpoint",
|
|
338
|
+
model: endpoint.model,
|
|
339
|
+
baseUrl: endpoint.baseUrl,
|
|
340
|
+
apiKey: `\${${ENDPOINT_API_KEY_ENV}}`,
|
|
341
|
+
provider: "generic-chat-completion-api"
|
|
342
|
+
}] }, null, 2)}\n`, {
|
|
343
|
+
mode: 420,
|
|
344
|
+
flag: "wx"
|
|
345
|
+
});
|
|
346
|
+
renameSync(candidate, path);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
rmSync(candidate, { force: true });
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
return path;
|
|
352
|
+
}
|
|
353
|
+
//#endregion
|
|
354
|
+
export { FactoryDroidsProviderAdapter, isFactoryDroidsEvent, mapFactoryDroidsEvent };
|
|
355
|
+
|
|
356
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Mt as CliExecArgs, Pt as CliProviderConfig, Rt as ExecutionState, ht as CliProviderAdapterBase } from "../../index-BpO3fvbf.mjs";
|
|
2
|
+
import { StreamParserConfig } from "@tangle-network/sdk-cli-runner";
|
|
3
|
+
import { StreamEvent } from "@tangle-network/agent-interface";
|
|
4
|
+
|
|
5
|
+
//#region src/adapters/grok/adapter.d.ts
|
|
6
|
+
type GrokLine = {
|
|
7
|
+
type: "text";
|
|
8
|
+
line: string;
|
|
9
|
+
};
|
|
10
|
+
declare class GrokProviderAdapter extends CliProviderAdapterBase<CliProviderConfig, GrokLine> {
|
|
11
|
+
readonly provider = "grok";
|
|
12
|
+
protected readonly defaultCliPath = "grok";
|
|
13
|
+
protected buildExecArgs(params: {
|
|
14
|
+
message: string;
|
|
15
|
+
config: CliProviderConfig;
|
|
16
|
+
model?: string;
|
|
17
|
+
sessionId?: string;
|
|
18
|
+
workspaceRoot: string;
|
|
19
|
+
systemPrompt?: string;
|
|
20
|
+
}): CliExecArgs;
|
|
21
|
+
/** The host agent environment, with the turn's key and endpoint over it. */
|
|
22
|
+
protected buildExecEnv(config: CliProviderConfig): Record<string, string | undefined>;
|
|
23
|
+
protected createEventParser(state: ExecutionState, onEvent?: (event: StreamEvent) => void): StreamParserConfig<GrokLine>;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { GrokProviderAdapter };
|
|
27
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { H as CliProviderAdapterBase, I as plaintextResponseParser, Tt as sessionObservedEvents, ot as prependPromptSection } from "../../src-Fn8Pu1xq.mjs";
|
|
2
|
+
//#region src/adapters/grok/adapter.ts
|
|
3
|
+
const DECLARE_MODEL = "d=$HOME/.grok; [ -z \"$GROK_HOME\" ] || d=$GROK_HOME; t=\"[model.\\\"$1\\\"]\"; mkdir -p \"$d\" && { grep -qxF \"$t\" \"$d/managed_config.toml\" 2>/dev/null || printf '%s\\nmodel = \"%s\"\\n' \"$t\" \"$1\" >> \"$d/managed_config.toml\"; }; shift; exec \"$@\"";
|
|
4
|
+
var GrokProviderAdapter = class extends CliProviderAdapterBase {
|
|
5
|
+
provider = "grok";
|
|
6
|
+
defaultCliPath = "grok";
|
|
7
|
+
buildExecArgs(params) {
|
|
8
|
+
const args = [
|
|
9
|
+
"--always-approve",
|
|
10
|
+
"--output-format",
|
|
11
|
+
"streaming-json"
|
|
12
|
+
];
|
|
13
|
+
const model = params.model ?? params.config.model ?? "";
|
|
14
|
+
if (model) args.push("-m", model);
|
|
15
|
+
if (params.sessionId) args.push("-r", params.sessionId);
|
|
16
|
+
args.push("-p", prependPromptSection(params.systemPrompt, params.message));
|
|
17
|
+
if (!params.config.baseUrl?.trim() || !/^[\w.:@+/-]+$/.test(model)) return {
|
|
18
|
+
command: this.getCliPath(),
|
|
19
|
+
args,
|
|
20
|
+
useStdin: false
|
|
21
|
+
};
|
|
22
|
+
args.unshift("-c", DECLARE_MODEL, "grok", model, this.getCliPath());
|
|
23
|
+
return {
|
|
24
|
+
command: "/bin/sh",
|
|
25
|
+
args,
|
|
26
|
+
useStdin: false
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** The host agent environment, with the turn's key and endpoint over it. */
|
|
30
|
+
buildExecEnv(config) {
|
|
31
|
+
const env = this.agentEnvironmentValues();
|
|
32
|
+
if (config.apiKey) env.XAI_API_KEY = config.apiKey;
|
|
33
|
+
const baseUrl = config.baseUrl?.trim();
|
|
34
|
+
if (baseUrl) env.GROK_MODELS_BASE_URL = baseUrl;
|
|
35
|
+
return env;
|
|
36
|
+
}
|
|
37
|
+
createEventParser(state, onEvent) {
|
|
38
|
+
return plaintextResponseParser(state, onEvent, {
|
|
39
|
+
classify: (line) => line.length === 0 ? null : {
|
|
40
|
+
type: "text",
|
|
41
|
+
line
|
|
42
|
+
},
|
|
43
|
+
map: (event, response) => {
|
|
44
|
+
response.text(event.line);
|
|
45
|
+
const id = /"sessionId"\s*:\s*"([^"]+)"/.exec(event.line)?.[1];
|
|
46
|
+
if (!id || state.sessionId === id) return [];
|
|
47
|
+
return sessionObservedEvents(state, "grok", id);
|
|
48
|
+
},
|
|
49
|
+
emptyFailureDetail: "grok exited without producing a response body"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { GrokProviderAdapter };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Mt as CliExecArgs, Pt as CliProviderConfig, Rt as ExecutionState, ht as CliProviderAdapterBase, n as HarnessId } from "../../index-BpO3fvbf.mjs";
|
|
2
|
+
import { i as PreparedAttachment } from "../../prompt-input-CD7okX8T.mjs";
|
|
3
|
+
import { StreamParserConfig } from "@tangle-network/sdk-cli-runner";
|
|
4
|
+
import { InputPart, McpConfig, StreamEvent } from "@tangle-network/agent-interface";
|
|
5
|
+
import { MessagePart } from "@tangle-network/sdk-session-persistence";
|
|
6
|
+
|
|
7
|
+
//#region src/adapters/hermes/types.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Authentication mode for hermes.
|
|
10
|
+
*
|
|
11
|
+
* - "api-key": Use the provider-specific key env (OPENROUTER_API_KEY,
|
|
12
|
+
* ANTHROPIC_API_KEY, …) from `apiKey` or env. The adapter sets the env var
|
|
13
|
+
* for the provider chosen by `inferenceProvider`.
|
|
14
|
+
* - "oauth": Use Nous Portal OAuth tokens previously stored via `hermes login`
|
|
15
|
+
* (or `hermes auth add ... --type oauth`). The CLI reads them from
|
|
16
|
+
* `$HERMES_HOME/.env` or its credential store.
|
|
17
|
+
*/
|
|
18
|
+
type HermesAuthMode = "api-key" | "oauth";
|
|
19
|
+
/**
|
|
20
|
+
* Inference provider hermes will route the model call through. Subset of the
|
|
21
|
+
* providers hermes supports natively (`hermes --provider …`). The list here is
|
|
22
|
+
* intentionally narrow to the providers we plumb env vars for in the adapter;
|
|
23
|
+
* hermes accepts a wider set for end-user CLI use.
|
|
24
|
+
*/
|
|
25
|
+
type HermesInferenceProvider = "auto" | "openrouter" | "anthropic" | "openai-codex" | "nous" | "custom";
|
|
26
|
+
/**
|
|
27
|
+
* Hermes-specific configuration.
|
|
28
|
+
*/
|
|
29
|
+
interface HermesConfig extends CliProviderConfig {
|
|
30
|
+
/** Path to the hermes CLI executable. Default: "hermes" (resolved on PATH). */
|
|
31
|
+
cliPath?: string;
|
|
32
|
+
/** Model to use (e.g. "anthropic/claude-sonnet-4"). Forwarded as `--model`. */
|
|
33
|
+
model?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Inference provider for the model call. Forwarded as `--provider`.
|
|
36
|
+
*/
|
|
37
|
+
inferenceProvider?: HermesInferenceProvider;
|
|
38
|
+
/**
|
|
39
|
+
* Authentication mode. Defaults to "api-key" when an apiKey is present;
|
|
40
|
+
* otherwise "oauth" (which assumes the CLI's credential store is populated).
|
|
41
|
+
*/
|
|
42
|
+
authMode?: HermesAuthMode;
|
|
43
|
+
/**
|
|
44
|
+
* Optional auth files to materialize under `$HOME` before invoking the CLI.
|
|
45
|
+
* Hermes reads its API keys from `$HERMES_HOME/.env` and OAuth tokens from
|
|
46
|
+
* `$HERMES_HOME/credentials/*` — both targets work via the standard authFile
|
|
47
|
+
* materialization pipeline (see apps/sidecar/src/backends/cli-auth.ts).
|
|
48
|
+
*/
|
|
49
|
+
authFiles?: Array<{
|
|
50
|
+
path: string;
|
|
51
|
+
content: string;
|
|
52
|
+
mode?: number;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* API key. When present, written to the env var matching `inferenceProvider`
|
|
56
|
+
* (`OPENROUTER_API_KEY` for openrouter, `ANTHROPIC_API_KEY` for anthropic,
|
|
57
|
+
* etc). Used only when `authMode === "api-key"`.
|
|
58
|
+
*/
|
|
59
|
+
apiKey?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Base URL for the inference provider. Forwarded as a provider-specific env
|
|
62
|
+
* var (`OPENROUTER_BASE_URL` for openrouter, etc) so hermes routes through
|
|
63
|
+
* a custom endpoint like `https://router.tangle.tools/v1`.
|
|
64
|
+
*/
|
|
65
|
+
baseUrl?: string;
|
|
66
|
+
/** Comma-separated toolsets to enable via `--toolsets`. */
|
|
67
|
+
toolsets?: string[];
|
|
68
|
+
/**
|
|
69
|
+
* Extra flags appended before the terminal `-q <prompt>` pair.
|
|
70
|
+
*/
|
|
71
|
+
extraFlags?: string[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Emitted for every stdout line — accumulated into the response text. Quiet
|
|
75
|
+
* mode prints only the final content block, so the entire stream is body text.
|
|
76
|
+
*/
|
|
77
|
+
interface HermesTextEvent {
|
|
78
|
+
type: "text";
|
|
79
|
+
line: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Emitted when stdout closes; carries the full accumulated response so the
|
|
83
|
+
* adapter can synthesize a final `message.part.updated` and `result` event.
|
|
84
|
+
*/
|
|
85
|
+
interface HermesCompleteEvent {
|
|
86
|
+
type: "complete";
|
|
87
|
+
text: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Emitted when hermes exits non-zero or prints a recognizable error pattern.
|
|
91
|
+
* `message` is the raw line so callers can surface the upstream message verbatim.
|
|
92
|
+
*/
|
|
93
|
+
interface HermesErrorEvent {
|
|
94
|
+
type: "error";
|
|
95
|
+
message: string;
|
|
96
|
+
}
|
|
97
|
+
type HermesEvent = HermesTextEvent | HermesCompleteEvent | HermesErrorEvent;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/adapters/hermes/adapter.d.ts
|
|
100
|
+
declare class HermesProviderAdapter extends CliProviderAdapterBase<HermesConfig, HermesEvent> {
|
|
101
|
+
readonly provider = "hermes";
|
|
102
|
+
protected readonly profileHarness: HarnessId;
|
|
103
|
+
private activeSessionId?;
|
|
104
|
+
protected readonly defaultCliPath = "hermes";
|
|
105
|
+
protected isBenignNonZeroExit({
|
|
106
|
+
exitCode,
|
|
107
|
+
stderr,
|
|
108
|
+
state
|
|
109
|
+
}: {
|
|
110
|
+
exitCode: number | null;
|
|
111
|
+
stderr: string;
|
|
112
|
+
state: ExecutionState;
|
|
113
|
+
}): boolean;
|
|
114
|
+
protected buildMcpAddArgs(name: string, config: McpConfig): string[];
|
|
115
|
+
/** The contract store, or a workspace fallback when no runtime home is set. */
|
|
116
|
+
private resolveHermesHome;
|
|
117
|
+
protected preparePromptInput(parts: InputPart[], workspaceRoot: string, sessionId: string): Promise<{
|
|
118
|
+
promptText: string;
|
|
119
|
+
persistedParts: MessagePart[];
|
|
120
|
+
attachments: PreparedAttachment[];
|
|
121
|
+
}>;
|
|
122
|
+
protected nativeAttachmentKinds(): ReadonlyArray<"image" | "file">;
|
|
123
|
+
protected buildExecArgs(params: {
|
|
124
|
+
message: string;
|
|
125
|
+
config: HermesConfig;
|
|
126
|
+
model?: string;
|
|
127
|
+
sessionId?: string;
|
|
128
|
+
workspaceRoot: string;
|
|
129
|
+
systemPrompt?: string;
|
|
130
|
+
attachments?: PreparedAttachment[];
|
|
131
|
+
}): CliExecArgs;
|
|
132
|
+
private releaseTurnLease;
|
|
133
|
+
protected buildExecEnv(config: HermesConfig): Record<string, string | undefined>;
|
|
134
|
+
protected afterCliProcessExit(params: {
|
|
135
|
+
state: ExecutionState;
|
|
136
|
+
stderr: string;
|
|
137
|
+
exit: {
|
|
138
|
+
success: boolean;
|
|
139
|
+
};
|
|
140
|
+
workspaceRoot: string;
|
|
141
|
+
env: Record<string, string | undefined>;
|
|
142
|
+
onEvent?: (event: StreamEvent) => void;
|
|
143
|
+
}): Promise<void>;
|
|
144
|
+
protected createEventParser(state: ExecutionState, onEvent?: (event: StreamEvent) => void): StreamParserConfig<HermesEvent>;
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { type HermesAuthMode, type HermesCompleteEvent, type HermesConfig, type HermesErrorEvent, type HermesEvent, type HermesInferenceProvider, HermesProviderAdapter, type HermesTextEvent };
|
|
148
|
+
//# sourceMappingURL=index.d.mts.map
|