@tryvira/opencode 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/dist/index.js +1354 -0
- package/package.json +12 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
// ../../sdks/js-client.ts
|
|
5
|
+
var ViraApiError = class extends Error {
|
|
6
|
+
status;
|
|
7
|
+
body;
|
|
8
|
+
requestId;
|
|
9
|
+
constructor(message, status, body, requestId) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "ViraApiError";
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.body = body;
|
|
14
|
+
this.requestId = requestId;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var required = (value, code) => {
|
|
18
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(code);
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
var normalizeBaseUrl = (value, allowInsecureLocalhost = false) => {
|
|
22
|
+
let url;
|
|
23
|
+
try {
|
|
24
|
+
url = new URL(required(value, "SDK_BASE_URL_REQUIRED"));
|
|
25
|
+
} catch {
|
|
26
|
+
throw new Error("SDK_BASE_URL_INVALID");
|
|
27
|
+
}
|
|
28
|
+
const hostname = url.hostname.toLowerCase();
|
|
29
|
+
const loopback = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
30
|
+
if (url.username || url.password || url.search || url.hash) throw new Error("SDK_BASE_URL_INVALID");
|
|
31
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback && allowInsecureLocalhost))
|
|
32
|
+
throw new Error("SDK_HTTPS_REQUIRED");
|
|
33
|
+
return value.replace(/\/$/, "");
|
|
34
|
+
};
|
|
35
|
+
var canonicalJson = (value) => JSON.stringify(
|
|
36
|
+
value,
|
|
37
|
+
(_key, item) => item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([left], [right]) => left.localeCompare(right))) : item
|
|
38
|
+
);
|
|
39
|
+
var contentIdempotencyKey = async (operation, request) => {
|
|
40
|
+
if (!globalThis.crypto?.subtle) throw new Error("SDK_IDEMPOTENCY_CRYPTO_UNAVAILABLE");
|
|
41
|
+
const bytes = new TextEncoder().encode(canonicalJson(request));
|
|
42
|
+
const digest3 = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
43
|
+
const hex = [...new Uint8Array(digest3)].map((value) => value.toString(16).padStart(2, "0")).join("");
|
|
44
|
+
return `${operation}:${hex}`;
|
|
45
|
+
};
|
|
46
|
+
var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
47
|
+
var ViraClient = class {
|
|
48
|
+
evaluations;
|
|
49
|
+
teach;
|
|
50
|
+
packs;
|
|
51
|
+
baseUrl;
|
|
52
|
+
apiKey;
|
|
53
|
+
fetcher;
|
|
54
|
+
timeoutMs;
|
|
55
|
+
retries;
|
|
56
|
+
failureMode;
|
|
57
|
+
defaultHeaders;
|
|
58
|
+
boundConnectionId;
|
|
59
|
+
constructor(options) {
|
|
60
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl, options.allowInsecureLocalhost === true);
|
|
61
|
+
this.apiKey = options.apiKey;
|
|
62
|
+
this.fetcher = options.fetcher ?? fetch;
|
|
63
|
+
this.timeoutMs = Math.max(
|
|
64
|
+
1e3,
|
|
65
|
+
Math.min(options.timeoutMs ?? 15e3, 12e4)
|
|
66
|
+
);
|
|
67
|
+
this.retries = Math.max(0, Math.min(options.retries ?? 2, 5));
|
|
68
|
+
this.failureMode = options.failureMode ?? "throw";
|
|
69
|
+
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
70
|
+
this.boundConnectionId = options.connectionId;
|
|
71
|
+
this.evaluations = {
|
|
72
|
+
createPlan: (input, options2 = {}) => this.createEvaluationPlan(input, options2),
|
|
73
|
+
get: (id) => this.getEvaluation(id),
|
|
74
|
+
compare: (id) => this.getEvaluationComparison(id),
|
|
75
|
+
reviewFinding: (evaluationId, findingId, input, options2 = {}) => this.reviewComparativeFinding(evaluationId, findingId, input, options2),
|
|
76
|
+
proposeImprovement: (id, input, options2 = {}) => this.createImprovementProposal(id, input, options2)
|
|
77
|
+
};
|
|
78
|
+
this.teach = {
|
|
79
|
+
review: (id, action = "request-review", options2 = {}) => this.reviewImprovementProposal(id, action, options2),
|
|
80
|
+
createCandidate: (input, options2 = {}) => this.createBehaviorRevisionCandidate(input, options2),
|
|
81
|
+
evaluateCandidate: (id, input, options2 = {}) => this.evaluateBehaviorRevisionCandidate(id, input, options2)
|
|
82
|
+
};
|
|
83
|
+
this.packs = {
|
|
84
|
+
createRevision: (input, options2 = {}) => this.createPackRevision(input, options2),
|
|
85
|
+
getRevision: (id) => this.getPackRevision(id),
|
|
86
|
+
diffRevision: (id) => this.getPackRevisionDiff(id),
|
|
87
|
+
transitionRevision: (id, transition, options2 = {}) => this.transitionPackRevision(id, transition, options2)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async request(request, options = {}) {
|
|
91
|
+
const url = new URL(this.baseUrl + request.path);
|
|
92
|
+
for (const [key, value] of Object.entries(request.query ?? {}))
|
|
93
|
+
url.searchParams.set(key, value);
|
|
94
|
+
const headers = {
|
|
95
|
+
accept: "application/json",
|
|
96
|
+
...this.defaultHeaders
|
|
97
|
+
};
|
|
98
|
+
if (request.body) headers["content-type"] = "application/json";
|
|
99
|
+
if (this.apiKey) {
|
|
100
|
+
if (this.apiKey.startsWith("vira_live_")) headers["x-vira-api-key"] = this.apiKey;
|
|
101
|
+
else {
|
|
102
|
+
headers.authorization = `Bearer ${this.apiKey}`;
|
|
103
|
+
headers["x-vira-api-key"] = this.apiKey;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (options.idempotencyKey)
|
|
107
|
+
headers["idempotency-key"] = options.idempotencyKey;
|
|
108
|
+
const retryable = request.method === "GET" || Boolean(options.idempotencyKey);
|
|
109
|
+
const attempts = retryable ? this.retries + 1 : 1;
|
|
110
|
+
let lastError;
|
|
111
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
114
|
+
try {
|
|
115
|
+
const response = await this.fetcher(url, {
|
|
116
|
+
method: request.method,
|
|
117
|
+
headers,
|
|
118
|
+
body: request.body ? JSON.stringify(request.body) : void 0,
|
|
119
|
+
signal: controller.signal
|
|
120
|
+
});
|
|
121
|
+
const raw = await response.text();
|
|
122
|
+
let body = null;
|
|
123
|
+
try {
|
|
124
|
+
body = raw ? JSON.parse(raw) : null;
|
|
125
|
+
} catch {
|
|
126
|
+
body = raw;
|
|
127
|
+
}
|
|
128
|
+
const requestId = response.headers.get("x-request-id") ?? "";
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
const error = new ViraApiError(
|
|
131
|
+
`VIRA_API_${response.status}`,
|
|
132
|
+
response.status,
|
|
133
|
+
body,
|
|
134
|
+
requestId
|
|
135
|
+
);
|
|
136
|
+
if ((response.status >= 500 || response.status === 429) && attempt + 1 < attempts) {
|
|
137
|
+
const retryAfter = Number(response.headers.get("retry-after") ?? "");
|
|
138
|
+
const serverDelay = Number.isFinite(retryAfter) && retryAfter >= 0 ? Math.min(retryAfter * 1e3, 12e4) : 0;
|
|
139
|
+
const jitter = Math.floor(Math.random() * 25);
|
|
140
|
+
await wait(Math.max(serverDelay, 25 * 2 ** attempt) + jitter);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (this.failureMode === "open" && !options.enforcing && response.status >= 500)
|
|
144
|
+
return null;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
return { status: response.status, body, requestId };
|
|
148
|
+
} catch (error) {
|
|
149
|
+
lastError = error;
|
|
150
|
+
if (error instanceof ViraApiError) throw error;
|
|
151
|
+
if (attempt + 1 < attempts) {
|
|
152
|
+
await wait(25 * 2 ** attempt);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (this.failureMode === "open" && !options.enforcing) return null;
|
|
156
|
+
throw error;
|
|
157
|
+
} finally {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
throw lastError;
|
|
162
|
+
}
|
|
163
|
+
connectSystem(input) {
|
|
164
|
+
const request = buildAISystemRequest(input);
|
|
165
|
+
return this.request(request, {
|
|
166
|
+
idempotencyKey: input.idempotencyKey
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
listAISystems() {
|
|
170
|
+
return this.request({ method: "GET", path: "/v1/ai-systems" });
|
|
171
|
+
}
|
|
172
|
+
async deleteAISystem(systemId, options = {}) {
|
|
173
|
+
required(systemId, "SDK_SYSTEM_ID_REQUIRED");
|
|
174
|
+
const path = `/v1/ai-systems/${encodeURIComponent(systemId)}`, body = { confirm: options.confirm === true };
|
|
175
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:ai-system:${systemId}`, { path, body }) });
|
|
176
|
+
}
|
|
177
|
+
async deleteAISystemCascade(systemId, options = {}) {
|
|
178
|
+
required(systemId, "SDK_SYSTEM_ID_REQUIRED");
|
|
179
|
+
const path = `/v1/ai-systems/${encodeURIComponent(systemId)}/cleanup`, body = { confirm: options.confirm === true, deleteObservations: options.deleteObservations === true };
|
|
180
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:ai-system-cascade:${systemId}`, { path, body }) });
|
|
181
|
+
}
|
|
182
|
+
getAISystem(systemId) {
|
|
183
|
+
required(systemId, "SDK_SYSTEM_ID_REQUIRED");
|
|
184
|
+
return this.request({ method: "GET", path: `/v1/ai-systems/${encodeURIComponent(systemId)}` });
|
|
185
|
+
}
|
|
186
|
+
connectAdapter(systemId, input) {
|
|
187
|
+
required(systemId, "SDK_SYSTEM_ID_REQUIRED");
|
|
188
|
+
const body = {
|
|
189
|
+
...input,
|
|
190
|
+
capabilities: [...input.capabilities ?? []]
|
|
191
|
+
};
|
|
192
|
+
delete body.idempotencyKey;
|
|
193
|
+
return this.request(
|
|
194
|
+
{
|
|
195
|
+
method: "POST",
|
|
196
|
+
path: `/v1/ai-systems/${encodeURIComponent(systemId)}/connections`,
|
|
197
|
+
body
|
|
198
|
+
},
|
|
199
|
+
{ idempotencyKey: input.idempotencyKey }
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
async connectOnce(input) {
|
|
203
|
+
const systemResult = await this.connectSystem(input);
|
|
204
|
+
if (!systemResult) return null;
|
|
205
|
+
const systemId = String(systemResult.body.system.id ?? "");
|
|
206
|
+
required(systemId, "SDK_SYSTEM_ID_MISSING");
|
|
207
|
+
const connectionResult = await this.connectAdapter(systemId, {
|
|
208
|
+
adapterType: input.adapterType,
|
|
209
|
+
adapterVersion: input.adapterVersion,
|
|
210
|
+
capabilities: input.capabilities,
|
|
211
|
+
config: input.config,
|
|
212
|
+
idempotencyKey: input.idempotencyKey
|
|
213
|
+
});
|
|
214
|
+
if (!connectionResult) return null;
|
|
215
|
+
const connectedId = String(connectionResult.body.connection?.id ?? "").trim();
|
|
216
|
+
if (connectedId) this.boundConnectionId = connectedId;
|
|
217
|
+
return {
|
|
218
|
+
status: connectionResult.status,
|
|
219
|
+
requestId: connectionResult.requestId,
|
|
220
|
+
body: {
|
|
221
|
+
system: systemResult.body.system,
|
|
222
|
+
connection: connectionResult.body.connection,
|
|
223
|
+
handshake: connectionResult.body.handshake
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
observe(event, options = {}) {
|
|
228
|
+
return this.request(buildObservationRequest(event), {
|
|
229
|
+
idempotencyKey: options.idempotencyKey ?? event.eventId
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
trace(event, options = {}) {
|
|
233
|
+
return this.observe({ ...event, eventType: "model.response" }, options);
|
|
234
|
+
}
|
|
235
|
+
searchRuntimeTraces(query = {}) {
|
|
236
|
+
const params = new URLSearchParams();
|
|
237
|
+
for (const [key, value] of Object.entries(query)) if (value !== void 0) params.set(key, String(value));
|
|
238
|
+
return this.request({ method: "GET", path: `/v1/runtime/traces/search${params.size ? `?${params.toString()}` : ""}` });
|
|
239
|
+
}
|
|
240
|
+
outcome(event, options = {}) {
|
|
241
|
+
return this.observe({ ...event, eventType: "business.outcome" }, options);
|
|
242
|
+
}
|
|
243
|
+
feedback(event, options = {}) {
|
|
244
|
+
return this.observe({ ...event, eventType: "feedback.user" }, options);
|
|
245
|
+
}
|
|
246
|
+
observeBatch(events, options = {}) {
|
|
247
|
+
if (!Array.isArray(events) || events.length < 1 || events.length > 100)
|
|
248
|
+
throw new Error("SDK_OBSERVATION_BATCH_INVALID");
|
|
249
|
+
if (events.some((event) => !event.organizationId?.trim() || !event.projectId?.trim() || !event.environmentId?.trim()))
|
|
250
|
+
throw new Error("SDK_OBSERVATION_SCOPE_REQUIRED");
|
|
251
|
+
return this.request(
|
|
252
|
+
{
|
|
253
|
+
method: "POST",
|
|
254
|
+
path: "/v1/observations/batch",
|
|
255
|
+
body: { events: [...events] }
|
|
256
|
+
},
|
|
257
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
observeSdk(delivery, options = {}) {
|
|
261
|
+
return this.request(
|
|
262
|
+
{ method: "POST", path: "/v1/observations/sdk", body: delivery },
|
|
263
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
discover(reviewState) {
|
|
267
|
+
return this.request({
|
|
268
|
+
method: "GET",
|
|
269
|
+
path: "/v1/discovery/candidates",
|
|
270
|
+
query: reviewState ? { reviewState } : void 0
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async createSignalDiscoveryProposal(input, options = {}) {
|
|
274
|
+
return this.request({ method: "POST", path: "/v1/signals/discovery-proposals", body: input }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey("sdk:signal-discovery-proposal", input) });
|
|
275
|
+
}
|
|
276
|
+
async reviewSignalDiscoveryProposal(proposalId, decision, options = {}) {
|
|
277
|
+
required(proposalId, "SDK_SIGNAL_PROPOSAL_ID_REQUIRED");
|
|
278
|
+
const path = `/v1/signals/discovery-proposals/${encodeURIComponent(proposalId)}/review`, body = { decision };
|
|
279
|
+
return this.request({ method: "POST", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`signal-review:${proposalId}`, body) });
|
|
280
|
+
}
|
|
281
|
+
async materializeSignalDiscovery(proposalId, input = {}, options = {}) {
|
|
282
|
+
required(proposalId, "SDK_SIGNAL_PROPOSAL_ID_REQUIRED");
|
|
283
|
+
const path = `/v1/signals/discovery-proposals/${encodeURIComponent(proposalId)}/materialize`;
|
|
284
|
+
return this.request({ method: "POST", path, body: input }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`signal-materialize:${proposalId}`, input) });
|
|
285
|
+
}
|
|
286
|
+
async createSignalMapping(input, options = {}) {
|
|
287
|
+
return this.request({ method: "POST", path: "/v1/signals/mappings", body: input }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey("sdk:signal-mapping", input) });
|
|
288
|
+
}
|
|
289
|
+
getSignalMapping(mappingId) {
|
|
290
|
+
required(mappingId, "SDK_SIGNAL_MAPPING_ID_REQUIRED");
|
|
291
|
+
return this.request({ method: "GET", path: `/v1/signals/mappings/${encodeURIComponent(mappingId)}` });
|
|
292
|
+
}
|
|
293
|
+
async reviewSignalMapping(mappingId, input = {}, options = {}) {
|
|
294
|
+
required(mappingId, "SDK_SIGNAL_MAPPING_ID_REQUIRED");
|
|
295
|
+
const path = `/v1/signals/mappings/${encodeURIComponent(mappingId)}/review`;
|
|
296
|
+
return this.request({ method: "POST", path, body: input }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`signal-mapping-review:${mappingId}`, input) });
|
|
297
|
+
}
|
|
298
|
+
connectionHealth(connectionId) {
|
|
299
|
+
required(connectionId, "SDK_CONNECTION_ID_REQUIRED");
|
|
300
|
+
return this.request({
|
|
301
|
+
method: "GET",
|
|
302
|
+
path: `/v1/connections/${encodeURIComponent(connectionId)}/health`
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
getConnection(connectionId) {
|
|
306
|
+
required(connectionId, "SDK_CONNECTION_ID_REQUIRED");
|
|
307
|
+
return this.request({
|
|
308
|
+
method: "GET",
|
|
309
|
+
path: `/v1/connections/${encodeURIComponent(connectionId)}`
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
listSystemConnections(systemId) {
|
|
313
|
+
required(systemId, "SDK_SYSTEM_ID_REQUIRED");
|
|
314
|
+
return this.request({
|
|
315
|
+
method: "GET",
|
|
316
|
+
path: `/v1/ai-systems/${encodeURIComponent(systemId)}/connections`
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
inventory(systemId) {
|
|
320
|
+
required(systemId, "SDK_SYSTEM_ID_REQUIRED");
|
|
321
|
+
return this.request({
|
|
322
|
+
method: "GET",
|
|
323
|
+
path: `/v1/ai-systems/${encodeURIComponent(systemId)}/inventory`
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
listPacks() {
|
|
327
|
+
return this.request({
|
|
328
|
+
method: "GET",
|
|
329
|
+
path: "/v1/packs"
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
async deletePack(packId, options = {}) {
|
|
333
|
+
required(packId, "SDK_PACK_ID_REQUIRED");
|
|
334
|
+
const path = `/v1/packs/${encodeURIComponent(packId)}`;
|
|
335
|
+
const body = { confirm: options.confirm === true };
|
|
336
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:pack:${packId}`, { path, body }) });
|
|
337
|
+
}
|
|
338
|
+
async importPack(input) {
|
|
339
|
+
if (!input?.manifest && !input?.archiveBase64)
|
|
340
|
+
throw new Error("SDK_PACK_MANIFEST_OR_ARCHIVE_REQUIRED");
|
|
341
|
+
if (input.archiveBase64 && !input.fileName)
|
|
342
|
+
throw new Error("SDK_PACK_FILE_NAME_REQUIRED");
|
|
343
|
+
const body = {
|
|
344
|
+
...input.manifest ? { manifest: input.manifest } : {},
|
|
345
|
+
objects: input.objects ?? {},
|
|
346
|
+
...input.archiveBase64 ? { archiveBase64: input.archiveBase64, fileName: input.fileName } : {}
|
|
347
|
+
};
|
|
348
|
+
return this.request(
|
|
349
|
+
{
|
|
350
|
+
method: "POST",
|
|
351
|
+
path: "/v1/packs/import",
|
|
352
|
+
body
|
|
353
|
+
},
|
|
354
|
+
{ idempotencyKey: input.idempotencyKey ?? await contentIdempotencyKey("sdk:pack:import", body) }
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
async installPack(packId, variables = {}) {
|
|
358
|
+
required(packId, "SDK_PACK_ID_REQUIRED");
|
|
359
|
+
const path = `/v1/packs/${encodeURIComponent(packId)}/install`;
|
|
360
|
+
const body = { variables };
|
|
361
|
+
return this.request(
|
|
362
|
+
{ method: "POST", path, body },
|
|
363
|
+
{ idempotencyKey: await contentIdempotencyKey(`sdk:pack:install:${packId}`, body) }
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
async evaluatePack(packId, input) {
|
|
367
|
+
required(packId, "SDK_PACK_ID_REQUIRED");
|
|
368
|
+
required(input?.evaluationId, "SDK_EVALUATION_ID_REQUIRED");
|
|
369
|
+
const path = `/v1/packs/${encodeURIComponent(packId)}/evaluate`;
|
|
370
|
+
const body = { evaluationId: input.evaluationId };
|
|
371
|
+
return this.request({ method: "POST", path, body }, { idempotencyKey: await contentIdempotencyKey(`sdk:pack:evaluate:${packId}`, body) });
|
|
372
|
+
}
|
|
373
|
+
async approvePack(packId, options = {}) {
|
|
374
|
+
required(packId, "SDK_PACK_ID_REQUIRED");
|
|
375
|
+
const body = options.manualApproval ? { manualApproval: true, reason: required(options.reason ?? "", "SDK_MANUAL_PACK_APPROVAL_REASON_REQUIRED") } : {};
|
|
376
|
+
return this.request(
|
|
377
|
+
{
|
|
378
|
+
method: "POST",
|
|
379
|
+
path: `/v1/packs/${encodeURIComponent(packId)}/approve`,
|
|
380
|
+
body
|
|
381
|
+
},
|
|
382
|
+
{ idempotencyKey: await contentIdempotencyKey(`sdk:pack:approve:${packId}`, body) }
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
async releasePack(packId) {
|
|
386
|
+
required(packId, "SDK_PACK_ID_REQUIRED");
|
|
387
|
+
return this.request(
|
|
388
|
+
{
|
|
389
|
+
method: "POST",
|
|
390
|
+
path: `/v1/packs/${encodeURIComponent(packId)}/release`,
|
|
391
|
+
body: {}
|
|
392
|
+
},
|
|
393
|
+
{ idempotencyKey: await contentIdempotencyKey(`sdk:pack:release:${packId}`, {}) }
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
async previewPackUpgrade(packId, incoming) {
|
|
397
|
+
required(packId, "SDK_PACK_ID_REQUIRED");
|
|
398
|
+
if (!incoming || typeof incoming !== "object")
|
|
399
|
+
throw new Error("SDK_PACK_UPGRADE_INPUT_REQUIRED");
|
|
400
|
+
return this.request(
|
|
401
|
+
{
|
|
402
|
+
method: "POST",
|
|
403
|
+
path: `/v1/packs/${encodeURIComponent(packId)}/upgrade-preview`,
|
|
404
|
+
body: { incoming }
|
|
405
|
+
},
|
|
406
|
+
{ idempotencyKey: await contentIdempotencyKey(`sdk:pack:upgrade-preview:${packId}`, { incoming }) }
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
executeRuntime(input) {
|
|
410
|
+
return this.request(
|
|
411
|
+
{
|
|
412
|
+
method: "POST",
|
|
413
|
+
path: "/v1/runtime/execute",
|
|
414
|
+
body: buildRuntimeRequest(
|
|
415
|
+
input
|
|
416
|
+
)
|
|
417
|
+
},
|
|
418
|
+
{ idempotencyKey: input.idempotencyKey, enforcing: true }
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
resolveRuntime(input) {
|
|
422
|
+
if (!input.behaviorPoint && !input.executionBoundary && !input.semanticContext)
|
|
423
|
+
throw new Error("SDK_RUNTIME_ROUTE_REQUIRED");
|
|
424
|
+
return this.request(
|
|
425
|
+
{
|
|
426
|
+
method: "POST",
|
|
427
|
+
path: "/v1/runtime/resolve",
|
|
428
|
+
body: {
|
|
429
|
+
...input.behaviorPoint ? { behaviorPoint: input.behaviorPoint } : {},
|
|
430
|
+
...input.executionBoundary ? { executionBoundary: input.executionBoundary } : {},
|
|
431
|
+
...input.semanticContext ? { semanticContext: input.semanticContext } : {}
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
idempotencyKey: input.idempotencyKey ?? `sdk:resolve:${input.behaviorPoint ?? "boundary"}`,
|
|
436
|
+
enforcing: true
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
/** Resolve a runtime route from a governed execution boundary and semantic context. */
|
|
441
|
+
resolveBoundary(input) {
|
|
442
|
+
if (!input.executionBoundary || typeof input.executionBoundary !== "object")
|
|
443
|
+
throw new Error("SDK_EXECUTION_BOUNDARY_REQUIRED");
|
|
444
|
+
return this.resolveRuntime({
|
|
445
|
+
executionBoundary: input.executionBoundary,
|
|
446
|
+
...input.semanticContext ? { semanticContext: input.semanticContext } : {},
|
|
447
|
+
idempotencyKey: input.idempotencyKey
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
resolveSemantic(input) {
|
|
451
|
+
required(input.input, "SDK_SEMANTIC_INPUT_REQUIRED");
|
|
452
|
+
const { idempotencyKey, requestId, ...body } = input;
|
|
453
|
+
const resolvedRequestId = requestId ?? `sdk-semantic-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
454
|
+
return this.request(
|
|
455
|
+
{
|
|
456
|
+
method: "POST",
|
|
457
|
+
path: "/v1/semantic/resolve",
|
|
458
|
+
body: { ...body, requestId: resolvedRequestId }
|
|
459
|
+
},
|
|
460
|
+
{ idempotencyKey: idempotencyKey ?? `sdk:semantic:${resolvedRequestId}` }
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
async resolveSemanticRuntime(input) {
|
|
464
|
+
const semantic = await this.resolveSemantic(input);
|
|
465
|
+
if (!semantic) return null;
|
|
466
|
+
const body = semantic.body;
|
|
467
|
+
const receipt = body.receipt && typeof body.receipt === "object" ? body.receipt : {};
|
|
468
|
+
if (receipt.outcome !== "select" || typeof receipt.taskKey !== "string")
|
|
469
|
+
return { semantic, runtime: null };
|
|
470
|
+
const capsules = Array.isArray(body.capsules) ? body.capsules : [];
|
|
471
|
+
const firstCapsule = capsules[0] && typeof capsules[0] === "object" ? capsules[0] : {};
|
|
472
|
+
const behaviorPoint = typeof firstCapsule.behaviorKey === "string" && firstCapsule.behaviorKey.trim() ? firstCapsule.behaviorKey : receipt.taskKey;
|
|
473
|
+
const runtime = input.executionBoundary ? await this.resolveBoundary({
|
|
474
|
+
executionBoundary: input.executionBoundary,
|
|
475
|
+
semanticContext: {
|
|
476
|
+
taskKey: receipt.taskKey,
|
|
477
|
+
behaviorPoint,
|
|
478
|
+
capsule: firstCapsule
|
|
479
|
+
},
|
|
480
|
+
idempotencyKey: input.idempotencyKey ? `sdk:runtime:${input.idempotencyKey}` : void 0
|
|
481
|
+
}) : await this.resolveRuntime({
|
|
482
|
+
semanticContext: { taskKey: receipt.taskKey, behaviorPoint, capsule: firstCapsule },
|
|
483
|
+
idempotencyKey: input.idempotencyKey ? `sdk:runtime:${input.idempotencyKey}` : void 0
|
|
484
|
+
});
|
|
485
|
+
return { semantic, runtime };
|
|
486
|
+
}
|
|
487
|
+
async authorizeHostAction(input) {
|
|
488
|
+
required(input.scope.organizationId, "SDK_ACTION_ORGANIZATION_REQUIRED");
|
|
489
|
+
required(input.scope.projectId, "SDK_ACTION_PROJECT_REQUIRED");
|
|
490
|
+
required(input.scope.environmentId, "SDK_ACTION_ENVIRONMENT_REQUIRED");
|
|
491
|
+
required(input.executionId, "SDK_ACTION_EXECUTION_REQUIRED");
|
|
492
|
+
return this.request({
|
|
493
|
+
method: "POST",
|
|
494
|
+
path: "/v1/runtime/actions/authorize",
|
|
495
|
+
body: { executionId: input.executionId, scope: input.scope, context: input.context, proposal: input.proposal }
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
listBehaviorStrategies() {
|
|
499
|
+
return this.request({
|
|
500
|
+
method: "GET",
|
|
501
|
+
path: "/v1/behavior-strategies"
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
async deleteBehaviorStrategy(strategyId, options = {}) {
|
|
505
|
+
required(strategyId, "SDK_STRATEGY_ID_REQUIRED");
|
|
506
|
+
const path = `/v1/behavior-strategies/${encodeURIComponent(strategyId)}`, body = { confirm: options.confirm === true };
|
|
507
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:strategy:${strategyId}`, { path, body }) });
|
|
508
|
+
}
|
|
509
|
+
getBehaviorStrategy(strategyId) {
|
|
510
|
+
required(strategyId, "SDK_STRATEGY_ID_REQUIRED");
|
|
511
|
+
return this.request({
|
|
512
|
+
method: "GET",
|
|
513
|
+
path: `/v1/behavior-strategies/${encodeURIComponent(strategyId)}`
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
createBehaviorStrategy(definition, id) {
|
|
517
|
+
if (!definition) throw new Error("SDK_STRATEGY_DEFINITION_REQUIRED");
|
|
518
|
+
return this.request({
|
|
519
|
+
method: "POST",
|
|
520
|
+
path: "/v1/behavior-strategies",
|
|
521
|
+
body: { definition, ...id ? { id } : {} }
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
listSemanticTasks() {
|
|
525
|
+
return this.request({ method: "GET", path: "/v1/semantic-tasks" });
|
|
526
|
+
}
|
|
527
|
+
async deleteSemanticTask(taskId, options = {}) {
|
|
528
|
+
required(taskId, "SDK_SEMANTIC_TASK_ID_REQUIRED");
|
|
529
|
+
const path = `/v1/semantic-tasks/${encodeURIComponent(taskId)}`, body = { confirm: options.confirm === true };
|
|
530
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:semantic-task:${taskId}`, { path, body }) });
|
|
531
|
+
}
|
|
532
|
+
async createSemanticTask(task, options = {}) {
|
|
533
|
+
required(task?.taskKey, "SDK_SEMANTIC_TASK_KEY_REQUIRED");
|
|
534
|
+
const body = { ...task, ...options.id ? { id: options.id } : {} };
|
|
535
|
+
return this.request({ method: "POST", path: "/v1/semantic-tasks", body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:semantic-task:${task.taskKey}`, body) });
|
|
536
|
+
}
|
|
537
|
+
listBehaviorPoints() {
|
|
538
|
+
return this.request({ method: "GET", path: "/v1/behavior-points" });
|
|
539
|
+
}
|
|
540
|
+
async deleteBehaviorPoint(pointId, options = {}) {
|
|
541
|
+
required(pointId, "SDK_BEHAVIOR_POINT_ID_REQUIRED");
|
|
542
|
+
const path = `/v1/behavior-points/${encodeURIComponent(pointId)}`, body = { confirm: options.confirm === true };
|
|
543
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:behavior-point:${pointId}`, { path, body }) });
|
|
544
|
+
}
|
|
545
|
+
async createBehaviorPoint(point, options = {}) {
|
|
546
|
+
required(point?.key, "SDK_BEHAVIOR_POINT_KEY_REQUIRED");
|
|
547
|
+
if (!point.taskId && !point.taskIds?.length) throw new Error("SDK_BEHAVIOR_POINT_TASK_REQUIRED");
|
|
548
|
+
const body = { ...point, ...options.id ? { id: options.id } : {} };
|
|
549
|
+
return this.request({ method: "POST", path: "/v1/behavior-points", body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:behavior-point:${point.key}`, body) });
|
|
550
|
+
}
|
|
551
|
+
createBehaviorStrategyVariant(strategyId, variant, options = {}) {
|
|
552
|
+
required(strategyId, "SDK_STRATEGY_ID_REQUIRED");
|
|
553
|
+
if (!variant || typeof variant !== "object")
|
|
554
|
+
throw new Error("SDK_STRATEGY_VARIANT_REQUIRED");
|
|
555
|
+
return this.request(
|
|
556
|
+
{
|
|
557
|
+
method: "POST",
|
|
558
|
+
path: `/v1/behavior-strategies/${encodeURIComponent(strategyId)}/variants`,
|
|
559
|
+
body: variant
|
|
560
|
+
},
|
|
561
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
validateBehaviorStrategy(strategyId) {
|
|
565
|
+
required(strategyId, "SDK_STRATEGY_ID_REQUIRED");
|
|
566
|
+
return this.request({
|
|
567
|
+
method: "POST",
|
|
568
|
+
path: `/v1/behavior-strategies/${encodeURIComponent(strategyId)}/validate`,
|
|
569
|
+
body: {}
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
activateBehaviorStrategy(strategyId) {
|
|
573
|
+
required(strategyId, "SDK_STRATEGY_ID_REQUIRED");
|
|
574
|
+
return this.request({ method: "POST", path: `/v1/behavior-strategies/${encodeURIComponent(strategyId)}/activate`, body: {} });
|
|
575
|
+
}
|
|
576
|
+
simulateBehaviorStrategy(strategyId, initialState = {}) {
|
|
577
|
+
required(strategyId, "SDK_STRATEGY_ID_REQUIRED");
|
|
578
|
+
return this.request({
|
|
579
|
+
method: "POST",
|
|
580
|
+
path: `/v1/behavior-strategies/${encodeURIComponent(strategyId)}/simulate`,
|
|
581
|
+
body: { initialState }
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
startStrategyExecution(input) {
|
|
585
|
+
required(input.artifactDigest, "SDK_ARTIFACT_DIGEST_REQUIRED");
|
|
586
|
+
for (const value of [
|
|
587
|
+
input.organizationId,
|
|
588
|
+
input.projectId,
|
|
589
|
+
input.environmentId,
|
|
590
|
+
input.revisionId,
|
|
591
|
+
input.idempotencyKey,
|
|
592
|
+
input.nonce,
|
|
593
|
+
input.decisionPlanId,
|
|
594
|
+
input.policy
|
|
595
|
+
])
|
|
596
|
+
required(value, "SDK_STRATEGY_START_FIELD_REQUIRED");
|
|
597
|
+
if (input.behaviorPoint !== void 0)
|
|
598
|
+
required(input.behaviorPoint, "SDK_BEHAVIOR_POINT_REQUIRED");
|
|
599
|
+
if (!Array.isArray(input.candidates) || input.candidates.length === 0)
|
|
600
|
+
throw new Error("SDK_STRATEGY_CANDIDATES_REQUIRED");
|
|
601
|
+
if (!Number.isFinite(input.expiresAt))
|
|
602
|
+
throw new Error("SDK_EXPIRY_REQUIRED");
|
|
603
|
+
const body = {
|
|
604
|
+
...buildRuntimeRequest(input),
|
|
605
|
+
artifactDigest: input.artifactDigest,
|
|
606
|
+
...input.behaviorPoint ? { behaviorPoint: input.behaviorPoint } : {},
|
|
607
|
+
...input.executionBoundary ? { executionBoundary: input.executionBoundary } : {},
|
|
608
|
+
...input.traceId ? { traceId: input.traceId } : {},
|
|
609
|
+
...input.executionId ? { executionId: input.executionId } : {},
|
|
610
|
+
...input.state ? { state: input.state } : {}
|
|
611
|
+
};
|
|
612
|
+
return this.request(
|
|
613
|
+
{ method: "POST", path: "/v1/runtime/strategy-executions", body },
|
|
614
|
+
{ idempotencyKey: input.idempotencyKey }
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
getStrategyExecution(executionId) {
|
|
618
|
+
required(executionId, "SDK_EXECUTION_ID_REQUIRED");
|
|
619
|
+
return this.request({
|
|
620
|
+
method: "GET",
|
|
621
|
+
path: `/v1/runtime/strategy-executions/${encodeURIComponent(executionId)}`
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
getStrategyExecutionTrace(executionId) {
|
|
625
|
+
required(executionId, "SDK_EXECUTION_ID_REQUIRED");
|
|
626
|
+
return this.request({
|
|
627
|
+
method: "GET",
|
|
628
|
+
path: `/v1/runtime/strategy-executions/${encodeURIComponent(executionId)}/trace`
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
continueStrategyExecution(executionId, continuation) {
|
|
632
|
+
required(executionId, "SDK_EXECUTION_ID_REQUIRED");
|
|
633
|
+
const input = typeof continuation === "string" ? { authorityDigest: continuation } : continuation ?? {};
|
|
634
|
+
return this.request({
|
|
635
|
+
method: "POST",
|
|
636
|
+
path: `/v1/runtime/strategy-executions/${encodeURIComponent(executionId)}/continue`,
|
|
637
|
+
body: {
|
|
638
|
+
...input.authorityDigest ? { authorityDigest: input.authorityDigest } : {},
|
|
639
|
+
...input.state ? { state: input.state } : {},
|
|
640
|
+
...input.references ? { references: input.references } : {}
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
cancelStrategyExecution(executionId) {
|
|
645
|
+
required(executionId, "SDK_EXECUTION_ID_REQUIRED");
|
|
646
|
+
return this.request({
|
|
647
|
+
method: "POST",
|
|
648
|
+
path: `/v1/runtime/strategy-executions/${encodeURIComponent(executionId)}/cancel`,
|
|
649
|
+
body: {}
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
resolveTaskRun(input) {
|
|
653
|
+
required(input.input, "SDK_TASKRUN_INPUT_REQUIRED");
|
|
654
|
+
if (input.requestedMode) required(input.requestedMode, "SDK_TASKRUN_MODE_REQUIRED");
|
|
655
|
+
const connectionId = input.connectionId ?? this.boundConnectionId;
|
|
656
|
+
return this.request({ method: "POST", path: "/v1/task-runs/resolve", body: { ...input, ...connectionId ? { connectionId } : {} } }, { idempotencyKey: `sdk:task-run:resolve:${input.input}` });
|
|
657
|
+
}
|
|
658
|
+
startTaskRun(input, options = {}) {
|
|
659
|
+
for (const value of [input.taskRunId, input.requestId]) required(value, "SDK_TASKRUN_ADMISSION_FIELD_REQUIRED");
|
|
660
|
+
if (input.input === void 0 || input.input === null) throw new Error("SDK_TASKRUN_INPUT_REQUIRED");
|
|
661
|
+
const body = { ...input, ...input.ownerActorRef ? { ownerActorRef: input.ownerActorRef } : {}, ...input.requestedMode ? { requestedMode: input.requestedMode } : {}, ...!input.connectionId && this.boundConnectionId ? { connectionId: this.boundConnectionId } : {} };
|
|
662
|
+
return this.request({ method: "POST", path: "/v1/task-runs", body }, { idempotencyKey: options.idempotencyKey ?? `sdk:task-run:start:${input.requestId}` });
|
|
663
|
+
}
|
|
664
|
+
startTaskRunFromPrompt(input, options = {}) {
|
|
665
|
+
if (input.input === void 0 || input.input === null) throw new Error("SDK_TASKRUN_INPUT_REQUIRED");
|
|
666
|
+
const id = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
667
|
+
const requestId = input.requestId ?? `sdk-task-run-${id()}`;
|
|
668
|
+
return this.startTaskRun({ taskRunId: input.taskRunId ?? `task-run-${id()}`, requestId, input: input.input, ...input.conversationId ? { conversationId: input.conversationId } : {}, ...input.episodeId ? { episodeId: input.episodeId } : {}, ...input.traceRootRef ? { traceRootRef: input.traceRootRef } : {}, ...input.budget ? { budget: input.budget } : {}, ...input.connectionId ? { connectionId: input.connectionId } : {} }, options);
|
|
669
|
+
}
|
|
670
|
+
getTaskRun(taskRunId) {
|
|
671
|
+
required(taskRunId, "SDK_TASKRUN_ID_REQUIRED");
|
|
672
|
+
return this.request({ method: "GET", path: `/v1/task-runs/${encodeURIComponent(taskRunId)}` });
|
|
673
|
+
}
|
|
674
|
+
listTaskRunEvents(taskRunId) {
|
|
675
|
+
required(taskRunId, "SDK_TASKRUN_ID_REQUIRED");
|
|
676
|
+
return this.request({ method: "GET", path: `/v1/task-runs/${encodeURIComponent(taskRunId)}/events` });
|
|
677
|
+
}
|
|
678
|
+
continueTaskRun(taskRunId, expectedVersion, options = {}) {
|
|
679
|
+
required(taskRunId, "SDK_TASKRUN_ID_REQUIRED");
|
|
680
|
+
if (!Number.isInteger(expectedVersion) || expectedVersion < 0) throw new Error("SDK_TASKRUN_STATE_VERSION_REQUIRED");
|
|
681
|
+
return this.request({ method: "POST", path: `/v1/task-runs/${encodeURIComponent(taskRunId)}/continue`, body: { expectedVersion, ...options.cursor ? { cursor: options.cursor } : {} } }, { idempotencyKey: options.idempotencyKey ?? `sdk:task-run:continue:${taskRunId}:${expectedVersion}` });
|
|
682
|
+
}
|
|
683
|
+
cancelTaskRun(taskRunId, expectedVersion, options = {}) {
|
|
684
|
+
required(taskRunId, "SDK_TASKRUN_ID_REQUIRED");
|
|
685
|
+
if (!Number.isInteger(expectedVersion) || expectedVersion < 0) throw new Error("SDK_TASKRUN_STATE_VERSION_REQUIRED");
|
|
686
|
+
return this.request({ method: "POST", path: `/v1/task-runs/${encodeURIComponent(taskRunId)}/cancel`, body: { expectedVersion } }, { idempotencyKey: options.idempotencyKey ?? `sdk:task-run:cancel:${taskRunId}:${expectedVersion}` });
|
|
687
|
+
}
|
|
688
|
+
retryTaskRun(taskRunId, expectedVersion, options = {}) {
|
|
689
|
+
required(taskRunId, "SDK_TASKRUN_ID_REQUIRED");
|
|
690
|
+
if (!Number.isInteger(expectedVersion) || expectedVersion < 0) throw new Error("SDK_TASKRUN_STATE_VERSION_REQUIRED");
|
|
691
|
+
return this.request({ method: "POST", path: `/v1/task-runs/${encodeURIComponent(taskRunId)}/retry`, body: { expectedVersion } }, { idempotencyKey: options.idempotencyKey ?? `sdk:task-run:retry:${taskRunId}:${expectedVersion}` });
|
|
692
|
+
}
|
|
693
|
+
listBehaviorPrograms() {
|
|
694
|
+
return this.request({
|
|
695
|
+
method: "GET",
|
|
696
|
+
path: "/v1/behavior-programs"
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
listCapabilities(options = {}) {
|
|
700
|
+
const query = options.surface ? `?surface=${encodeURIComponent(options.surface)}` : "";
|
|
701
|
+
return this.request({ method: "GET", path: `/v1/capabilities${query}` });
|
|
702
|
+
}
|
|
703
|
+
getBehaviorProgram(programId) {
|
|
704
|
+
required(programId, "SDK_PROGRAM_ID_REQUIRED");
|
|
705
|
+
return this.request({
|
|
706
|
+
method: "GET",
|
|
707
|
+
path: `/v1/behavior-programs/${encodeURIComponent(programId)}`
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
async deleteBehaviorProgram(programId, options = {}) {
|
|
711
|
+
required(programId, "SDK_PROGRAM_ID_REQUIRED");
|
|
712
|
+
const path = `/v1/behavior-programs/${encodeURIComponent(programId)}`, body = { confirm: options.confirm === true };
|
|
713
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:behavior-program:${programId}`, { path, body }) });
|
|
714
|
+
}
|
|
715
|
+
async deleteBehaviorProgramCascade(programId, options = {}) {
|
|
716
|
+
required(programId, "SDK_PROGRAM_ID_REQUIRED");
|
|
717
|
+
const path = `/v1/behavior-programs/${encodeURIComponent(programId)}/cascade`, body = { confirm: options.confirm === true, purgeRuntime: options.purgeRuntime === true };
|
|
718
|
+
return this.request({ method: "DELETE", path, body }, { idempotencyKey: options.idempotencyKey ?? await contentIdempotencyKey(`sdk:delete:behavior-program-cascade:${programId}`, { path, body }) });
|
|
719
|
+
}
|
|
720
|
+
createBehaviorProgram(program, options = {}) {
|
|
721
|
+
if (!program || typeof program !== "object")
|
|
722
|
+
throw new Error("SDK_PROGRAM_REQUIRED");
|
|
723
|
+
return this.request(
|
|
724
|
+
{ method: "POST", path: "/v1/behavior-programs", body: program },
|
|
725
|
+
{
|
|
726
|
+
idempotencyKey: options.idempotencyKey ?? `sdk:program:${String(program.id ?? "")}`
|
|
727
|
+
}
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
attestCapabilities(attestation, options = {}) {
|
|
731
|
+
return this.request(
|
|
732
|
+
{
|
|
733
|
+
method: "POST",
|
|
734
|
+
path: "/v1/runtime/capabilities/attest",
|
|
735
|
+
body: attestation
|
|
736
|
+
},
|
|
737
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
resolveCapabilities(attestationId, request, options = {}) {
|
|
741
|
+
required(attestationId, "SDK_ATTESTATION_ID_REQUIRED");
|
|
742
|
+
return this.request(
|
|
743
|
+
{
|
|
744
|
+
method: "POST",
|
|
745
|
+
path: "/v1/runtime/capabilities/resolve",
|
|
746
|
+
body: { attestationId, request }
|
|
747
|
+
},
|
|
748
|
+
{ idempotencyKey: options.idempotencyKey, enforcing: true }
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
recordCapabilityTrace(decisionDigest, trace, options = {}) {
|
|
752
|
+
required(decisionDigest, "SDK_DECISION_DIGEST_REQUIRED");
|
|
753
|
+
return this.request(
|
|
754
|
+
{
|
|
755
|
+
method: "POST",
|
|
756
|
+
path: "/v1/runtime/capabilities/traces",
|
|
757
|
+
body: { decisionDigest, trace }
|
|
758
|
+
},
|
|
759
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
resolveModelServing(request, options = {}) {
|
|
763
|
+
return this.request(
|
|
764
|
+
{ method: "POST", path: "/v1/model-serving/resolve", body: request },
|
|
765
|
+
{ idempotencyKey: options.idempotencyKey, enforcing: true }
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
recordModelServingTrace(trace, selection, options = {}) {
|
|
769
|
+
return this.request(
|
|
770
|
+
{
|
|
771
|
+
method: "POST",
|
|
772
|
+
path: "/v1/model-serving/traces",
|
|
773
|
+
body: { trace, selection }
|
|
774
|
+
},
|
|
775
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
submitTeachEvidence(evidence, options = {}) {
|
|
779
|
+
return this.request(
|
|
780
|
+
{ method: "POST", path: "/v1/teach/evidence", body: evidence },
|
|
781
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
listConversationArchive(input = {}) {
|
|
785
|
+
return this.request({ method: "GET", path: "/v1/teach/conversations", query: { ...input.limit === void 0 ? {} : { limit: String(input.limit) }, ...input.cursor ? { cursor: input.cursor } : {} } });
|
|
786
|
+
}
|
|
787
|
+
importConversationArchive(conversations, options = {}) {
|
|
788
|
+
if (!Array.isArray(conversations) || !conversations.length) throw new Error("SDK_CONVERSATION_IMPORT_REQUIRED");
|
|
789
|
+
return this.request({ method: "POST", path: "/v1/teach/conversations/import", body: { conversations: [...conversations] } }, { idempotencyKey: options.idempotencyKey });
|
|
790
|
+
}
|
|
791
|
+
revokeConversation(evidenceId, reason, options = {}) {
|
|
792
|
+
required(evidenceId, "SDK_CONVERSATION_ID_REQUIRED");
|
|
793
|
+
required(reason, "SDK_CONVERSATION_REVOCATION_REASON_REQUIRED");
|
|
794
|
+
return this.request({ method: "POST", path: `/v1/teach/conversations/${encodeURIComponent(evidenceId)}/revoke`, body: { reason } }, { idempotencyKey: options.idempotencyKey });
|
|
795
|
+
}
|
|
796
|
+
createTeachAnalysis(evidenceIds, options = {}) {
|
|
797
|
+
if (!Array.isArray(evidenceIds) || evidenceIds.length < 1) throw new Error("SDK_TEACH_ANALYSIS_EVIDENCE_REQUIRED");
|
|
798
|
+
const body = { evidenceIds: [...evidenceIds] };
|
|
799
|
+
if (options.analysisId) body.analysisId = options.analysisId;
|
|
800
|
+
if (options.controlPointId) body.controlPointId = options.controlPointId;
|
|
801
|
+
if (options.summary) body.summary = options.summary;
|
|
802
|
+
const requestKey = JSON.stringify([evidenceIds, options.analysisId ?? "", options.controlPointId ?? "", options.summary ?? ""]);
|
|
803
|
+
return this.request({ method: "POST", path: "/v1/teach/analyses", body }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:analysis:${requestKey}` });
|
|
804
|
+
}
|
|
805
|
+
getTeachAnalysis(analysisId) {
|
|
806
|
+
required(analysisId, "SDK_TEACH_ANALYSIS_ID_REQUIRED");
|
|
807
|
+
return this.request({ method: "GET", path: `/v1/teach/analyses/${encodeURIComponent(analysisId)}` });
|
|
808
|
+
}
|
|
809
|
+
createCrossConversationTeachAnalysis(input, options = {}) {
|
|
810
|
+
if (!Array.isArray(input.conversationRefs) || input.conversationRefs.length < 2) throw new Error("SDK_TEACH_CROSS_CONVERSATION_REFS_REQUIRED");
|
|
811
|
+
const body = { conversationRefs: input.conversationRefs };
|
|
812
|
+
if (input.analysisId) body.analysisId = input.analysisId;
|
|
813
|
+
if (input.controlPointId) body.controlPointId = input.controlPointId;
|
|
814
|
+
if (input.objective) body.objective = input.objective;
|
|
815
|
+
return this.request({ method: "POST", path: "/v1/teach/cross-conversation-analyses", body }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:cross-conversation:${JSON.stringify(body)}` });
|
|
816
|
+
}
|
|
817
|
+
createTeachProposal(input, options = {}) {
|
|
818
|
+
return this.request({ method: "POST", path: "/v1/teach/proposals", body: input }, { idempotencyKey: options.idempotencyKey });
|
|
819
|
+
}
|
|
820
|
+
getTeachRunBehaviorSet(teachRunId) {
|
|
821
|
+
required(teachRunId, "SDK_TEACH_RUN_ID_REQUIRED");
|
|
822
|
+
return this.request({ method: "GET", path: `/v1/teach/runs/${encodeURIComponent(teachRunId)}/behavior-set` });
|
|
823
|
+
}
|
|
824
|
+
listTeachRuns(input = {}) {
|
|
825
|
+
return this.request({ method: "GET", path: "/v1/teach/runs", query: { ...input.limit === void 0 ? {} : { limit: String(input.limit) }, ...input.cursor ? { cursor: input.cursor } : {}, ...input.status ? { status: input.status } : {} } });
|
|
826
|
+
}
|
|
827
|
+
getTeachRunRecommendation(teachRunId) {
|
|
828
|
+
required(teachRunId, "SDK_TEACH_RUN_ID_REQUIRED");
|
|
829
|
+
return this.request({ method: "GET", path: `/v1/teach/runs/${encodeURIComponent(teachRunId)}/recommendation` });
|
|
830
|
+
}
|
|
831
|
+
acceptTeachRecommendation(teachRunId, actorId, options = {}) {
|
|
832
|
+
required(teachRunId, "SDK_TEACH_RUN_ID_REQUIRED");
|
|
833
|
+
required(actorId, "SDK_TEACH_RECOMMENDATION_ACTOR_REQUIRED");
|
|
834
|
+
return this.request({ method: "POST", path: `/v1/teach/runs/${encodeURIComponent(teachRunId)}/recommendation/accept`, body: { actorId } }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:recommendation:accept:${teachRunId}` });
|
|
835
|
+
}
|
|
836
|
+
rejectTeachRecommendation(teachRunId, reason, options = {}) {
|
|
837
|
+
required(teachRunId, "SDK_TEACH_RUN_ID_REQUIRED");
|
|
838
|
+
required(reason, "SDK_TEACH_RECOMMENDATION_REASON_REQUIRED");
|
|
839
|
+
return this.request({ method: "POST", path: `/v1/teach/runs/${encodeURIComponent(teachRunId)}/recommendation/reject`, body: { reason } }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:recommendation:reject:${teachRunId}` });
|
|
840
|
+
}
|
|
841
|
+
editTeachRecommendation(teachRunId, input, options = {}) {
|
|
842
|
+
required(teachRunId, "SDK_TEACH_RUN_ID_REQUIRED");
|
|
843
|
+
required(input?.revisionId, "SDK_TEACH_RECOMMENDATION_REVISION_ID_REQUIRED");
|
|
844
|
+
required(input?.reason, "SDK_TEACH_RECOMMENDATION_REASON_REQUIRED");
|
|
845
|
+
return this.request({ method: "POST", path: `/v1/teach/runs/${encodeURIComponent(teachRunId)}/recommendation/edit`, body: input }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:recommendation:edit:${input.revisionId}` });
|
|
846
|
+
}
|
|
847
|
+
transitionTeachProposal(proposalId, transition, input = {}, options = {}) {
|
|
848
|
+
required(proposalId, "SDK_TEACH_PROPOSAL_ID_REQUIRED");
|
|
849
|
+
return this.request({ method: "POST", path: `/v1/teach/proposals/${encodeURIComponent(proposalId)}/${transition}`, body: input }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:${transition}:${proposalId}` });
|
|
850
|
+
}
|
|
851
|
+
requestTeachProposalChanges(proposalId, feedback, options = {}) {
|
|
852
|
+
required(proposalId, "SDK_TEACH_PROPOSAL_ID_REQUIRED");
|
|
853
|
+
required(feedback, "SDK_TEACH_REQUEST_CHANGES_FEEDBACK_REQUIRED");
|
|
854
|
+
return this.request({ method: "POST", path: `/v1/teach/proposals/${encodeURIComponent(proposalId)}/request-changes`, body: { feedback } }, { idempotencyKey: options.idempotencyKey ?? `sdk:teach:request-changes:${proposalId}:${feedback}` });
|
|
855
|
+
}
|
|
856
|
+
startEvaluation(input, options = {}) {
|
|
857
|
+
return this.request(
|
|
858
|
+
{ method: "POST", path: "/v1/evaluations", body: input },
|
|
859
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
/** Request a server-executed OFF/current-ON/candidate-ON comparison.
|
|
863
|
+
* The server owns arm execution and receipts; callers cannot submit outputs.
|
|
864
|
+
*/
|
|
865
|
+
startComparativeEvaluation(input, options = {}) {
|
|
866
|
+
if (!input.comparisonPlan || typeof input.comparisonPlan !== "object")
|
|
867
|
+
throw new Error("SDK_COMPARISON_PLAN_REQUIRED");
|
|
868
|
+
return this.startEvaluation(input, options);
|
|
869
|
+
}
|
|
870
|
+
evaluateQuality(input, options = {}) {
|
|
871
|
+
return this.request(
|
|
872
|
+
{ method: "POST", path: "/v1/quality/evaluate", body: input },
|
|
873
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
getEvaluation(evaluationId) {
|
|
877
|
+
required(evaluationId, "SDK_EVALUATION_ID_REQUIRED");
|
|
878
|
+
return this.request({
|
|
879
|
+
method: "GET",
|
|
880
|
+
path: `/v1/evaluations/${encodeURIComponent(evaluationId)}`
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
getEvaluationComparison(evaluationId) {
|
|
884
|
+
required(evaluationId, "SDK_EVALUATION_ID_REQUIRED");
|
|
885
|
+
return this.request({
|
|
886
|
+
method: "GET",
|
|
887
|
+
path: `/v1/evaluations/${encodeURIComponent(evaluationId)}/comparison`
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
reviewComparativeFinding(evaluationId, findingId, input, options = {}) {
|
|
891
|
+
required(evaluationId, "SDK_EVALUATION_ID_REQUIRED");
|
|
892
|
+
required(findingId, "SDK_COMPARATIVE_FINDING_ID_REQUIRED");
|
|
893
|
+
if (!["ACCEPT", "REJECT", "EDIT", "MORE_EVIDENCE"].includes(input?.decision)) throw new Error("SDK_COMPARATIVE_FINDING_DECISION_INVALID");
|
|
894
|
+
return this.request({ method: "POST", path: `/v1/evaluations/${encodeURIComponent(evaluationId)}/comparison/findings/${encodeURIComponent(findingId)}/review`, body: { ...input } }, { idempotencyKey: options.idempotencyKey ?? `sdk:comparison:finding:${evaluationId}:${findingId}` });
|
|
895
|
+
}
|
|
896
|
+
createEvaluationPlan(input, options = {}) {
|
|
897
|
+
return this.request({ method: "POST", path: "/v1/evaluation-plans", body: input }, { idempotencyKey: options.idempotencyKey });
|
|
898
|
+
}
|
|
899
|
+
getEvaluationPlan(planId) {
|
|
900
|
+
required(planId, "SDK_EVALUATION_PLAN_ID_REQUIRED");
|
|
901
|
+
return this.request({ method: "GET", path: `/v1/evaluation-plans/${encodeURIComponent(planId)}` });
|
|
902
|
+
}
|
|
903
|
+
sealEvaluationPlan(planId, options = {}) {
|
|
904
|
+
required(planId, "SDK_EVALUATION_PLAN_ID_REQUIRED");
|
|
905
|
+
return this.request({ method: "POST", path: `/v1/evaluation-plans/${encodeURIComponent(planId)}/seal`, body: {} }, { idempotencyKey: options.idempotencyKey ?? `sdk:evaluation-plan:seal:${planId}` });
|
|
906
|
+
}
|
|
907
|
+
createImprovementProposal(evaluationId, input, options = {}) {
|
|
908
|
+
required(evaluationId, "SDK_EVALUATION_ID_REQUIRED");
|
|
909
|
+
return this.request({ method: "POST", path: `/v1/evaluations/${encodeURIComponent(evaluationId)}/improvement-proposal`, body: input }, { idempotencyKey: options.idempotencyKey });
|
|
910
|
+
}
|
|
911
|
+
getImprovementProposal(proposalId) {
|
|
912
|
+
required(proposalId, "SDK_IMPROVEMENT_PROPOSAL_ID_REQUIRED");
|
|
913
|
+
return this.request({ method: "GET", path: `/v1/behavior-improvement-proposals/${encodeURIComponent(proposalId)}` });
|
|
914
|
+
}
|
|
915
|
+
reviewImprovementProposal(proposalId, action = "request-review", options = {}) {
|
|
916
|
+
required(proposalId, "SDK_IMPROVEMENT_PROPOSAL_ID_REQUIRED");
|
|
917
|
+
return this.request({ method: "POST", path: `/v1/behavior-improvement-proposals/${encodeURIComponent(proposalId)}`, body: { action } }, { idempotencyKey: options.idempotencyKey ?? `sdk:improvement-proposal:${action}:${proposalId}` });
|
|
918
|
+
}
|
|
919
|
+
editImprovementProposalAsNew(proposalId, input, options = {}) {
|
|
920
|
+
required(proposalId, "SDK_IMPROVEMENT_PROPOSAL_ID_REQUIRED");
|
|
921
|
+
required(input?.id, "SDK_IMPROVEMENT_EDIT_ID_REQUIRED");
|
|
922
|
+
required(input?.reason, "SDK_IMPROVEMENT_EDIT_REASON_REQUIRED");
|
|
923
|
+
return this.request({ method: "POST", path: `/v1/behavior-improvement-proposals/${encodeURIComponent(proposalId)}`, body: { ...input, action: "edit" } }, { idempotencyKey: options.idempotencyKey ?? `sdk:improvement-proposal:edit:${input.id}` });
|
|
924
|
+
}
|
|
925
|
+
createBehaviorRevisionCandidate(input, options = {}) {
|
|
926
|
+
return this.request({ method: "POST", path: "/v1/behavior-revision-candidates", body: input }, { idempotencyKey: options.idempotencyKey });
|
|
927
|
+
}
|
|
928
|
+
evaluateBehaviorRevisionCandidate(candidateId, input, options = {}) {
|
|
929
|
+
required(candidateId, "SDK_BEHAVIOR_CANDIDATE_ID_REQUIRED");
|
|
930
|
+
if (!Array.isArray(input.dataset) || input.dataset.length === 0) throw new Error("SDK_CANDIDATE_EVALUATION_DATASET_REQUIRED");
|
|
931
|
+
return this.request({ method: "POST", path: `/v1/behavior-revision-candidates/${encodeURIComponent(candidateId)}/evaluate`, body: input }, { idempotencyKey: options.idempotencyKey ?? `sdk:behavior-candidate:evaluate:${candidateId}` });
|
|
932
|
+
}
|
|
933
|
+
transitionBehaviorRevisionCandidate(candidateId, transition, input = {}, options = {}) {
|
|
934
|
+
required(candidateId, "SDK_BEHAVIOR_CANDIDATE_ID_REQUIRED");
|
|
935
|
+
return this.request({ method: "POST", path: `/v1/behavior-revision-candidates/${encodeURIComponent(candidateId)}/${transition}`, body: input }, { idempotencyKey: options.idempotencyKey ?? `sdk:behavior-candidate:${transition}:${candidateId}` });
|
|
936
|
+
}
|
|
937
|
+
createPackRevision(input, options = {}) {
|
|
938
|
+
return this.request({ method: "POST", path: "/v1/pack-revisions", body: input }, { idempotencyKey: options.idempotencyKey });
|
|
939
|
+
}
|
|
940
|
+
getPackRevision(revisionId) {
|
|
941
|
+
required(revisionId, "SDK_PACK_REVISION_ID_REQUIRED");
|
|
942
|
+
return this.request({ method: "GET", path: `/v1/pack-revisions/${encodeURIComponent(revisionId)}` });
|
|
943
|
+
}
|
|
944
|
+
getPackRevisionDiff(revisionId) {
|
|
945
|
+
required(revisionId, "SDK_PACK_REVISION_ID_REQUIRED");
|
|
946
|
+
return this.request({ method: "GET", path: `/v1/pack-revisions/${encodeURIComponent(revisionId)}/diff` });
|
|
947
|
+
}
|
|
948
|
+
transitionPackRevision(revisionId, transition, options = {}) {
|
|
949
|
+
required(revisionId, "SDK_PACK_REVISION_ID_REQUIRED");
|
|
950
|
+
return this.request({ method: "POST", path: `/v1/pack-revisions/${encodeURIComponent(revisionId)}/${transition}`, body: {} }, { idempotencyKey: options.idempotencyKey ?? `sdk:pack-revision:${transition}:${revisionId}` });
|
|
951
|
+
}
|
|
952
|
+
materializePackRevision(revisionId, input, options = {}) {
|
|
953
|
+
required(revisionId, "SDK_PACK_REVISION_ID_REQUIRED");
|
|
954
|
+
return this.request({ method: "POST", path: `/v1/pack-revisions/${encodeURIComponent(revisionId)}/materialize`, body: input }, { idempotencyKey: options.idempotencyKey ?? `sdk:pack-revision:materialize:${revisionId}` });
|
|
955
|
+
}
|
|
956
|
+
createRelease(input, options = {}) {
|
|
957
|
+
return this.request(
|
|
958
|
+
{ method: "POST", path: "/v1/releases", body: input },
|
|
959
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
createManualRelease(input, options = {}) {
|
|
963
|
+
if (!input || typeof input !== "object" || !input.manualApproval) throw new Error("SDK_MANUAL_RELEASE_APPROVAL_REQUIRED");
|
|
964
|
+
return this.createRelease(input, { idempotencyKey: options.idempotencyKey });
|
|
965
|
+
}
|
|
966
|
+
releaseProgramManually(input, options = {}) {
|
|
967
|
+
required(input?.programId, "SDK_MANUAL_RELEASE_PROGRAM_REQUIRED");
|
|
968
|
+
required(input?.reason, "SDK_MANUAL_RELEASE_REASON_REQUIRED");
|
|
969
|
+
return this.request({ method: "POST", path: "/v1/releases/manual-program", body: { programId: input.programId, reason: input.reason } }, { idempotencyKey: options.idempotencyKey ?? `sdk:manual-release:${input.programId}` });
|
|
970
|
+
}
|
|
971
|
+
recordDeploymentComparison(deploymentId, comparison, options = {}) {
|
|
972
|
+
required(deploymentId, "SDK_DEPLOYMENT_ID_REQUIRED");
|
|
973
|
+
return this.request(
|
|
974
|
+
{
|
|
975
|
+
method: "POST",
|
|
976
|
+
path: `/v1/deployments/${encodeURIComponent(deploymentId)}/comparisons`,
|
|
977
|
+
body: comparison
|
|
978
|
+
},
|
|
979
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
recordDeploymentHealth(deploymentId, health, options = {}) {
|
|
983
|
+
required(deploymentId, "SDK_DEPLOYMENT_ID_REQUIRED");
|
|
984
|
+
return this.request(
|
|
985
|
+
{
|
|
986
|
+
method: "POST",
|
|
987
|
+
path: `/v1/deployments/${encodeURIComponent(deploymentId)}/authoritative-health`,
|
|
988
|
+
body: health
|
|
989
|
+
},
|
|
990
|
+
{ idempotencyKey: options.idempotencyKey }
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
async compileBehaviorCapsule(input) {
|
|
994
|
+
required(input.programId, "SDK_PROGRAM_ID_REQUIRED");
|
|
995
|
+
required(input.input, "SDK_RUNTIME_INPUT_REQUIRED");
|
|
996
|
+
const body = { ...input };
|
|
997
|
+
return this.request(
|
|
998
|
+
{
|
|
999
|
+
method: "POST",
|
|
1000
|
+
path: "/v1/runtime/behavior-capsule",
|
|
1001
|
+
body
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
idempotencyKey: input.idempotencyKey ?? await contentIdempotencyKey(`sdk:capsule:${input.programId}`, body),
|
|
1005
|
+
enforcing: true
|
|
1006
|
+
}
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
async serveBehaviorProjection(input) {
|
|
1010
|
+
required(input.input, "SDK_RUNTIME_INPUT_REQUIRED");
|
|
1011
|
+
required(input.provider, "SDK_PROVIDER_REQUIRED");
|
|
1012
|
+
const body = { ...input };
|
|
1013
|
+
return this.request(
|
|
1014
|
+
{
|
|
1015
|
+
method: "POST",
|
|
1016
|
+
path: "/v1/runtime/behavior-projection",
|
|
1017
|
+
body
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
idempotencyKey: input.idempotencyKey ?? await contentIdempotencyKey(`sdk:projection:${input.programId ?? "active"}`, body),
|
|
1021
|
+
enforcing: true
|
|
1022
|
+
}
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
async testConnection(connectionId) {
|
|
1026
|
+
required(connectionId, "SDK_CONNECTION_ID_REQUIRED");
|
|
1027
|
+
return this.request({
|
|
1028
|
+
method: "POST",
|
|
1029
|
+
path: `/v1/connections/${encodeURIComponent(connectionId)}/test`,
|
|
1030
|
+
body: {}
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
function buildRuntimeRequest(input) {
|
|
1035
|
+
const {
|
|
1036
|
+
organizationId,
|
|
1037
|
+
projectId,
|
|
1038
|
+
environmentId,
|
|
1039
|
+
revisionId,
|
|
1040
|
+
idempotencyKey,
|
|
1041
|
+
nonce,
|
|
1042
|
+
expiresAt,
|
|
1043
|
+
decisionPlanId,
|
|
1044
|
+
calibrationId,
|
|
1045
|
+
candidates,
|
|
1046
|
+
allowedIds,
|
|
1047
|
+
policy,
|
|
1048
|
+
postModelJudge,
|
|
1049
|
+
shadow
|
|
1050
|
+
} = input;
|
|
1051
|
+
for (const value of [
|
|
1052
|
+
organizationId,
|
|
1053
|
+
projectId,
|
|
1054
|
+
environmentId,
|
|
1055
|
+
revisionId,
|
|
1056
|
+
idempotencyKey,
|
|
1057
|
+
nonce
|
|
1058
|
+
])
|
|
1059
|
+
if (typeof value !== "string" || !value.trim())
|
|
1060
|
+
throw new Error("SDK_REQUEST_FIELD_REQUIRED");
|
|
1061
|
+
if (!Number.isFinite(expiresAt)) throw new Error("SDK_EXPIRY_REQUIRED");
|
|
1062
|
+
const boundary = {
|
|
1063
|
+
...typeof decisionPlanId === "string" ? { decisionPlanId } : {},
|
|
1064
|
+
...typeof calibrationId === "string" ? { calibrationId } : {},
|
|
1065
|
+
...Array.isArray(candidates) ? { candidates } : {},
|
|
1066
|
+
...Array.isArray(allowedIds) ? { allowedIds } : {},
|
|
1067
|
+
...typeof policy === "string" ? { policy } : {},
|
|
1068
|
+
...postModelJudge !== void 0 ? { postModelJudge } : {},
|
|
1069
|
+
...shadow !== void 0 ? { shadow } : {}
|
|
1070
|
+
};
|
|
1071
|
+
return Object.freeze({
|
|
1072
|
+
organizationId,
|
|
1073
|
+
projectId,
|
|
1074
|
+
environmentId,
|
|
1075
|
+
revisionId,
|
|
1076
|
+
idempotencyKey,
|
|
1077
|
+
nonce,
|
|
1078
|
+
expiresAt,
|
|
1079
|
+
...boundary
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
function buildAISystemRequest(input) {
|
|
1083
|
+
const { name, systemType, organizationId, projectId, environmentId } = input;
|
|
1084
|
+
for (const value of [
|
|
1085
|
+
name,
|
|
1086
|
+
systemType,
|
|
1087
|
+
organizationId,
|
|
1088
|
+
projectId,
|
|
1089
|
+
environmentId
|
|
1090
|
+
])
|
|
1091
|
+
if (typeof value !== "string" || !value.trim())
|
|
1092
|
+
throw new Error("SDK_AI_SYSTEM_FIELD_REQUIRED");
|
|
1093
|
+
return Object.freeze({
|
|
1094
|
+
method: "POST",
|
|
1095
|
+
path: "/v1/ai-systems",
|
|
1096
|
+
body: Object.freeze({
|
|
1097
|
+
name,
|
|
1098
|
+
systemType,
|
|
1099
|
+
organizationId,
|
|
1100
|
+
projectId,
|
|
1101
|
+
environmentId
|
|
1102
|
+
})
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
function buildObservationRequest(event) {
|
|
1106
|
+
if (!event?.organizationId?.trim() || !event?.projectId?.trim() || !event?.environmentId?.trim())
|
|
1107
|
+
throw new Error("SDK_OBSERVATION_SCOPE_REQUIRED");
|
|
1108
|
+
if (!event?.eventId || !event.eventType || !event.aiSystemId || !event.connectionId)
|
|
1109
|
+
throw new Error("SDK_OBSERVATION_FIELD_REQUIRED");
|
|
1110
|
+
return Object.freeze({
|
|
1111
|
+
method: "POST",
|
|
1112
|
+
path: "/v1/observations",
|
|
1113
|
+
body: Object.freeze({ ...event })
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// ../../sdks/host-lifecycle.ts
|
|
1118
|
+
import { createHash } from "node:crypto";
|
|
1119
|
+
var nonEmpty = (value, code) => {
|
|
1120
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(code);
|
|
1121
|
+
return value.trim();
|
|
1122
|
+
};
|
|
1123
|
+
var bounded = (value, fallback) => {
|
|
1124
|
+
const parsed = Number(value ?? fallback);
|
|
1125
|
+
if (!Number.isFinite(parsed) || parsed < 256) throw new Error("SDK_HOST_PROJECTION_BUDGET_INVALID");
|
|
1126
|
+
return Math.min(Math.floor(parsed), 64e3);
|
|
1127
|
+
};
|
|
1128
|
+
var lifecycleTimeout = (value) => {
|
|
1129
|
+
const parsed = Number(value ?? 15e3);
|
|
1130
|
+
if (!Number.isFinite(parsed) || parsed < 250) throw new Error("SDK_HOST_TIMEOUT_INVALID");
|
|
1131
|
+
return Math.min(Math.floor(parsed), 12e4);
|
|
1132
|
+
};
|
|
1133
|
+
var withTimeout = async (operation, timeoutMs, code) => {
|
|
1134
|
+
let timer;
|
|
1135
|
+
try {
|
|
1136
|
+
return await Promise.race([operation, new Promise((_, reject) => {
|
|
1137
|
+
timer = setTimeout(() => reject(new Error(code)), timeoutMs);
|
|
1138
|
+
})]);
|
|
1139
|
+
} finally {
|
|
1140
|
+
if (timer) clearTimeout(timer);
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
var bodyOf = (response) => {
|
|
1144
|
+
if (response && typeof response === "object" && "body" in response) {
|
|
1145
|
+
const body = response.body;
|
|
1146
|
+
return body && typeof body === "object" && !Array.isArray(body) ? body : {};
|
|
1147
|
+
}
|
|
1148
|
+
return response && typeof response === "object" && !Array.isArray(response) ? response : {};
|
|
1149
|
+
};
|
|
1150
|
+
var isAllow = (decision) => decision.decision === "ALLOW" && Boolean(decision.authorityReceiptDigest && decision.decisionDigest);
|
|
1151
|
+
var canonical = (value) => Array.isArray(value) ? `[${value.map(canonical).join(",")}]` : value && typeof value === "object" ? `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(",")}}` : JSON.stringify(value);
|
|
1152
|
+
var digest = (value) => createHash("sha256").update(canonical(value), "utf8").digest("hex");
|
|
1153
|
+
function createViraHostRuntime(client, config) {
|
|
1154
|
+
if (!client) throw new Error("SDK_HOST_CLIENT_REQUIRED");
|
|
1155
|
+
const mode = config.mode ?? "off";
|
|
1156
|
+
if (!["off", "observe", "enforce"].includes(mode)) throw new Error("SDK_HOST_MODE_INVALID");
|
|
1157
|
+
const capabilities = config.capabilities;
|
|
1158
|
+
if (!capabilities || typeof capabilities !== "object") throw new Error("SDK_HOST_CAPABILITIES_REQUIRED");
|
|
1159
|
+
const maxBytes = bounded(config.projectionMaxBytes, 8e3);
|
|
1160
|
+
const timeoutMs = lifecycleTimeout(config.timeoutMs);
|
|
1161
|
+
if (mode === "enforce" && (!capabilities.canInterceptToolBefore || !capabilities.canPreventSideEffect)) throw new Error("SDK_HOST_ENFORCE_CAPABILITY_REQUIRED");
|
|
1162
|
+
if (mode === "enforce" && typeof config.authorizeAction !== "function") throw new Error("SDK_HOST_ACTION_AUTHORIZER_REQUIRED");
|
|
1163
|
+
let connected = null;
|
|
1164
|
+
const projections = /* @__PURE__ */ new Map();
|
|
1165
|
+
const connectOnce = async () => {
|
|
1166
|
+
if (mode === "off" || !config.connect) return null;
|
|
1167
|
+
if (!connected) connected = withTimeout(client.connectOnce(config.connect), timeoutMs, "SDK_HOST_CONNECT_TIMEOUT");
|
|
1168
|
+
return connected;
|
|
1169
|
+
};
|
|
1170
|
+
const sessionKey = (request) => `${request.organizationId}:${request.projectId}:${request.environmentId}:${request.conversationId}`;
|
|
1171
|
+
const beforeModel = async (request) => {
|
|
1172
|
+
nonEmpty(request.input, "SDK_HOST_MODEL_INPUT_REQUIRED");
|
|
1173
|
+
if (mode === "off") return { status: "off" };
|
|
1174
|
+
if (!capabilities.canInjectContext) return { status: "unavailable", reasonCode: "HOST_CONTEXT_INJECTION_UNSUPPORTED" };
|
|
1175
|
+
await connectOnce();
|
|
1176
|
+
const semantic = await withTimeout(client.resolveSemanticRuntime({ input: request.input, sessionId: request.sessionId ?? request.conversationId, requestId: request.requestId }), timeoutMs, "SDK_HOST_RESOLVE_TIMEOUT");
|
|
1177
|
+
if (!semantic) return { status: "unavailable", reasonCode: "VIRA_SEMANTIC_UNAVAILABLE" };
|
|
1178
|
+
const semanticBody = bodyOf(semantic.semantic);
|
|
1179
|
+
const receipt = semanticBody.receipt && typeof semanticBody.receipt === "object" ? semanticBody.receipt : {};
|
|
1180
|
+
if (receipt.outcome !== "select" || typeof receipt.taskKey !== "string" || !semantic.runtime) {
|
|
1181
|
+
const result2 = { status: "abstain", taskKey: typeof receipt.taskKey === "string" ? receipt.taskKey : void 0, reasonCode: String(receipt.abstainReason ?? receipt.clarifyReason ?? "NO_BEHAVIOR_MATCH") };
|
|
1182
|
+
projections.set(sessionKey(request), result2);
|
|
1183
|
+
return result2;
|
|
1184
|
+
}
|
|
1185
|
+
const runtimeBody = bodyOf(semantic.runtime);
|
|
1186
|
+
const projection = runtimeBody.behaviorProjection && typeof runtimeBody.behaviorProjection === "object" ? runtimeBody.behaviorProjection : {};
|
|
1187
|
+
const rendered = typeof projection.rendered === "string" ? projection.rendered : "";
|
|
1188
|
+
if (!rendered.trim()) return { status: "abstain", taskKey: String(receipt.taskKey), reasonCode: "EMPTY_BEHAVIOR_PROJECTION" };
|
|
1189
|
+
if (Buffer.byteLength(rendered, "utf8") > maxBytes) return { status: "unavailable", taskKey: String(receipt.taskKey), reasonCode: "BEHAVIOR_PROJECTION_BUDGET_EXCEEDED" };
|
|
1190
|
+
const result = { status: "applied", response: semantic.runtime, projection: rendered, projectionDigest: typeof projection.projectionDigest === "string" ? projection.projectionDigest : void 0, taskKey: String(receipt.taskKey), behaviorPoint: typeof runtimeBody.behaviorPoint === "string" ? runtimeBody.behaviorPoint : void 0 };
|
|
1191
|
+
projections.set(sessionKey(request), result);
|
|
1192
|
+
return result;
|
|
1193
|
+
};
|
|
1194
|
+
const beforeTool = async (proposal) => {
|
|
1195
|
+
nonEmpty(proposal.callId, "SDK_HOST_TOOL_CALL_ID_REQUIRED");
|
|
1196
|
+
nonEmpty(proposal.toolName, "SDK_HOST_TOOL_NAME_REQUIRED");
|
|
1197
|
+
if (mode !== "enforce") return { decision: "ABSTAIN", reasonCode: mode === "off" ? "HOST_MODE_OFF" : "OBSERVE_ONLY" };
|
|
1198
|
+
let decision;
|
|
1199
|
+
try {
|
|
1200
|
+
decision = await withTimeout(Promise.resolve(config.authorizeAction(proposal)), timeoutMs, "SDK_HOST_ACTION_AUTHORIZATION_TIMEOUT");
|
|
1201
|
+
} catch {
|
|
1202
|
+
return { decision: "ERROR", reasonCode: "VIRA_ACTION_AUTHORIZATION_TIMEOUT" };
|
|
1203
|
+
}
|
|
1204
|
+
if (!decision || !["ALLOW", "BLOCK", "ESCALATE", "ABSTAIN", "ERROR"].includes(decision.decision)) throw new Error("SDK_HOST_ACTION_DECISION_INVALID");
|
|
1205
|
+
if (decision.decision === "ALLOW" && !isAllow(decision)) throw new Error("SDK_HOST_ALLOW_RECEIPT_INVALID");
|
|
1206
|
+
return decision;
|
|
1207
|
+
};
|
|
1208
|
+
const afterTool = async (result) => {
|
|
1209
|
+
nonEmpty(result.callId, "SDK_HOST_TOOL_CALL_ID_REQUIRED");
|
|
1210
|
+
if (mode === "off" || !capabilities.canInspectToolAfter) return { status: "off" };
|
|
1211
|
+
const observedResult = result.ok ? result.result : { errorCode: result.errorCode ?? "HOST_TOOL_FAILED" };
|
|
1212
|
+
const evidence = { abi: "vira.host-tool-evidence.v1", eventId: result.callId, callId: result.callId, toolName: result.toolName, argumentsDigest: digest(result.args), resultDigest: result.resultDigest ?? digest(observedResult), outcome: result.ok ? "success" : "failure", requestId: result.requestId, conversationId: result.conversationId, ...result.sessionId ? { sessionId: result.sessionId } : {}, ...result.turnId ? { turnId: result.turnId } : {} };
|
|
1213
|
+
return withTimeout(client.observe({ eventId: result.callId, eventType: result.ok ? "tool.result" : "tool.failed", aiSystemId: result.aiSystemId, connectionId: result.connectionId, organizationId: result.organizationId, projectId: result.projectId, environmentId: result.environmentId, conversationId: result.conversationId, requestId: result.requestId, traceId: result.traceId, payload: { result: observedResult, evidence }, resultDigest: evidence.resultDigest }), timeoutMs, "SDK_HOST_OBSERVE_TIMEOUT");
|
|
1214
|
+
};
|
|
1215
|
+
const endSession = (identity) => {
|
|
1216
|
+
projections.delete(sessionKey({ ...identity, requestId: "session-end", input: "" }));
|
|
1217
|
+
};
|
|
1218
|
+
return Object.freeze({ mode, capabilities, connectOnce, beforeModel, beforeTool, afterTool, endSession, getProjection: (identity) => projections.get(sessionKey({ ...identity, requestId: "projection", input: "" })), supportsPostModelVerification: capabilities.canInterceptResponseBeforeDisplay });
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// src/index.ts
|
|
1222
|
+
var Plugin = { define: (plugin) => plugin };
|
|
1223
|
+
var text = (value) => typeof value === "string" ? value : "";
|
|
1224
|
+
var required2 = (value, code) => {
|
|
1225
|
+
const result = text(value).trim();
|
|
1226
|
+
if (!result) throw new Error(code);
|
|
1227
|
+
return result;
|
|
1228
|
+
};
|
|
1229
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1230
|
+
var canonical2 = (value) => Array.isArray(value) ? "[" + value.map(canonical2).join(",") + "]" : value && typeof value === "object" ? "{" + Object.entries(value).filter(([, child]) => child !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => JSON.stringify(key) + ":" + canonical2(child)).join(",") + "}" : JSON.stringify(value);
|
|
1231
|
+
var digest2 = (value) => createHash2("sha256").update(canonical2(value), "utf8").digest("hex");
|
|
1232
|
+
var lastUserText = (event) => {
|
|
1233
|
+
const messages = Array.isArray(event.messages) ? event.messages : [];
|
|
1234
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
1235
|
+
const message = asRecord(messages[index]);
|
|
1236
|
+
if (message.role === "user") return text(message.content ?? message.text);
|
|
1237
|
+
}
|
|
1238
|
+
return text(event.input ?? event.prompt);
|
|
1239
|
+
};
|
|
1240
|
+
var ViraOpenCodeActionError = class extends Error {
|
|
1241
|
+
decision;
|
|
1242
|
+
constructor(decision, reason) {
|
|
1243
|
+
super(`VIRA_ACTION_${decision}:${reason}`);
|
|
1244
|
+
this.name = "ViraOpenCodeActionError";
|
|
1245
|
+
this.decision = decision;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
var v2Plugin = Plugin.define({
|
|
1249
|
+
id: "vira",
|
|
1250
|
+
async setup(rawContext) {
|
|
1251
|
+
const ctx = rawContext;
|
|
1252
|
+
const options = ctx.options ?? {};
|
|
1253
|
+
const env = (name) => typeof process !== "undefined" ? process.env[name] : void 0;
|
|
1254
|
+
const mode = options.mode ?? env("VIRA_HOST_MODE") ?? "off";
|
|
1255
|
+
if (mode === "off") return;
|
|
1256
|
+
const apiUrl = options.apiUrl ?? env("VIRA_CONTROL_URL") ?? env("VIRA_API_URL");
|
|
1257
|
+
const apiKey = options.apiKey ?? env("VIRA_API_KEY");
|
|
1258
|
+
const scope = { organizationId: required2(options.organizationId ?? env("VIRA_ORGANIZATION_ID"), "VIRA_HOST_ORGANIZATION_REQUIRED"), projectId: required2(options.projectId ?? env("VIRA_PROJECT_ID"), "VIRA_HOST_PROJECT_REQUIRED"), environmentId: required2(options.environmentId ?? env("VIRA_ENVIRONMENT_ID"), "VIRA_HOST_ENVIRONMENT_REQUIRED") };
|
|
1259
|
+
const client = new ViraClient({ baseUrl: required2(apiUrl, "VIRA_HOST_API_URL_REQUIRED"), apiKey });
|
|
1260
|
+
const capabilities = { canObserveModel: true, canInjectContext: true, canInterceptToolBefore: true, canInspectToolAfter: true, canPreventSideEffect: mode === "enforce", canInterceptResponseBeforeDisplay: false, ...options.capabilities ?? {} };
|
|
1261
|
+
const actionContexts = /* @__PURE__ */ new Map();
|
|
1262
|
+
const runtime = createViraHostRuntime(client, { mode, capabilities, authorizeAction: options.authorizeAction ?? (async (proposal) => {
|
|
1263
|
+
const contextKey = proposal.organizationId + ":" + proposal.projectId + ":" + proposal.environmentId + ":" + proposal.conversationId;
|
|
1264
|
+
const bound = actionContexts.get(contextKey);
|
|
1265
|
+
if (!bound) return { decision: "BLOCK", reasonCode: "RUNTIME_ACTION_CONTEXT_UNAVAILABLE" };
|
|
1266
|
+
try {
|
|
1267
|
+
const response = await client.authorizeHostAction({ executionId: bound.executionId, scope, context: bound.context, proposal: { proposal_id: proposal.conversationId + ":" + proposal.callId, tool_key: proposal.toolName, arguments: proposal.args, arguments_digest: digest2(proposal.args), request_context_digest: digest2(bound.context), schema_digest: String(asRecord(asRecord(bound.context).tools).schema_digest ?? "") } });
|
|
1268
|
+
const body = asRecord(response?.body);
|
|
1269
|
+
return body.allowed === true ? { decision: "ALLOW", authorityReceiptDigest: text(body.authority_receipt_digest), decisionDigest: text(body.decision_digest) } : { decision: "BLOCK", reasonCode: text(body.reason_code) || "POLICY_DENIED", authorityReceiptDigest: text(body.authority_receipt_digest) };
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
return { decision: "ERROR", reasonCode: error instanceof Error ? error.message : "RUNTIME_ACTION_AUTHORIZATION_FAILED" };
|
|
1272
|
+
}
|
|
1273
|
+
}), ...(options.aiSystemId ?? env("VIRA_AI_SYSTEM_ID")) && (options.connectionId ?? env("VIRA_CONNECTION_ID")) ? {} : { connect: { ...scope, name: "OpenCode", systemType: "agent", adapterType: "custom-node", adapterVersion: options.hostVersion ?? ctx.app?.version ?? "unknown", idempotencyKey: "opencode:vira:connect" } } });
|
|
1274
|
+
const registrations = [];
|
|
1275
|
+
const sessionIdentity = (event) => ({ ...scope, hostName: options.hostName ?? "opencode", hostVersion: options.hostVersion ?? ctx.app?.version ?? "unknown", aiSystemId: required2(options.aiSystemId ?? env("VIRA_AI_SYSTEM_ID"), "VIRA_HOST_AI_SYSTEM_REQUIRED"), connectionId: required2(options.connectionId ?? env("VIRA_CONNECTION_ID"), "VIRA_HOST_CONNECTION_REQUIRED"), conversationId: required2(event.sessionID ?? event.conversationId, "VIRA_HOST_SESSION_REQUIRED") });
|
|
1276
|
+
registrations.push(await ctx.session.hook("context", async (event) => {
|
|
1277
|
+
const identity = sessionIdentity(event);
|
|
1278
|
+
const result = await runtime.beforeModel({ ...identity, requestId: text(event.messageID ?? event.requestId) || `opencode:${Date.now()}`, input: required2(lastUserText(event), "VIRA_HOST_MODEL_INPUT_REQUIRED"), traceId: text(event.traceID), turnId: text(event.turnID) });
|
|
1279
|
+
const responseBody = asRecord(result.response?.body);
|
|
1280
|
+
const runtimeContext = asRecord(event.viraRuntimeContext ?? responseBody.requestContext ?? responseBody.context);
|
|
1281
|
+
const executionId = text(event.executionId ?? responseBody.executionId);
|
|
1282
|
+
if (executionId && runtimeContext.abi === "vira.request-behavior-context.v2") actionContexts.set(identity.organizationId + ":" + identity.projectId + ":" + identity.environmentId + ":" + identity.conversationId, { executionId, context: runtimeContext });
|
|
1283
|
+
if (result.status === "applied" && result.projection) {
|
|
1284
|
+
const system = Array.isArray(event.system) ? event.system : event.system = [];
|
|
1285
|
+
if (!system.some((item) => text(asRecord(item).text) === result.projection)) system.push({ type: "text", text: result.projection });
|
|
1286
|
+
}
|
|
1287
|
+
}));
|
|
1288
|
+
registrations.push(await ctx.tool.hook("execute.before", async (event) => {
|
|
1289
|
+
const identity = sessionIdentity(event);
|
|
1290
|
+
const decision = await runtime.beforeTool({ ...identity, requestId: text(event.messageID ?? event.requestId) || `opencode:${Date.now()}`, callId: required2(event.callID ?? event.id, "VIRA_HOST_TOOL_CALL_REQUIRED"), toolName: required2(event.tool ?? event.name, "VIRA_HOST_TOOL_NAME_REQUIRED"), args: asRecord(event.input ?? event.args), traceId: text(event.traceID), turnId: text(event.turnID) });
|
|
1291
|
+
if (decision.decision !== "ALLOW") throw new ViraOpenCodeActionError(decision.decision, text(decision.reasonCode) || "VIRA_ACTION_NOT_ALLOWED");
|
|
1292
|
+
}));
|
|
1293
|
+
registrations.push(await ctx.tool.hook("execute.after", async (event) => {
|
|
1294
|
+
const identity = sessionIdentity(event);
|
|
1295
|
+
try {
|
|
1296
|
+
await runtime.afterTool({ ...identity, requestId: text(event.messageID ?? event.requestId) || `opencode:${Date.now()}`, callId: required2(event.callID ?? event.id, "VIRA_HOST_TOOL_CALL_REQUIRED"), toolName: required2(event.tool ?? event.name, "VIRA_HOST_TOOL_NAME_REQUIRED"), args: asRecord(event.input ?? event.args), ok: event.status === "completed", result: event.result, errorCode: text(asRecord(event.error).code), traceId: text(event.traceID), turnId: text(event.turnID) });
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
if (mode === "enforce") throw error;
|
|
1299
|
+
}
|
|
1300
|
+
}));
|
|
1301
|
+
registrations.push(await ctx.session.hook("fork", (event) => runtime.endSession({ ...scope, hostName: "opencode", hostVersion: ctx.app?.version ?? "unknown", aiSystemId: options.aiSystemId ?? "", connectionId: options.connectionId ?? "", conversationId: text(event.sessionID) })));
|
|
1302
|
+
return async () => {
|
|
1303
|
+
actionContexts.clear();
|
|
1304
|
+
for (const registration of registrations) await registration.dispose?.();
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
var v1Runtime = () => {
|
|
1309
|
+
const env = (name) => typeof process !== "undefined" ? process.env[name] : void 0;
|
|
1310
|
+
const mode = env("VIRA_HOST_MODE") ?? "off";
|
|
1311
|
+
if (mode === "off") return null;
|
|
1312
|
+
const scope = { organizationId: required2(env("VIRA_ORGANIZATION_ID"), "VIRA_HOST_ORGANIZATION_REQUIRED"), projectId: required2(env("VIRA_PROJECT_ID"), "VIRA_HOST_PROJECT_REQUIRED"), environmentId: required2(env("VIRA_ENVIRONMENT_ID"), "VIRA_HOST_ENVIRONMENT_REQUIRED") };
|
|
1313
|
+
const client = new ViraClient({ baseUrl: required2(env("VIRA_CONTROL_URL") ?? env("VIRA_API_URL"), "VIRA_HOST_API_URL_REQUIRED"), apiKey: env("VIRA_API_KEY") });
|
|
1314
|
+
return { mode, scope, client, runtime: createViraHostRuntime(client, { mode, capabilities: { canObserveModel: true, canInjectContext: true, canInterceptToolBefore: true, canInspectToolAfter: true, canPreventSideEffect: mode === "enforce", canInterceptResponseBeforeDisplay: false }, authorizeAction: async () => ({ decision: "BLOCK", reasonCode: "VIRA_V1_ACTION_AUTHORIZER_UNAVAILABLE" }) }) };
|
|
1315
|
+
};
|
|
1316
|
+
var index_default = { ...v2Plugin, async server() {
|
|
1317
|
+
const configured = v1Runtime();
|
|
1318
|
+
if (!configured) return {};
|
|
1319
|
+
const { mode, scope, runtime } = configured;
|
|
1320
|
+
const env = (name) => typeof process !== "undefined" ? process.env[name] : void 0;
|
|
1321
|
+
const prompts = /* @__PURE__ */ new Map();
|
|
1322
|
+
const identity = (input) => ({ ...scope, hostName: "opencode", hostVersion: "1.19.5", aiSystemId: required2(env("VIRA_AI_SYSTEM_ID"), "VIRA_HOST_AI_SYSTEM_REQUIRED"), connectionId: required2(env("VIRA_CONNECTION_ID"), "VIRA_HOST_CONNECTION_REQUIRED"), conversationId: required2(input.sessionID ?? input.conversationId ?? input.id, "VIRA_HOST_SESSION_REQUIRED") });
|
|
1323
|
+
const partsText = (value) => Array.isArray(value) ? value.map((part) => text(asRecord(part).text)).filter(Boolean).join("\n") : "";
|
|
1324
|
+
return {
|
|
1325
|
+
"chat.message": async (input, output) => {
|
|
1326
|
+
const sessionId = text(input.sessionID ?? input.conversationId ?? input.id);
|
|
1327
|
+
const message = asRecord(output.message ?? input.message);
|
|
1328
|
+
const prompt = text(message.text ?? message.content) || partsText(output.parts ?? input.parts);
|
|
1329
|
+
if (sessionId && prompt.trim()) prompts.set(sessionId, prompt);
|
|
1330
|
+
},
|
|
1331
|
+
"chat.params": async (input, output) => {
|
|
1332
|
+
const sessionId = text(input.sessionID ?? input.conversationId ?? input.id);
|
|
1333
|
+
const prompt = text(input.prompt ?? asRecord(input.message).text ?? input.input ?? output.prompt) || prompts.get(sessionId) || "";
|
|
1334
|
+
if (!prompt.trim()) return;
|
|
1335
|
+
const result = await runtime.beforeModel({ ...identity(input), requestId: text(input.messageID ?? input.requestId) || `opencode:${Date.now()}`, input: prompt, traceId: text(input.traceID), turnId: text(input.turnID) });
|
|
1336
|
+
if (result.status === "applied" && result.projection) output.system = [...Array.isArray(output.system) ? output.system : [], result.projection];
|
|
1337
|
+
},
|
|
1338
|
+
"tool.execute.before": async (input, output) => {
|
|
1339
|
+
const decision = await runtime.beforeTool({ ...identity(input), requestId: text(input.messageID ?? input.requestId) || `opencode:${Date.now()}`, callId: required2(input.callID ?? input.id, "VIRA_HOST_TOOL_CALL_REQUIRED"), toolName: required2(input.tool ?? input.name, "VIRA_HOST_TOOL_NAME_REQUIRED"), args: asRecord(output.args ?? input.args ?? input.input), traceId: text(input.traceID), turnId: text(input.turnID) });
|
|
1340
|
+
if (mode === "enforce" && decision.decision !== "ALLOW") throw new ViraOpenCodeActionError(decision.decision, text(decision.reasonCode) || "VIRA_ACTION_NOT_ALLOWED");
|
|
1341
|
+
},
|
|
1342
|
+
"tool.execute.after": async (input, output) => {
|
|
1343
|
+
try {
|
|
1344
|
+
await runtime.afterTool({ ...identity(input), requestId: text(input.messageID ?? input.requestId) || `opencode:${Date.now()}`, callId: required2(input.callID ?? input.id, "VIRA_HOST_TOOL_CALL_REQUIRED"), toolName: required2(input.tool ?? input.name, "VIRA_HOST_TOOL_NAME_REQUIRED"), args: asRecord(input.args ?? input.input), ok: true, result: output.output, errorCode: text(asRecord(output.error).code), traceId: text(input.traceID), turnId: text(input.turnID) });
|
|
1345
|
+
} catch (error) {
|
|
1346
|
+
if (mode === "enforce") throw error;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
} };
|
|
1351
|
+
export {
|
|
1352
|
+
ViraOpenCodeActionError,
|
|
1353
|
+
index_default as default
|
|
1354
|
+
};
|