@tangle-network/agent-runtime 0.84.0 → 0.86.0
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/agent.js +2 -2
- package/dist/{chunk-UJW6EVNY.js → chunk-3TZOXS7B.js} +875 -41
- package/dist/chunk-3TZOXS7B.js.map +1 -0
- package/dist/{chunk-XBSWWF6A.js → chunk-EJ7MAQN4.js} +3 -3
- package/dist/{chunk-XN5TIJGP.js → chunk-F7OO2SKB.js} +22 -2
- package/dist/chunk-F7OO2SKB.js.map +1 -0
- package/dist/{chunk-G7HQI6DB.js → chunk-U4TS65XZ.js} +2 -2
- package/dist/index.js +16 -625
- package/dist/index.js.map +1 -1
- package/dist/loop-runner-bin.js +3 -3
- package/dist/loops.d.ts +191 -26
- package/dist/loops.js +6 -2
- package/dist/mcp/bin.js +1 -1
- package/dist/mcp/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-UJW6EVNY.js.map +0 -1
- package/dist/chunk-XN5TIJGP.js.map +0 -1
- /package/dist/{chunk-XBSWWF6A.js.map → chunk-EJ7MAQN4.js.map} +0 -0
- /package/dist/{chunk-G7HQI6DB.js.map → chunk-U4TS65XZ.js.map} +0 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
settledToIteration,
|
|
16
16
|
supervise,
|
|
17
17
|
withDriverExecutor
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-F7OO2SKB.js";
|
|
19
19
|
import {
|
|
20
20
|
addTokenUsage,
|
|
21
21
|
isAbortError,
|
|
@@ -24,8 +24,12 @@ import {
|
|
|
24
24
|
stringifySafe,
|
|
25
25
|
zeroTokenUsage
|
|
26
26
|
} from "./chunk-BZF3KQ6G.js";
|
|
27
|
+
import {
|
|
28
|
+
mapSandboxEvent
|
|
29
|
+
} from "./chunk-4J6RBI3K.js";
|
|
27
30
|
import {
|
|
28
31
|
AnalystError,
|
|
32
|
+
BackendTransportError,
|
|
29
33
|
PlannerError,
|
|
30
34
|
ValidationError
|
|
31
35
|
} from "./chunk-YEJR7IXO.js";
|
|
@@ -678,8 +682,14 @@ function inlineSandboxClient(factory) {
|
|
|
678
682
|
const createOptions = options;
|
|
679
683
|
return {
|
|
680
684
|
id,
|
|
681
|
-
async *streamPrompt(message) {
|
|
685
|
+
async *streamPrompt(message, opts) {
|
|
682
686
|
const controller = new AbortController();
|
|
687
|
+
const callerSignal = opts?.signal;
|
|
688
|
+
const onAbort = () => controller.abort(callerSignal?.reason ?? new Error("prompt aborted"));
|
|
689
|
+
if (callerSignal) {
|
|
690
|
+
if (callerSignal.aborted) onAbort();
|
|
691
|
+
else callerSignal.addEventListener("abort", onAbort, { once: true });
|
|
692
|
+
}
|
|
683
693
|
const spec = { profile: { name: id }, harness: null };
|
|
684
694
|
const exec = factory(spec, { signal: controller.signal, seams: { createOptions } });
|
|
685
695
|
try {
|
|
@@ -706,6 +716,7 @@ function inlineSandboxClient(factory) {
|
|
|
706
716
|
}
|
|
707
717
|
};
|
|
708
718
|
} finally {
|
|
719
|
+
callerSignal?.removeEventListener("abort", onAbort);
|
|
709
720
|
await exec.teardown("brutalKill").catch(() => {
|
|
710
721
|
});
|
|
711
722
|
}
|
|
@@ -966,49 +977,68 @@ function defineLeaderboard(spec) {
|
|
|
966
977
|
return p;
|
|
967
978
|
};
|
|
968
979
|
let shotNonce = 0;
|
|
969
|
-
const dispatch = spec.dispatch ??
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
980
|
+
const dispatch = spec.dispatch ?? ((profile, scenario, dispatchCtx) => {
|
|
981
|
+
const cellDispatch = loopDispatch({
|
|
982
|
+
sandboxClient,
|
|
983
|
+
toLoopOptions: (cellScenario, cellProfile) => {
|
|
984
|
+
const axis = harnessAxisOf(cellProfile);
|
|
985
|
+
const modelId = bareModel(axis?.model ?? models[0] ?? "");
|
|
986
|
+
return {
|
|
987
|
+
// naiveDriver = the no-signal retry floor: re-run the same case as
|
|
988
|
+
// an independent attempt until one scores (>0) or the shot cap.
|
|
989
|
+
driver: naiveDriver({
|
|
990
|
+
continuation: "",
|
|
991
|
+
applyContinuation: (task) => task,
|
|
992
|
+
maxIterations: shots
|
|
993
|
+
}),
|
|
994
|
+
agentRun: {
|
|
995
|
+
profile: cellProfile,
|
|
996
|
+
taskToPrompt: (s) => `${promptOf(s)}
|
|
985
997
|
|
|
986
998
|
<!-- independent-attempt:${shotNonce++} -->`,
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
999
|
+
...axis ? {
|
|
1000
|
+
sandboxOverrides: {
|
|
1001
|
+
backend: {
|
|
1002
|
+
type: axis.harness,
|
|
1003
|
+
model: { ...spec.modelBackend, model: modelId }
|
|
1004
|
+
}
|
|
992
1005
|
}
|
|
1006
|
+
} : {}
|
|
1007
|
+
},
|
|
1008
|
+
output: {
|
|
1009
|
+
parse: (events) => spec.parseOutput ? spec.parseOutput(events, cellScenario.case) : (
|
|
1010
|
+
// The default decode produces string — the TArtifact
|
|
1011
|
+
// default. A structured-TArtifact spec supplies parseOutput
|
|
1012
|
+
// (documented on the field), so this cast never lies.
|
|
1013
|
+
collectAgentResponseText(events) ?? ""
|
|
1014
|
+
)
|
|
1015
|
+
},
|
|
1016
|
+
validator: {
|
|
1017
|
+
validate: async (output) => {
|
|
1018
|
+
const s = normalizeScore(spec.score(output, cellScenario.case));
|
|
1019
|
+
return { valid: s.composite > 0, score: s.composite };
|
|
993
1020
|
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1021
|
+
},
|
|
1022
|
+
task: cellScenario,
|
|
1023
|
+
maxIterations: shots
|
|
1024
|
+
};
|
|
1025
|
+
},
|
|
1026
|
+
toArtifact: (result) => {
|
|
1027
|
+
for (const iter of result.iterations) {
|
|
1028
|
+
spec.onCellEvents?.(iter.events, scenario.case, {
|
|
1029
|
+
index: iter.index,
|
|
1030
|
+
...iter.error ? { error: iter.error.message } : {},
|
|
1031
|
+
...iter.verdict ? { verdict: { score: iter.verdict.score } } : {}
|
|
1032
|
+
});
|
|
1033
|
+
if (spec.resolveModel) {
|
|
1034
|
+
const served = spec.resolveModel(iter.events);
|
|
1035
|
+
if (served !== void 0) dispatchCtx.cost.observeModel?.(served);
|
|
1006
1036
|
}
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1037
|
+
}
|
|
1038
|
+
return result.winner?.output;
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
return cellDispatch(profile, scenario, dispatchCtx);
|
|
1012
1042
|
});
|
|
1013
1043
|
await spec.setup?.(ctx);
|
|
1014
1044
|
try {
|
|
@@ -4138,6 +4168,800 @@ ${summary}`,
|
|
|
4138
4168
|
};
|
|
4139
4169
|
}
|
|
4140
4170
|
|
|
4171
|
+
// src/runtime/stream-agent-turn.ts
|
|
4172
|
+
import { scoreKnowledgeReadiness } from "@tangle-network/agent-eval";
|
|
4173
|
+
|
|
4174
|
+
// src/sessions.ts
|
|
4175
|
+
function newRuntimeSession(backend, requestedId, metadata) {
|
|
4176
|
+
const now = nowIso();
|
|
4177
|
+
return {
|
|
4178
|
+
id: requestedId || crypto.randomUUID(),
|
|
4179
|
+
backend,
|
|
4180
|
+
status: "active",
|
|
4181
|
+
createdAt: now,
|
|
4182
|
+
updatedAt: now,
|
|
4183
|
+
metadata
|
|
4184
|
+
};
|
|
4185
|
+
}
|
|
4186
|
+
function touchSession(session) {
|
|
4187
|
+
return { ...session, updatedAt: nowIso() };
|
|
4188
|
+
}
|
|
4189
|
+
function nowIso() {
|
|
4190
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4191
|
+
}
|
|
4192
|
+
var InMemoryRuntimeSessionStore = class {
|
|
4193
|
+
sessions = /* @__PURE__ */ new Map();
|
|
4194
|
+
events = /* @__PURE__ */ new Map();
|
|
4195
|
+
get(sessionId) {
|
|
4196
|
+
return this.sessions.get(sessionId);
|
|
4197
|
+
}
|
|
4198
|
+
put(session) {
|
|
4199
|
+
this.sessions.set(session.id, session);
|
|
4200
|
+
}
|
|
4201
|
+
appendEvent(sessionId, event) {
|
|
4202
|
+
const existing = this.events.get(sessionId) ?? [];
|
|
4203
|
+
existing.push(event);
|
|
4204
|
+
this.events.set(sessionId, existing);
|
|
4205
|
+
}
|
|
4206
|
+
listEvents(sessionId) {
|
|
4207
|
+
return [...this.events.get(sessionId) ?? []];
|
|
4208
|
+
}
|
|
4209
|
+
};
|
|
4210
|
+
|
|
4211
|
+
// src/backends.ts
|
|
4212
|
+
function createIterableBackend(options) {
|
|
4213
|
+
return options;
|
|
4214
|
+
}
|
|
4215
|
+
function createSandboxPromptBackend(options) {
|
|
4216
|
+
const kind = options.kind ?? "sandbox";
|
|
4217
|
+
return {
|
|
4218
|
+
kind,
|
|
4219
|
+
async start(input, context) {
|
|
4220
|
+
const box = await options.getBox(input, context);
|
|
4221
|
+
return newRuntimeSession(
|
|
4222
|
+
kind,
|
|
4223
|
+
options.getSessionId?.(box, input) ?? context.requestedSessionId,
|
|
4224
|
+
{ resumable: true }
|
|
4225
|
+
);
|
|
4226
|
+
},
|
|
4227
|
+
resume(session) {
|
|
4228
|
+
return touchSession({ ...session, status: "active" });
|
|
4229
|
+
},
|
|
4230
|
+
async *stream(input, context) {
|
|
4231
|
+
const box = await options.getBox(input, context);
|
|
4232
|
+
const message = input.message ?? input.messages?.at(-1)?.content ?? context.task.intent;
|
|
4233
|
+
for await (const event of options.streamPrompt(box, message, context)) {
|
|
4234
|
+
const mapped = options.mapEvent?.(event, context) ?? mapCommonBackendEvent(event, context);
|
|
4235
|
+
if (mapped) yield mapped;
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
};
|
|
4239
|
+
}
|
|
4240
|
+
var DEFAULT_RETRY_STATUSES = [408, 425, 429, 500, 502, 503, 504];
|
|
4241
|
+
function pickRetryDelayMs(attempt, policy) {
|
|
4242
|
+
const exp = policy.initialBackoffMs * 2 ** (attempt - 1);
|
|
4243
|
+
const capped = Math.min(exp, policy.maxBackoffMs);
|
|
4244
|
+
const jitter = capped * policy.jitter * (Math.random() * 2 - 1);
|
|
4245
|
+
return Math.max(0, Math.round(capped + jitter));
|
|
4246
|
+
}
|
|
4247
|
+
function withTimeout(callerSignal, timeoutMs) {
|
|
4248
|
+
if (timeoutMs <= 0) {
|
|
4249
|
+
return { signal: callerSignal ?? new AbortController().signal, dispose: () => void 0 };
|
|
4250
|
+
}
|
|
4251
|
+
const controller = new AbortController();
|
|
4252
|
+
const timer = setTimeout(
|
|
4253
|
+
() => controller.abort(new Error(`request timed out after ${timeoutMs}ms`)),
|
|
4254
|
+
timeoutMs
|
|
4255
|
+
);
|
|
4256
|
+
if (typeof timer.unref === "function") {
|
|
4257
|
+
;
|
|
4258
|
+
timer.unref();
|
|
4259
|
+
}
|
|
4260
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason ?? new Error("aborted"));
|
|
4261
|
+
if (callerSignal) {
|
|
4262
|
+
if (callerSignal.aborted) onCallerAbort();
|
|
4263
|
+
else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
4264
|
+
}
|
|
4265
|
+
return {
|
|
4266
|
+
signal: controller.signal,
|
|
4267
|
+
dispose: () => {
|
|
4268
|
+
clearTimeout(timer);
|
|
4269
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
4270
|
+
}
|
|
4271
|
+
};
|
|
4272
|
+
}
|
|
4273
|
+
function sleep2(ms, signal) {
|
|
4274
|
+
return new Promise((resolve, reject) => {
|
|
4275
|
+
if (signal?.aborted) {
|
|
4276
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
const t = setTimeout(() => {
|
|
4280
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4281
|
+
resolve();
|
|
4282
|
+
}, ms);
|
|
4283
|
+
const onAbort = () => {
|
|
4284
|
+
clearTimeout(t);
|
|
4285
|
+
reject(signal?.reason ?? new Error("aborted"));
|
|
4286
|
+
};
|
|
4287
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4288
|
+
});
|
|
4289
|
+
}
|
|
4290
|
+
function createOpenAICompatibleBackend(options) {
|
|
4291
|
+
const fetcher = options.fetchImpl ?? fetch;
|
|
4292
|
+
const kind = options.kind ?? "tcloud";
|
|
4293
|
+
const retryPolicy = {
|
|
4294
|
+
maxAttempts: options.retry?.maxAttempts ?? 5,
|
|
4295
|
+
initialBackoffMs: options.retry?.initialBackoffMs ?? 1e3,
|
|
4296
|
+
maxBackoffMs: options.retry?.maxBackoffMs ?? 3e4,
|
|
4297
|
+
jitter: options.retry?.jitter ?? 0.25,
|
|
4298
|
+
retryStatuses: options.retry?.retryStatuses ?? DEFAULT_RETRY_STATUSES,
|
|
4299
|
+
requestTimeoutMs: options.retry?.requestTimeoutMs ?? 12e4
|
|
4300
|
+
};
|
|
4301
|
+
return {
|
|
4302
|
+
kind,
|
|
4303
|
+
start(_input, context) {
|
|
4304
|
+
return newRuntimeSession(kind, context.requestedSessionId);
|
|
4305
|
+
},
|
|
4306
|
+
async *stream(input, context) {
|
|
4307
|
+
const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
4308
|
+
const bodyPayload = {
|
|
4309
|
+
model: options.model,
|
|
4310
|
+
stream: true,
|
|
4311
|
+
stream_options: { include_usage: true },
|
|
4312
|
+
messages: input.messages ?? [
|
|
4313
|
+
{ role: "user", content: input.message ?? context.task.intent }
|
|
4314
|
+
]
|
|
4315
|
+
};
|
|
4316
|
+
if (options.tools && options.tools.length > 0) {
|
|
4317
|
+
bodyPayload.tools = options.tools;
|
|
4318
|
+
if (options.toolChoice !== void 0) bodyPayload.tool_choice = options.toolChoice;
|
|
4319
|
+
}
|
|
4320
|
+
if (options.responseFormat !== void 0) {
|
|
4321
|
+
bodyPayload.response_format = options.responseFormat;
|
|
4322
|
+
}
|
|
4323
|
+
const requestBody = JSON.stringify(bodyPayload);
|
|
4324
|
+
let response;
|
|
4325
|
+
let lastStatus = 0;
|
|
4326
|
+
let lastThrown;
|
|
4327
|
+
for (let attempt = 1; attempt <= retryPolicy.maxAttempts; attempt++) {
|
|
4328
|
+
lastThrown = void 0;
|
|
4329
|
+
const attemptSignal = withTimeout(context.signal, retryPolicy.requestTimeoutMs);
|
|
4330
|
+
try {
|
|
4331
|
+
response = await fetcher(url, {
|
|
4332
|
+
method: "POST",
|
|
4333
|
+
headers: {
|
|
4334
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
4335
|
+
"Content-Type": "application/json",
|
|
4336
|
+
// Cross-gateway forwarding: when this call is part of a
|
|
4337
|
+
// multi-agent conversation, the runner stamps run/turn/
|
|
4338
|
+
// depth/forwarded-auth headers onto the context. They flow
|
|
4339
|
+
// through to the downstream gateway verbatim so the original
|
|
4340
|
+
// user gets billed, the recursion depth stays bounded, and
|
|
4341
|
+
// the trace correlates across hops.
|
|
4342
|
+
...context.propagatedHeaders ?? {}
|
|
4343
|
+
},
|
|
4344
|
+
body: requestBody,
|
|
4345
|
+
signal: attemptSignal.signal
|
|
4346
|
+
});
|
|
4347
|
+
} catch (err) {
|
|
4348
|
+
attemptSignal.dispose();
|
|
4349
|
+
if (context.signal?.aborted) throw err;
|
|
4350
|
+
lastThrown = err;
|
|
4351
|
+
response = void 0;
|
|
4352
|
+
if (attempt === retryPolicy.maxAttempts) break;
|
|
4353
|
+
await sleep2(pickRetryDelayMs(attempt, retryPolicy), context.signal);
|
|
4354
|
+
continue;
|
|
4355
|
+
}
|
|
4356
|
+
attemptSignal.dispose();
|
|
4357
|
+
if (response.ok) break;
|
|
4358
|
+
lastStatus = response.status;
|
|
4359
|
+
if (!retryPolicy.retryStatuses.includes(response.status)) break;
|
|
4360
|
+
if (attempt === retryPolicy.maxAttempts) break;
|
|
4361
|
+
try {
|
|
4362
|
+
await response.body?.cancel();
|
|
4363
|
+
} catch {
|
|
4364
|
+
}
|
|
4365
|
+
const delayMs = pickRetryDelayMs(attempt, retryPolicy);
|
|
4366
|
+
await sleep2(delayMs, context.signal);
|
|
4367
|
+
}
|
|
4368
|
+
if (!response) {
|
|
4369
|
+
const reason = lastThrown instanceof Error ? lastThrown.message : String(lastThrown);
|
|
4370
|
+
throw new BackendTransportError(
|
|
4371
|
+
kind,
|
|
4372
|
+
`chat backend unreachable after ${retryPolicy.maxAttempts} attempts: ${reason}`,
|
|
4373
|
+
{ status: 0 }
|
|
4374
|
+
);
|
|
4375
|
+
}
|
|
4376
|
+
if (!response.ok) {
|
|
4377
|
+
let body;
|
|
4378
|
+
try {
|
|
4379
|
+
const raw = await response.text();
|
|
4380
|
+
body = raw.length > MAX_ERROR_BODY_BYTES ? `${raw.slice(0, MAX_ERROR_BODY_BYTES)}\u2026` : raw;
|
|
4381
|
+
} catch {
|
|
4382
|
+
body = void 0;
|
|
4383
|
+
}
|
|
4384
|
+
throw new BackendTransportError(kind, `chat backend returned ${lastStatus || "unknown"}`, {
|
|
4385
|
+
status: lastStatus || 0,
|
|
4386
|
+
body
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
yield* streamResponseEvents(response, context, options.model);
|
|
4390
|
+
}
|
|
4391
|
+
};
|
|
4392
|
+
}
|
|
4393
|
+
var MAX_ERROR_BODY_BYTES = 2048;
|
|
4394
|
+
function normalizeBackendStreamEvent(event, task, session) {
|
|
4395
|
+
if ("task" in event && event.task && "session" in event && event.session && "timestamp" in event && event.timestamp) {
|
|
4396
|
+
return event;
|
|
4397
|
+
}
|
|
4398
|
+
return {
|
|
4399
|
+
...event,
|
|
4400
|
+
task: "task" in event && event.task ? event.task : task,
|
|
4401
|
+
session: "session" in event && event.session ? event.session : session,
|
|
4402
|
+
timestamp: "timestamp" in event && event.timestamp ? event.timestamp : nowIso()
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4405
|
+
function mapCommonBackendEvent(event, context) {
|
|
4406
|
+
if (!event || typeof event !== "object") return void 0;
|
|
4407
|
+
const record = event;
|
|
4408
|
+
const type = String(record.type ?? "");
|
|
4409
|
+
const data = record.data && typeof record.data === "object" ? record.data : record;
|
|
4410
|
+
if (type === "message.part.updated" || type === "text_delta" || type === "delta") {
|
|
4411
|
+
const part = data.part;
|
|
4412
|
+
const partText = part !== void 0 && typeof part === "object" && (part.type === "text" || part.type === void 0) ? stringValue(part.text) : void 0;
|
|
4413
|
+
const text = stringValue(data.text) ?? stringValue(data.delta) ?? stringValue(record.text) ?? partText;
|
|
4414
|
+
return text ? {
|
|
4415
|
+
type: "text_delta",
|
|
4416
|
+
task: context.task,
|
|
4417
|
+
session: context.session,
|
|
4418
|
+
text,
|
|
4419
|
+
timestamp: nowIso()
|
|
4420
|
+
} : void 0;
|
|
4421
|
+
}
|
|
4422
|
+
if (type === "reasoning_delta") {
|
|
4423
|
+
const text = stringValue(data.text) ?? stringValue(record.text);
|
|
4424
|
+
return text ? {
|
|
4425
|
+
type: "reasoning_delta",
|
|
4426
|
+
task: context.task,
|
|
4427
|
+
session: context.session,
|
|
4428
|
+
text,
|
|
4429
|
+
timestamp: nowIso()
|
|
4430
|
+
} : void 0;
|
|
4431
|
+
}
|
|
4432
|
+
if (type === "tool_call") {
|
|
4433
|
+
return {
|
|
4434
|
+
type: "tool_call",
|
|
4435
|
+
task: context.task,
|
|
4436
|
+
session: context.session,
|
|
4437
|
+
toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
|
|
4438
|
+
toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
|
|
4439
|
+
args: data.args ?? data.input ?? record.args,
|
|
4440
|
+
timestamp: nowIso()
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
if (type === "tool_result") {
|
|
4444
|
+
return {
|
|
4445
|
+
type: "tool_result",
|
|
4446
|
+
task: context.task,
|
|
4447
|
+
session: context.session,
|
|
4448
|
+
toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
|
|
4449
|
+
toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
|
|
4450
|
+
result: data.result ?? data.output ?? record.result,
|
|
4451
|
+
timestamp: nowIso()
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4454
|
+
if (type === "artifact") {
|
|
4455
|
+
const artifactId = stringValue(data.artifactId) ?? stringValue(data.id) ?? stringValue(record.artifactId);
|
|
4456
|
+
if (!artifactId) return void 0;
|
|
4457
|
+
return {
|
|
4458
|
+
type: "artifact",
|
|
4459
|
+
task: context.task,
|
|
4460
|
+
session: context.session,
|
|
4461
|
+
artifactId,
|
|
4462
|
+
name: stringValue(data.name) ?? stringValue(record.name),
|
|
4463
|
+
mimeType: stringValue(data.mimeType) ?? stringValue(record.mimeType),
|
|
4464
|
+
uri: stringValue(data.uri) ?? stringValue(record.uri),
|
|
4465
|
+
content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
|
|
4466
|
+
metadata: data.metadata && typeof data.metadata === "object" ? data.metadata : void 0,
|
|
4467
|
+
timestamp: nowIso()
|
|
4468
|
+
};
|
|
4469
|
+
}
|
|
4470
|
+
if (type === "proposal_created" || type === "proposal" || type === "filing") {
|
|
4471
|
+
const proposalId = stringValue(data.proposalId) ?? stringValue(data.id) ?? stringValue(record.proposalId);
|
|
4472
|
+
if (!proposalId) return void 0;
|
|
4473
|
+
const status = stringValue(data.status) ?? stringValue(record.status);
|
|
4474
|
+
return {
|
|
4475
|
+
type: "proposal_created",
|
|
4476
|
+
task: context.task,
|
|
4477
|
+
session: context.session,
|
|
4478
|
+
proposalId,
|
|
4479
|
+
title: stringValue(data.title) ?? stringValue(record.title) ?? proposalId,
|
|
4480
|
+
status: status === "pending" || status === "approved" || status === "rejected" ? status : void 0,
|
|
4481
|
+
content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
|
|
4482
|
+
timestamp: nowIso()
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4485
|
+
if (type === "result" || type === "final") {
|
|
4486
|
+
const text = stringValue(data.finalText) ?? stringValue(data.text) ?? stringValue(record.text);
|
|
4487
|
+
return text ? {
|
|
4488
|
+
type: "text_delta",
|
|
4489
|
+
task: context.task,
|
|
4490
|
+
session: context.session,
|
|
4491
|
+
text,
|
|
4492
|
+
timestamp: nowIso()
|
|
4493
|
+
} : void 0;
|
|
4494
|
+
}
|
|
4495
|
+
return void 0;
|
|
4496
|
+
}
|
|
4497
|
+
async function* streamResponseEvents(response, context, requestedModel) {
|
|
4498
|
+
const body = response.body;
|
|
4499
|
+
if (!body) return;
|
|
4500
|
+
const reader = body.getReader();
|
|
4501
|
+
const decoder = new TextDecoder();
|
|
4502
|
+
let buffer = "";
|
|
4503
|
+
const usage = { saw: false };
|
|
4504
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
4505
|
+
const startedAt = Date.now();
|
|
4506
|
+
for (; ; ) {
|
|
4507
|
+
const { done, value } = await reader.read();
|
|
4508
|
+
if (done) break;
|
|
4509
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
4510
|
+
for (const event of drainStreamBuffer(false)) yield event;
|
|
4511
|
+
}
|
|
4512
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
4513
|
+
for (const event of drainStreamBuffer(true)) yield event;
|
|
4514
|
+
if (buffer.trim()) {
|
|
4515
|
+
for (const event of parseStreamChunk(buffer, context, usage, toolCalls)) yield event;
|
|
4516
|
+
}
|
|
4517
|
+
for (const event of flushPendingToolCalls(toolCalls, context)) yield event;
|
|
4518
|
+
if (usage.saw) {
|
|
4519
|
+
yield {
|
|
4520
|
+
type: "llm_call",
|
|
4521
|
+
task: context.task,
|
|
4522
|
+
session: context.session,
|
|
4523
|
+
model: usage.model ?? requestedModel,
|
|
4524
|
+
tokensIn: usage.tokensIn,
|
|
4525
|
+
tokensOut: usage.tokensOut,
|
|
4526
|
+
// `costUsd` is intentionally absent — pricing tables live in consumers
|
|
4527
|
+
// (agent-eval's `estimateCost`, MetricsCollector). Emitting a wrong
|
|
4528
|
+
// number here is worse than emitting none.
|
|
4529
|
+
latencyMs: Date.now() - startedAt,
|
|
4530
|
+
finishReason: usage.finishReason,
|
|
4531
|
+
timestamp: nowIso()
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
function* drainStreamBuffer(flush) {
|
|
4535
|
+
for (; ; ) {
|
|
4536
|
+
const sseBoundary = buffer.indexOf("\n\n");
|
|
4537
|
+
if (sseBoundary >= 0) {
|
|
4538
|
+
const chunk = buffer.slice(0, sseBoundary);
|
|
4539
|
+
buffer = buffer.slice(sseBoundary + 2);
|
|
4540
|
+
for (const event of parseStreamChunk(chunk, context, usage, toolCalls)) yield event;
|
|
4541
|
+
continue;
|
|
4542
|
+
}
|
|
4543
|
+
const newline = buffer.indexOf("\n");
|
|
4544
|
+
if (newline >= 0 && !buffer.slice(0, newline).startsWith("data:")) {
|
|
4545
|
+
const line = buffer.slice(0, newline);
|
|
4546
|
+
buffer = buffer.slice(newline + 1);
|
|
4547
|
+
for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
|
|
4548
|
+
continue;
|
|
4549
|
+
}
|
|
4550
|
+
if (flush && buffer.trim() && !buffer.trimStart().startsWith("data:")) {
|
|
4551
|
+
const line = buffer;
|
|
4552
|
+
buffer = "";
|
|
4553
|
+
for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
|
|
4554
|
+
continue;
|
|
4555
|
+
}
|
|
4556
|
+
break;
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
function* parseStreamChunk(chunk, context, usage, toolCalls) {
|
|
4561
|
+
const lines = chunk.split(/\r?\n/);
|
|
4562
|
+
const dataLines = lines.filter((line) => line.startsWith("data:"));
|
|
4563
|
+
if (dataLines.length === 0 && lines.every((line) => {
|
|
4564
|
+
const trimmed = line.trim();
|
|
4565
|
+
return trimmed.length === 0 || trimmed.startsWith(":");
|
|
4566
|
+
})) {
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4569
|
+
const data = dataLines.length > 0 ? dataLines.map((line) => line.slice(5).trimStart()).join("\n") : chunk.trim();
|
|
4570
|
+
if (!data || data === "[DONE]") return;
|
|
4571
|
+
let parsed;
|
|
4572
|
+
try {
|
|
4573
|
+
parsed = JSON.parse(data);
|
|
4574
|
+
} catch {
|
|
4575
|
+
yield {
|
|
4576
|
+
type: "text_delta",
|
|
4577
|
+
task: context.task,
|
|
4578
|
+
session: context.session,
|
|
4579
|
+
text: data,
|
|
4580
|
+
timestamp: nowIso()
|
|
4581
|
+
};
|
|
4582
|
+
return;
|
|
4583
|
+
}
|
|
4584
|
+
captureStreamUsage(parsed, usage);
|
|
4585
|
+
const choices = parsed.choices;
|
|
4586
|
+
const choice = Array.isArray(choices) ? choices[0] : void 0;
|
|
4587
|
+
const delta = choice?.delta;
|
|
4588
|
+
const message = choice?.message;
|
|
4589
|
+
const deltaToolCalls = delta?.tool_calls;
|
|
4590
|
+
if (Array.isArray(deltaToolCalls)) {
|
|
4591
|
+
for (const tc of deltaToolCalls) {
|
|
4592
|
+
if (!tc || typeof tc !== "object") continue;
|
|
4593
|
+
const rec = tc;
|
|
4594
|
+
const idx = numberValue(rec.index) ?? 0;
|
|
4595
|
+
const key = `openai:${idx}`;
|
|
4596
|
+
const acc = toolCalls.get(key) ?? { argsRaw: "", source: "openai", finalized: false };
|
|
4597
|
+
const id = stringValue(rec.id);
|
|
4598
|
+
if (id) acc.id = id;
|
|
4599
|
+
const fn = rec.function;
|
|
4600
|
+
const name = stringValue(fn?.name);
|
|
4601
|
+
if (name) acc.name = name;
|
|
4602
|
+
const args = stringValue(fn?.arguments);
|
|
4603
|
+
if (args) acc.argsRaw += args;
|
|
4604
|
+
toolCalls.set(key, acc);
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
const messageToolCalls = message?.tool_calls;
|
|
4608
|
+
if (Array.isArray(messageToolCalls)) {
|
|
4609
|
+
for (const tc of messageToolCalls) {
|
|
4610
|
+
if (!tc || typeof tc !== "object") continue;
|
|
4611
|
+
const rec = tc;
|
|
4612
|
+
const fn = rec.function;
|
|
4613
|
+
const idx = numberValue(rec.index) ?? messageToolCalls.indexOf(tc);
|
|
4614
|
+
const key = `openai:${idx}`;
|
|
4615
|
+
const acc = toolCalls.get(key) ?? { argsRaw: "", source: "openai", finalized: false };
|
|
4616
|
+
const id = stringValue(rec.id);
|
|
4617
|
+
if (id) acc.id = id;
|
|
4618
|
+
const name = stringValue(fn?.name);
|
|
4619
|
+
if (name) acc.name = name;
|
|
4620
|
+
const args = stringValue(fn?.arguments);
|
|
4621
|
+
if (args) acc.argsRaw += args;
|
|
4622
|
+
acc.finalized = true;
|
|
4623
|
+
toolCalls.set(key, acc);
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
const finishReason = stringValue(choice?.finish_reason);
|
|
4627
|
+
if (finishReason === "tool_calls") {
|
|
4628
|
+
for (const [key, acc] of toolCalls) {
|
|
4629
|
+
if (acc.source === "openai" && !acc.finalized) acc.finalized = true;
|
|
4630
|
+
toolCalls.set(key, acc);
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
const eventType = stringValue(parsed.type);
|
|
4634
|
+
if (eventType === "content_block_start") {
|
|
4635
|
+
const block = parsed.content_block;
|
|
4636
|
+
if (block && stringValue(block.type) === "tool_use") {
|
|
4637
|
+
const idx = numberValue(parsed.index) ?? 0;
|
|
4638
|
+
const key = `anthropic:${idx}`;
|
|
4639
|
+
toolCalls.set(key, {
|
|
4640
|
+
id: stringValue(block.id),
|
|
4641
|
+
name: stringValue(block.name),
|
|
4642
|
+
argsRaw: "",
|
|
4643
|
+
source: "anthropic",
|
|
4644
|
+
finalized: false
|
|
4645
|
+
});
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
if (eventType === "content_block_delta") {
|
|
4649
|
+
const d = parsed.delta;
|
|
4650
|
+
const dType = stringValue(d?.type);
|
|
4651
|
+
if (dType === "input_json_delta") {
|
|
4652
|
+
const idx = numberValue(parsed.index) ?? 0;
|
|
4653
|
+
const key = `anthropic:${idx}`;
|
|
4654
|
+
const acc = toolCalls.get(key);
|
|
4655
|
+
if (acc) {
|
|
4656
|
+
const partial = stringValue(d?.partial_json) ?? "";
|
|
4657
|
+
acc.argsRaw += partial;
|
|
4658
|
+
toolCalls.set(key, acc);
|
|
4659
|
+
}
|
|
4660
|
+
} else {
|
|
4661
|
+
const text2 = stringValue(d?.text);
|
|
4662
|
+
if (text2) {
|
|
4663
|
+
yield {
|
|
4664
|
+
type: "text_delta",
|
|
4665
|
+
task: context.task,
|
|
4666
|
+
session: context.session,
|
|
4667
|
+
text: text2,
|
|
4668
|
+
timestamp: nowIso()
|
|
4669
|
+
};
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
if (eventType === "content_block_stop") {
|
|
4674
|
+
const idx = numberValue(parsed.index) ?? 0;
|
|
4675
|
+
const key = `anthropic:${idx}`;
|
|
4676
|
+
const acc = toolCalls.get(key);
|
|
4677
|
+
if (acc) {
|
|
4678
|
+
acc.finalized = true;
|
|
4679
|
+
toolCalls.set(key, acc);
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
for (const event of drainFinalizedToolCalls(toolCalls, context)) yield event;
|
|
4683
|
+
const text = stringValue(delta?.content) ?? stringValue(message?.content) ?? stringValue(parsed.text);
|
|
4684
|
+
if (text) {
|
|
4685
|
+
yield {
|
|
4686
|
+
type: "text_delta",
|
|
4687
|
+
task: context.task,
|
|
4688
|
+
session: context.session,
|
|
4689
|
+
text,
|
|
4690
|
+
timestamp: nowIso()
|
|
4691
|
+
};
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
const mapped = mapCommonBackendEvent(parsed, context);
|
|
4695
|
+
if (mapped) yield mapped;
|
|
4696
|
+
}
|
|
4697
|
+
function* drainFinalizedToolCalls(toolCalls, context) {
|
|
4698
|
+
for (const [key, acc] of toolCalls) {
|
|
4699
|
+
if (!acc.finalized) continue;
|
|
4700
|
+
toolCalls.delete(key);
|
|
4701
|
+
yield buildToolCallEvent(acc, context);
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
function* flushPendingToolCalls(toolCalls, context) {
|
|
4705
|
+
for (const [key, acc] of toolCalls) {
|
|
4706
|
+
toolCalls.delete(key);
|
|
4707
|
+
yield buildToolCallEvent(acc, context);
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
function buildToolCallEvent(acc, context) {
|
|
4711
|
+
let args = acc.argsRaw;
|
|
4712
|
+
if (acc.argsRaw.length > 0) {
|
|
4713
|
+
try {
|
|
4714
|
+
args = JSON.parse(acc.argsRaw);
|
|
4715
|
+
} catch {
|
|
4716
|
+
args = acc.argsRaw;
|
|
4717
|
+
}
|
|
4718
|
+
} else {
|
|
4719
|
+
args = {};
|
|
4720
|
+
}
|
|
4721
|
+
return {
|
|
4722
|
+
type: "tool_call",
|
|
4723
|
+
task: context.task,
|
|
4724
|
+
session: context.session,
|
|
4725
|
+
toolName: acc.name ?? "tool",
|
|
4726
|
+
toolCallId: acc.id,
|
|
4727
|
+
args,
|
|
4728
|
+
timestamp: nowIso()
|
|
4729
|
+
};
|
|
4730
|
+
}
|
|
4731
|
+
function captureStreamUsage(parsed, usage) {
|
|
4732
|
+
const model = stringValue(parsed.model);
|
|
4733
|
+
if (model && !usage.model) usage.model = model;
|
|
4734
|
+
const openAiUsage = parsed.usage;
|
|
4735
|
+
if (openAiUsage && typeof openAiUsage === "object") {
|
|
4736
|
+
const promptTokens = numberValue(openAiUsage.prompt_tokens);
|
|
4737
|
+
const completionTokens = numberValue(openAiUsage.completion_tokens);
|
|
4738
|
+
const inputTokens = numberValue(openAiUsage.input_tokens);
|
|
4739
|
+
const outputTokens = numberValue(openAiUsage.output_tokens);
|
|
4740
|
+
if (promptTokens !== void 0) {
|
|
4741
|
+
usage.tokensIn = promptTokens;
|
|
4742
|
+
usage.saw = true;
|
|
4743
|
+
} else if (inputTokens !== void 0) {
|
|
4744
|
+
usage.tokensIn = (usage.tokensIn ?? 0) + inputTokens;
|
|
4745
|
+
usage.saw = true;
|
|
4746
|
+
}
|
|
4747
|
+
if (completionTokens !== void 0) {
|
|
4748
|
+
usage.tokensOut = completionTokens;
|
|
4749
|
+
usage.saw = true;
|
|
4750
|
+
} else if (outputTokens !== void 0) {
|
|
4751
|
+
usage.tokensOut = (usage.tokensOut ?? 0) + outputTokens;
|
|
4752
|
+
usage.saw = true;
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
const type = stringValue(parsed.type);
|
|
4756
|
+
if (type === "message_start") {
|
|
4757
|
+
const message = parsed.message;
|
|
4758
|
+
const messageModel = stringValue(message?.model);
|
|
4759
|
+
if (messageModel && !usage.model) usage.model = messageModel;
|
|
4760
|
+
const messageUsage = message?.usage;
|
|
4761
|
+
const inputTokens = numberValue(messageUsage?.input_tokens);
|
|
4762
|
+
if (inputTokens !== void 0) {
|
|
4763
|
+
usage.tokensIn = inputTokens;
|
|
4764
|
+
usage.saw = true;
|
|
4765
|
+
}
|
|
4766
|
+
const outputTokens = numberValue(messageUsage?.output_tokens);
|
|
4767
|
+
if (outputTokens !== void 0) {
|
|
4768
|
+
usage.tokensOut = (usage.tokensOut ?? 0) + outputTokens;
|
|
4769
|
+
usage.saw = true;
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
if (type === "message_delta") {
|
|
4773
|
+
const delta = parsed.delta;
|
|
4774
|
+
const stopReason = stringValue(delta?.stop_reason);
|
|
4775
|
+
if (stopReason) usage.finishReason = stopReason;
|
|
4776
|
+
}
|
|
4777
|
+
const choices = parsed.choices;
|
|
4778
|
+
if (Array.isArray(choices)) {
|
|
4779
|
+
const finishReason = stringValue(
|
|
4780
|
+
choices[0]?.finish_reason
|
|
4781
|
+
);
|
|
4782
|
+
if (finishReason) usage.finishReason = finishReason;
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
function numberValue(value) {
|
|
4786
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4787
|
+
}
|
|
4788
|
+
function stringValue(value) {
|
|
4789
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4790
|
+
}
|
|
4791
|
+
|
|
4792
|
+
// src/runtime/stream-agent-turn.ts
|
|
4793
|
+
async function* streamAgentTurn(backend, prompt, opts = {}) {
|
|
4794
|
+
const label = backend.kind === "chat" ? backend.backend.kind : backend.kind;
|
|
4795
|
+
const task = { id: `turn-${crypto.randomUUID()}`, intent: prompt };
|
|
4796
|
+
const acc = { deltaText: "", input: 0, output: 0, costUsd: 0 };
|
|
4797
|
+
const deadline = deriveTurnSignal(opts.signal, opts.timeoutMs ?? 0);
|
|
4798
|
+
let session;
|
|
4799
|
+
try {
|
|
4800
|
+
session = await startTurnSession(backend, task, prompt, deadline.signal, label);
|
|
4801
|
+
yield { type: "backend_start", task, session, backend: label, timestamp: nowIso() };
|
|
4802
|
+
const inner = backend.kind === "chat" ? driveChatTurn(backend.backend, task, session, prompt, deadline.signal, acc) : driveBoxTurn(
|
|
4803
|
+
backend.kind === "box" ? backend.box : await inlineSandboxClient(backend.factory).create(),
|
|
4804
|
+
prompt,
|
|
4805
|
+
deadline.signal,
|
|
4806
|
+
backend.agentRunName ?? "agent",
|
|
4807
|
+
acc
|
|
4808
|
+
);
|
|
4809
|
+
for await (const event of inner) {
|
|
4810
|
+
yield event;
|
|
4811
|
+
throwIfAborted(deadline.signal);
|
|
4812
|
+
}
|
|
4813
|
+
yield buildFinalEvent(task, session, acc, { status: "completed", reason: "turn completed" });
|
|
4814
|
+
} catch (err) {
|
|
4815
|
+
const callerAborted = opts.signal?.aborted === true;
|
|
4816
|
+
const status = callerAborted ? "aborted" : "failed";
|
|
4817
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4818
|
+
const error = err instanceof BackendTransportError ? { kind: "transport", message, status: err.status, body: err.body } : { kind: "backend", message };
|
|
4819
|
+
yield {
|
|
4820
|
+
type: "backend_error",
|
|
4821
|
+
task,
|
|
4822
|
+
...session ? { session } : {},
|
|
4823
|
+
backend: label,
|
|
4824
|
+
message,
|
|
4825
|
+
recoverable: !callerAborted,
|
|
4826
|
+
error,
|
|
4827
|
+
timestamp: nowIso()
|
|
4828
|
+
};
|
|
4829
|
+
yield buildFinalEvent(task, session, acc, { status, reason: message, error });
|
|
4830
|
+
} finally {
|
|
4831
|
+
deadline.dispose();
|
|
4832
|
+
}
|
|
4833
|
+
}
|
|
4834
|
+
async function collectAgentTurn(stream) {
|
|
4835
|
+
const events = [];
|
|
4836
|
+
for await (const event of stream) events.push(event);
|
|
4837
|
+
const final = events.at(-1);
|
|
4838
|
+
if (!final || final.type !== "final") {
|
|
4839
|
+
throw new Error(
|
|
4840
|
+
`collectAgentTurn: stream ended without a terminal 'final' event (last: ${final ? final.type : "none"})`
|
|
4841
|
+
);
|
|
4842
|
+
}
|
|
4843
|
+
const metadata = final.metadata ?? {};
|
|
4844
|
+
const tokenUsage = metadata.tokenUsage && typeof metadata.tokenUsage === "object" ? metadata.tokenUsage : {};
|
|
4845
|
+
const usage = {
|
|
4846
|
+
input: finiteNumber(tokenUsage.input) ?? 0,
|
|
4847
|
+
output: finiteNumber(tokenUsage.output) ?? 0
|
|
4848
|
+
};
|
|
4849
|
+
const costUsd = finiteNumber(metadata.costUsd);
|
|
4850
|
+
if (costUsd !== void 0) usage.costUsd = costUsd;
|
|
4851
|
+
if (typeof metadata.model === "string" && metadata.model.length > 0) {
|
|
4852
|
+
usage.model = metadata.model;
|
|
4853
|
+
}
|
|
4854
|
+
return {
|
|
4855
|
+
finalText: final.text ?? "",
|
|
4856
|
+
usage,
|
|
4857
|
+
events,
|
|
4858
|
+
status: final.status,
|
|
4859
|
+
...final.error ? { error: final.error } : {}
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
async function startTurnSession(backend, task, prompt, signal, label) {
|
|
4863
|
+
if (backend.kind === "chat" && backend.backend.start) {
|
|
4864
|
+
return backend.backend.start(
|
|
4865
|
+
{ task, message: prompt },
|
|
4866
|
+
{ task, knowledge: emptyReadiness(task), signal }
|
|
4867
|
+
);
|
|
4868
|
+
}
|
|
4869
|
+
return newRuntimeSession(label);
|
|
4870
|
+
}
|
|
4871
|
+
async function* driveBoxTurn(box, prompt, signal, agentRunName, acc) {
|
|
4872
|
+
for await (const event of box.streamPrompt(prompt, { signal })) {
|
|
4873
|
+
const terminalText = terminalTextFromSandboxEvent(event);
|
|
4874
|
+
if (terminalText !== void 0) acc.terminalText = terminalText;
|
|
4875
|
+
const mapped = mapSandboxEvent(event, { agentRunName });
|
|
4876
|
+
if (!mapped) continue;
|
|
4877
|
+
foldEvent(mapped, acc, agentRunName);
|
|
4878
|
+
yield mapped;
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
async function* driveChatTurn(backend, task, session, prompt, signal, acc) {
|
|
4882
|
+
const input = { task, message: prompt };
|
|
4883
|
+
const context = { task, knowledge: emptyReadiness(task), session, signal };
|
|
4884
|
+
for await (const raw of backend.stream(input, context)) {
|
|
4885
|
+
const event = normalizeBackendStreamEvent(raw, task, session);
|
|
4886
|
+
foldEvent(event, acc);
|
|
4887
|
+
yield event;
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4890
|
+
function foldEvent(event, acc, fallbackModelLabel) {
|
|
4891
|
+
if (event.type === "text_delta") {
|
|
4892
|
+
acc.deltaText += event.text;
|
|
4893
|
+
return;
|
|
4894
|
+
}
|
|
4895
|
+
if (event.type === "llm_call") {
|
|
4896
|
+
acc.input += event.tokensIn ?? 0;
|
|
4897
|
+
acc.output += event.tokensOut ?? 0;
|
|
4898
|
+
acc.costUsd += event.costUsd ?? 0;
|
|
4899
|
+
if (event.model && event.model !== fallbackModelLabel) acc.model = event.model;
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
function terminalTextFromSandboxEvent(event) {
|
|
4903
|
+
if (!event || typeof event !== "object") return void 0;
|
|
4904
|
+
const type = String(event.type ?? "");
|
|
4905
|
+
if (type !== "result" && type !== "done" && type !== "final") return void 0;
|
|
4906
|
+
const data = event.data && typeof event.data === "object" ? event.data : {};
|
|
4907
|
+
for (const key of ["finalText", "text", "response", "content"]) {
|
|
4908
|
+
const value = data[key];
|
|
4909
|
+
if (typeof value === "string") return value;
|
|
4910
|
+
}
|
|
4911
|
+
return void 0;
|
|
4912
|
+
}
|
|
4913
|
+
function buildFinalEvent(task, session, acc, outcome) {
|
|
4914
|
+
const finalText = acc.terminalText ?? acc.deltaText;
|
|
4915
|
+
return {
|
|
4916
|
+
type: "final",
|
|
4917
|
+
task,
|
|
4918
|
+
...session ? { session } : {},
|
|
4919
|
+
status: outcome.status,
|
|
4920
|
+
reason: outcome.reason,
|
|
4921
|
+
...finalText ? { text: finalText } : {},
|
|
4922
|
+
metadata: {
|
|
4923
|
+
tokenUsage: { input: acc.input, output: acc.output },
|
|
4924
|
+
...acc.costUsd > 0 ? { costUsd: acc.costUsd } : {},
|
|
4925
|
+
...acc.model ? { model: acc.model } : {}
|
|
4926
|
+
},
|
|
4927
|
+
...outcome.error ? { error: outcome.error } : {},
|
|
4928
|
+
timestamp: nowIso()
|
|
4929
|
+
};
|
|
4930
|
+
}
|
|
4931
|
+
function emptyReadiness(task) {
|
|
4932
|
+
return scoreKnowledgeReadiness({ taskId: task.id, requirements: [] });
|
|
4933
|
+
}
|
|
4934
|
+
function finiteNumber(value) {
|
|
4935
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4936
|
+
}
|
|
4937
|
+
function throwIfAborted(signal) {
|
|
4938
|
+
if (!signal.aborted) return;
|
|
4939
|
+
throw signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason));
|
|
4940
|
+
}
|
|
4941
|
+
function deriveTurnSignal(callerSignal, timeoutMs) {
|
|
4942
|
+
const controller = new AbortController();
|
|
4943
|
+
const timer = timeoutMs > 0 ? setTimeout(
|
|
4944
|
+
() => controller.abort(new Error(`agent turn timed out after ${timeoutMs}ms`)),
|
|
4945
|
+
timeoutMs
|
|
4946
|
+
) : void 0;
|
|
4947
|
+
if (timer && typeof timer.unref === "function") {
|
|
4948
|
+
;
|
|
4949
|
+
timer.unref();
|
|
4950
|
+
}
|
|
4951
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason ?? new Error("agent turn aborted"));
|
|
4952
|
+
if (callerSignal) {
|
|
4953
|
+
if (callerSignal.aborted) onCallerAbort();
|
|
4954
|
+
else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
4955
|
+
}
|
|
4956
|
+
return {
|
|
4957
|
+
signal: controller.signal,
|
|
4958
|
+
dispose: () => {
|
|
4959
|
+
if (timer) clearTimeout(timer);
|
|
4960
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
4961
|
+
}
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4141
4965
|
// src/runtime/supervise/detector-monitor.ts
|
|
4142
4966
|
import {
|
|
4143
4967
|
argHash,
|
|
@@ -4946,6 +5770,14 @@ function tail(s) {
|
|
|
4946
5770
|
}
|
|
4947
5771
|
|
|
4948
5772
|
export {
|
|
5773
|
+
newRuntimeSession,
|
|
5774
|
+
touchSession,
|
|
5775
|
+
nowIso,
|
|
5776
|
+
InMemoryRuntimeSessionStore,
|
|
5777
|
+
createIterableBackend,
|
|
5778
|
+
createSandboxPromptBackend,
|
|
5779
|
+
createOpenAICompatibleBackend,
|
|
5780
|
+
normalizeBackendStreamEvent,
|
|
4949
5781
|
anytimeReport,
|
|
4950
5782
|
renderAnytimeTable,
|
|
4951
5783
|
defaultAuditorInstruction,
|
|
@@ -5016,6 +5848,8 @@ export {
|
|
|
5016
5848
|
pickChampion,
|
|
5017
5849
|
selectChampion,
|
|
5018
5850
|
runStrategyEvolution,
|
|
5851
|
+
streamAgentTurn,
|
|
5852
|
+
collectAgentTurn,
|
|
5019
5853
|
defaultToolDetectors,
|
|
5020
5854
|
watchTrace,
|
|
5021
5855
|
runCoderChecks,
|
|
@@ -5036,4 +5870,4 @@ export {
|
|
|
5036
5870
|
computeFindingId,
|
|
5037
5871
|
makeFinding2 as makeFinding
|
|
5038
5872
|
};
|
|
5039
|
-
//# sourceMappingURL=chunk-
|
|
5873
|
+
//# sourceMappingURL=chunk-3TZOXS7B.js.map
|