bopodev-agent-sdk 0.1.24 → 0.1.26
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/agent-sdk/src/adapters.d.ts +12 -1
- package/dist/agent-sdk/src/provider-failures/anthropic-api.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/claude-code.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/codex.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/common.d.ts +7 -0
- package/dist/agent-sdk/src/provider-failures/cursor.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/gemini-cli.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/http.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/index.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/openai-api.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/opencode.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/shell.d.ts +5 -0
- package/dist/agent-sdk/src/provider-failures/types.d.ts +20 -0
- package/dist/agent-sdk/src/runtime-core.d.ts +1 -1
- package/dist/agent-sdk/src/runtime-http.d.ts +4 -2
- package/dist/agent-sdk/src/runtime-parsers.d.ts +1 -1
- package/dist/agent-sdk/src/runtime.d.ts +13 -0
- package/dist/agent-sdk/src/types.d.ts +17 -1
- package/dist/contracts/src/index.d.ts +1134 -2
- package/package.json +2 -2
- package/src/adapters.ts +426 -52
- package/src/provider-failures/anthropic-api.ts +20 -0
- package/src/provider-failures/claude-code.ts +20 -0
- package/src/provider-failures/codex.ts +23 -0
- package/src/provider-failures/common.ts +86 -0
- package/src/provider-failures/cursor.ts +20 -0
- package/src/provider-failures/gemini-cli.ts +20 -0
- package/src/provider-failures/http.ts +12 -0
- package/src/provider-failures/index.ts +54 -0
- package/src/provider-failures/openai-api.ts +20 -0
- package/src/provider-failures/opencode.ts +20 -0
- package/src/provider-failures/shell.ts +12 -0
- package/src/provider-failures/types.ts +28 -0
- package/src/runtime-core.ts +7 -1
- package/src/runtime-http.ts +51 -6
- package/src/runtime-parsers.ts +1 -0
- package/src/runtime.ts +283 -0
- package/src/types.ts +17 -1
package/src/runtime.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { access, cp, lstat, mkdir, mkdtemp, readdir, rm, symlink } from "node:fs
|
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
4
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { AgentFinalRunOutputSchema, type AgentFinalRunOutput } from "bopodev-contracts";
|
|
6
7
|
import type { AgentRuntimeConfig } from "./types";
|
|
7
8
|
|
|
8
9
|
type LocalProvider = "claude_code" | "codex" | "cursor" | "opencode" | "gemini_cli";
|
|
@@ -32,6 +33,15 @@ type UsageSourceResolution = {
|
|
|
32
33
|
source?: "stdout" | "stderr";
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
type FinalRunOutputParseError = "missing" | "malformed" | "schema_mismatch";
|
|
37
|
+
|
|
38
|
+
type FinalRunOutputResolution = {
|
|
39
|
+
finalRunOutput?: AgentFinalRunOutput;
|
|
40
|
+
source?: "stdout" | "stderr";
|
|
41
|
+
error?: FinalRunOutputParseError;
|
|
42
|
+
detail?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
35
45
|
type CursorParsedStream = {
|
|
36
46
|
usage: ParsedUsageRecord;
|
|
37
47
|
sessionId?: string;
|
|
@@ -58,6 +68,8 @@ export interface RuntimeExecutionOutput {
|
|
|
58
68
|
usdCost?: number;
|
|
59
69
|
summary?: string;
|
|
60
70
|
};
|
|
71
|
+
finalRunOutput?: AgentFinalRunOutput;
|
|
72
|
+
finalRunOutputSource?: "stdout" | "stderr";
|
|
61
73
|
structuredOutputSource?: "stdout" | "stderr";
|
|
62
74
|
structuredOutputDiagnostics?: {
|
|
63
75
|
stdoutJsonObjectCount: number;
|
|
@@ -73,6 +85,8 @@ export interface RuntimeExecutionOutput {
|
|
|
73
85
|
| "json_missing"
|
|
74
86
|
| "json_on_stderr_only"
|
|
75
87
|
| "schema_or_shape_mismatch";
|
|
88
|
+
finalRunOutputStatus?: "valid" | "missing" | "malformed" | "schema_mismatch";
|
|
89
|
+
finalRunOutputError?: string;
|
|
76
90
|
claudeStopReason?: string;
|
|
77
91
|
claudeResultSubtype?: string;
|
|
78
92
|
claudeSessionId?: string;
|
|
@@ -380,6 +394,7 @@ export async function executePromptRuntime(
|
|
|
380
394
|
const stdoutUsage = parseStructuredUsage(stdout);
|
|
381
395
|
const stderrUsage = parseStructuredUsage(stderr);
|
|
382
396
|
const usageResolution = resolveUsageSource(stdoutUsage, stderrUsage);
|
|
397
|
+
const finalRunOutputResolution = resolveFinalRunOutput(stdout, stderr);
|
|
383
398
|
return {
|
|
384
399
|
ok: true,
|
|
385
400
|
code: attemptResult.code,
|
|
@@ -390,6 +405,8 @@ export async function executePromptRuntime(
|
|
|
390
405
|
attemptCount: attempts.length,
|
|
391
406
|
attempts,
|
|
392
407
|
parsedUsage: usageResolution.usage,
|
|
408
|
+
finalRunOutput: finalRunOutputResolution.finalRunOutput,
|
|
409
|
+
finalRunOutputSource: finalRunOutputResolution.source,
|
|
393
410
|
structuredOutputSource: usageResolution.source,
|
|
394
411
|
structuredOutputDiagnostics: {
|
|
395
412
|
stdoutJsonObjectCount: extractJsonObjectBlocks(stdout).length,
|
|
@@ -401,6 +418,12 @@ export async function executePromptRuntime(
|
|
|
401
418
|
lastStdoutLine: tailLine(stdout),
|
|
402
419
|
lastStderrLine: tailLine(stderr),
|
|
403
420
|
likelyCause: classifyStructuredOutputLikelyCause(stdout, stderr, stdoutUsage, stderrUsage),
|
|
421
|
+
finalRunOutputStatus: finalRunOutputResolution.finalRunOutput
|
|
422
|
+
? "valid"
|
|
423
|
+
: finalRunOutputResolution.error,
|
|
424
|
+
...(finalRunOutputResolution.detail
|
|
425
|
+
? { finalRunOutputError: finalRunOutputResolution.detail }
|
|
426
|
+
: {}),
|
|
404
427
|
...(claudeStream?.stopReason ? { claudeStopReason: claudeStream.stopReason } : {}),
|
|
405
428
|
...(claudeStream?.resultSubtype ? { claudeResultSubtype: claudeStream.resultSubtype } : {}),
|
|
406
429
|
...(claudeStream?.sessionId ? { claudeSessionId: claudeStream.sessionId } : {}),
|
|
@@ -439,6 +462,7 @@ export async function executePromptRuntime(
|
|
|
439
462
|
const stdoutUsage = parseStructuredUsage(stdout);
|
|
440
463
|
const stderrUsage = parseStructuredUsage(stderr);
|
|
441
464
|
const usageResolution = resolveUsageSource(stdoutUsage, stderrUsage);
|
|
465
|
+
const finalRunOutputResolution = resolveFinalRunOutput(stdout, stderr);
|
|
442
466
|
return {
|
|
443
467
|
ok: false,
|
|
444
468
|
code: lastResult?.code ?? null,
|
|
@@ -450,6 +474,8 @@ export async function executePromptRuntime(
|
|
|
450
474
|
attempts,
|
|
451
475
|
failureType: classifyFailure(lastResult?.timedOut ?? false, lastResult?.spawnErrorCode, lastResult?.code ?? null),
|
|
452
476
|
parsedUsage: usageResolution.usage,
|
|
477
|
+
finalRunOutput: finalRunOutputResolution.finalRunOutput,
|
|
478
|
+
finalRunOutputSource: finalRunOutputResolution.source,
|
|
453
479
|
structuredOutputSource: usageResolution.source,
|
|
454
480
|
structuredOutputDiagnostics: {
|
|
455
481
|
stdoutJsonObjectCount: extractJsonObjectBlocks(stdout).length,
|
|
@@ -461,6 +487,10 @@ export async function executePromptRuntime(
|
|
|
461
487
|
lastStdoutLine: tailLine(stdout),
|
|
462
488
|
lastStderrLine: tailLine(stderr),
|
|
463
489
|
likelyCause: classifyStructuredOutputLikelyCause(stdout, stderr, stdoutUsage, stderrUsage),
|
|
490
|
+
finalRunOutputStatus: finalRunOutputResolution.finalRunOutput
|
|
491
|
+
? "valid"
|
|
492
|
+
: finalRunOutputResolution.error,
|
|
493
|
+
...(finalRunOutputResolution.detail ? { finalRunOutputError: finalRunOutputResolution.detail } : {}),
|
|
464
494
|
...(claudeStream?.stopReason ? { claudeStopReason: claudeStream.stopReason } : {}),
|
|
465
495
|
...(claudeStream?.resultSubtype ? { claudeResultSubtype: claudeStream.resultSubtype } : {}),
|
|
466
496
|
...(claudeStream?.sessionId ? { claudeSessionId: claudeStream.sessionId } : {}),
|
|
@@ -1320,6 +1350,24 @@ export function containsRateLimitFailure(text: string) {
|
|
|
1320
1350
|
);
|
|
1321
1351
|
}
|
|
1322
1352
|
|
|
1353
|
+
export function containsUsageLimitHardStopFailure(text: string) {
|
|
1354
|
+
const normalized = text.toLowerCase();
|
|
1355
|
+
return (
|
|
1356
|
+
normalized.includes("api usage limits") ||
|
|
1357
|
+
normalized.includes("you have reached your specified api usage limits") ||
|
|
1358
|
+
normalized.includes("you will regain access on") ||
|
|
1359
|
+
normalized.includes("regain access on") ||
|
|
1360
|
+
normalized.includes("insufficient_quota") ||
|
|
1361
|
+
normalized.includes("billing_hard_limit_reached") ||
|
|
1362
|
+
normalized.includes("exceeded your current quota") ||
|
|
1363
|
+
normalized.includes("usage limit reached")
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
export function containsProviderUsageLimitFailure(text: string) {
|
|
1368
|
+
return containsUsageLimitHardStopFailure(text);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1323
1371
|
export async function checkRuntimeCommandHealth(
|
|
1324
1372
|
command: string,
|
|
1325
1373
|
options?: { cwd?: string; timeoutMs?: number; env?: Record<string, string> }
|
|
@@ -3240,6 +3288,241 @@ function extractJsonObjectBlocks(text: string) {
|
|
|
3240
3288
|
return blocks;
|
|
3241
3289
|
}
|
|
3242
3290
|
|
|
3291
|
+
export function parseAgentFinalRunOutput(text: string): {
|
|
3292
|
+
output?: AgentFinalRunOutput;
|
|
3293
|
+
error?: FinalRunOutputParseError;
|
|
3294
|
+
detail?: string;
|
|
3295
|
+
} {
|
|
3296
|
+
const candidates = extractFinalRunOutputCandidates(text);
|
|
3297
|
+
if (candidates.length === 0) {
|
|
3298
|
+
return { error: "missing", detail: "No final JSON object was found in runtime output." };
|
|
3299
|
+
}
|
|
3300
|
+
let sawMalformedJson = false;
|
|
3301
|
+
let lastSchemaError = "";
|
|
3302
|
+
for (let index = candidates.length - 1; index >= 0; index -= 1) {
|
|
3303
|
+
const candidate = candidates[index] ?? "";
|
|
3304
|
+
try {
|
|
3305
|
+
const parsed = JSON.parse(candidate) as unknown;
|
|
3306
|
+
const normalizedCandidate = normalizeAgentFinalRunOutputCandidate(parsed);
|
|
3307
|
+
const validated = AgentFinalRunOutputSchema.safeParse(normalizedCandidate ?? parsed);
|
|
3308
|
+
if (validated.success) {
|
|
3309
|
+
const canonical = toCanonicalFinalRunOutput(validated.data);
|
|
3310
|
+
if (!isPromptTemplateFinalRunOutput(canonical)) {
|
|
3311
|
+
return { output: canonical };
|
|
3312
|
+
}
|
|
3313
|
+
} else {
|
|
3314
|
+
lastSchemaError = formatFinalRunOutputSchemaError(validated.error);
|
|
3315
|
+
}
|
|
3316
|
+
const legacyOutput = toLegacyFinalRunOutput(parsed);
|
|
3317
|
+
if (legacyOutput) {
|
|
3318
|
+
return { output: legacyOutput };
|
|
3319
|
+
}
|
|
3320
|
+
} catch (error) {
|
|
3321
|
+
sawMalformedJson = true;
|
|
3322
|
+
lastSchemaError = error instanceof Error ? error.message : String(error);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
return {
|
|
3326
|
+
error: sawMalformedJson ? "malformed" : "schema_mismatch",
|
|
3327
|
+
detail: lastSchemaError || "Final JSON object did not match the required schema."
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
function extractFinalRunOutputCandidates(text: string) {
|
|
3332
|
+
const candidates: string[] = [];
|
|
3333
|
+
const trimmed = text.trim();
|
|
3334
|
+
if (looksLikeFinalRunOutputText(trimmed)) {
|
|
3335
|
+
candidates.push(trimmed);
|
|
3336
|
+
}
|
|
3337
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
3338
|
+
const line = rawLine.trim();
|
|
3339
|
+
if (!line) {
|
|
3340
|
+
continue;
|
|
3341
|
+
}
|
|
3342
|
+
if (looksLikeFinalRunOutputText(line)) {
|
|
3343
|
+
candidates.push(line);
|
|
3344
|
+
}
|
|
3345
|
+
const parsed = parseJsonRecord(line);
|
|
3346
|
+
if (!parsed) {
|
|
3347
|
+
continue;
|
|
3348
|
+
}
|
|
3349
|
+
candidates.push(...collectPotentialFinalOutputStrings(parsed));
|
|
3350
|
+
}
|
|
3351
|
+
for (const block of extractJsonObjectBlocks(text)) {
|
|
3352
|
+
if (looksLikeFinalRunOutputText(block)) {
|
|
3353
|
+
candidates.push(block);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
return Array.from(new Set(candidates.map((value) => value.trim()).filter(Boolean)));
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
function normalizeAgentFinalRunOutputCandidate(parsed: unknown): AgentFinalRunOutput | null {
|
|
3360
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3361
|
+
return null;
|
|
3362
|
+
}
|
|
3363
|
+
const record = parsed as Record<string, unknown>;
|
|
3364
|
+
const employeeComment =
|
|
3365
|
+
typeof record.employee_comment === "string" && record.employee_comment.trim().length > 0
|
|
3366
|
+
? record.employee_comment.trim()
|
|
3367
|
+
: null;
|
|
3368
|
+
if (!employeeComment) {
|
|
3369
|
+
return null;
|
|
3370
|
+
}
|
|
3371
|
+
const results = normalizeStringList(record.results);
|
|
3372
|
+
const errors = normalizeStringList(record.errors);
|
|
3373
|
+
const artifacts = normalizeArtifactList(record.artifacts);
|
|
3374
|
+
return {
|
|
3375
|
+
employee_comment: employeeComment,
|
|
3376
|
+
results,
|
|
3377
|
+
errors,
|
|
3378
|
+
artifacts
|
|
3379
|
+
};
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
function normalizeStringList(value: unknown) {
|
|
3383
|
+
if (typeof value === "string") {
|
|
3384
|
+
const trimmed = value.trim();
|
|
3385
|
+
return trimmed ? [trimmed] : [];
|
|
3386
|
+
}
|
|
3387
|
+
if (!Array.isArray(value)) {
|
|
3388
|
+
return [];
|
|
3389
|
+
}
|
|
3390
|
+
return value
|
|
3391
|
+
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
|
3392
|
+
.filter((entry): entry is string => Boolean(entry));
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
function normalizeArtifactList(value: unknown): AgentFinalRunOutput["artifacts"] {
|
|
3396
|
+
if (!Array.isArray(value)) {
|
|
3397
|
+
return [];
|
|
3398
|
+
}
|
|
3399
|
+
return value
|
|
3400
|
+
.map((entry) => normalizeArtifactEntry(entry))
|
|
3401
|
+
.filter((entry): entry is AgentFinalRunOutput["artifacts"][number] => Boolean(entry));
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
function normalizeArtifactEntry(value: unknown): AgentFinalRunOutput["artifacts"][number] | null {
|
|
3405
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3406
|
+
return null;
|
|
3407
|
+
}
|
|
3408
|
+
const record = value as Record<string, unknown>;
|
|
3409
|
+
const pathCandidate =
|
|
3410
|
+
(typeof record.path === "string" && record.path.trim()) ||
|
|
3411
|
+
(typeof record.relativePath === "string" && record.relativePath.trim()) ||
|
|
3412
|
+
(typeof record.absolutePath === "string" && record.absolutePath.trim()) ||
|
|
3413
|
+
"";
|
|
3414
|
+
if (!pathCandidate) {
|
|
3415
|
+
return null;
|
|
3416
|
+
}
|
|
3417
|
+
const explicitKind = typeof record.kind === "string" && record.kind.trim() ? record.kind.trim() : "";
|
|
3418
|
+
return {
|
|
3419
|
+
kind: explicitKind || inferArtifactKind(pathCandidate),
|
|
3420
|
+
path: pathCandidate
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
function inferArtifactKind(path: string) {
|
|
3425
|
+
const normalized = path.toLowerCase();
|
|
3426
|
+
if (/\.[a-z0-9]{1,10}$/i.test(normalized)) {
|
|
3427
|
+
return "file";
|
|
3428
|
+
}
|
|
3429
|
+
if (normalized.endsWith("/") || normalized.includes("/dist") || normalized.includes("/build")) {
|
|
3430
|
+
return "directory";
|
|
3431
|
+
}
|
|
3432
|
+
return "artifact";
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
function collectPotentialFinalOutputStrings(value: unknown, depth = 0): string[] {
|
|
3436
|
+
if (depth > 6 || value === null || value === undefined) {
|
|
3437
|
+
return [];
|
|
3438
|
+
}
|
|
3439
|
+
if (typeof value === "string") {
|
|
3440
|
+
return looksLikeFinalRunOutputText(value) ? [value.trim()] : [];
|
|
3441
|
+
}
|
|
3442
|
+
if (Array.isArray(value)) {
|
|
3443
|
+
return value.flatMap((entry) => collectPotentialFinalOutputStrings(entry, depth + 1));
|
|
3444
|
+
}
|
|
3445
|
+
if (typeof value === "object") {
|
|
3446
|
+
return Object.values(value).flatMap((entry) => collectPotentialFinalOutputStrings(entry, depth + 1));
|
|
3447
|
+
}
|
|
3448
|
+
return [];
|
|
3449
|
+
}
|
|
3450
|
+
|
|
3451
|
+
function looksLikeFinalRunOutputText(value: string) {
|
|
3452
|
+
const trimmed = value.trim();
|
|
3453
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
3454
|
+
return false;
|
|
3455
|
+
}
|
|
3456
|
+
return trimmed.includes("\"employee_comment\"") || trimmed.includes("\"summary\"");
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3459
|
+
function formatFinalRunOutputSchemaError(output: {
|
|
3460
|
+
issues: Array<{
|
|
3461
|
+
path: PropertyKey[];
|
|
3462
|
+
message: string;
|
|
3463
|
+
}>;
|
|
3464
|
+
}) {
|
|
3465
|
+
const issue = output.issues[0];
|
|
3466
|
+
if (!issue) {
|
|
3467
|
+
return "Final JSON object did not match the required schema.";
|
|
3468
|
+
}
|
|
3469
|
+
const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
3470
|
+
return `${path}${issue.message}`;
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
function toCanonicalFinalRunOutput(output: AgentFinalRunOutput): AgentFinalRunOutput {
|
|
3474
|
+
return {
|
|
3475
|
+
employee_comment: output.employee_comment,
|
|
3476
|
+
results: output.results,
|
|
3477
|
+
errors: output.errors,
|
|
3478
|
+
artifacts: output.artifacts
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
function isPromptTemplateFinalRunOutput(output: AgentFinalRunOutput) {
|
|
3483
|
+
return (
|
|
3484
|
+
output.employee_comment.trim().toLowerCase() === "markdown update to the manager" &&
|
|
3485
|
+
output.results.length === 1 &&
|
|
3486
|
+
output.results[0]?.trim().toLowerCase() === "short concrete outcome" &&
|
|
3487
|
+
output.errors.length === 0 &&
|
|
3488
|
+
output.artifacts.length === 1 &&
|
|
3489
|
+
output.artifacts[0]?.kind === "file" &&
|
|
3490
|
+
output.artifacts[0]?.path === "relative/path"
|
|
3491
|
+
);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
function toLegacyFinalRunOutput(parsed: unknown): AgentFinalRunOutput | undefined {
|
|
3495
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3496
|
+
return undefined;
|
|
3497
|
+
}
|
|
3498
|
+
const record = parsed as Record<string, unknown>;
|
|
3499
|
+
const summary = typeof record.summary === "string" ? record.summary.trim() : "";
|
|
3500
|
+
if (!summary) {
|
|
3501
|
+
return undefined;
|
|
3502
|
+
}
|
|
3503
|
+
return {
|
|
3504
|
+
employee_comment: summary,
|
|
3505
|
+
results: [summary],
|
|
3506
|
+
errors: [],
|
|
3507
|
+
artifacts: []
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
function resolveFinalRunOutput(stdout: string, stderr: string): FinalRunOutputResolution {
|
|
3512
|
+
const stdoutResolution = parseAgentFinalRunOutput(stdout);
|
|
3513
|
+
if (stdoutResolution.output) {
|
|
3514
|
+
return { finalRunOutput: stdoutResolution.output, source: "stdout" };
|
|
3515
|
+
}
|
|
3516
|
+
const stderrResolution = parseAgentFinalRunOutput(stderr);
|
|
3517
|
+
if (stderrResolution.output) {
|
|
3518
|
+
return { finalRunOutput: stderrResolution.output, source: "stderr" };
|
|
3519
|
+
}
|
|
3520
|
+
return {
|
|
3521
|
+
error: stdoutResolution.error ?? stderrResolution.error ?? "missing",
|
|
3522
|
+
detail: stdoutResolution.detail ?? stderrResolution.detail ?? "No final JSON object was found in runtime output."
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3243
3526
|
function tryParseUsage(candidate: string) {
|
|
3244
3527
|
try {
|
|
3245
3528
|
const parsed = JSON.parse(candidate) as Record<string, unknown>;
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { ExecutionOutcome, ProviderType } from "bopodev-contracts";
|
|
1
|
+
import type { AgentFinalRunOutput, ExecutionOutcome, ProviderType } from "bopodev-contracts";
|
|
2
2
|
|
|
3
3
|
export type AgentProviderType = ProviderType;
|
|
4
4
|
|
|
5
5
|
export interface AgentWorkItem {
|
|
6
6
|
issueId: string;
|
|
7
7
|
projectId: string;
|
|
8
|
+
parentIssueId?: string | null;
|
|
9
|
+
childIssueIds?: string[];
|
|
8
10
|
projectName?: string | null;
|
|
9
11
|
title: string;
|
|
10
12
|
body?: string | null;
|
|
@@ -62,6 +64,12 @@ export interface HeartbeatContext {
|
|
|
62
64
|
state: AgentState;
|
|
63
65
|
memoryContext?: AgentMemoryContext;
|
|
64
66
|
runtime?: AgentRuntimeConfig;
|
|
67
|
+
wakeContext?: {
|
|
68
|
+
reason?: string | null;
|
|
69
|
+
commentId?: string | null;
|
|
70
|
+
commentBody?: string | null;
|
|
71
|
+
issueIds?: string[];
|
|
72
|
+
};
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
/**
|
|
@@ -87,12 +95,20 @@ export interface AdapterExecutionResult {
|
|
|
87
95
|
tokenInput: number;
|
|
88
96
|
tokenOutput: number;
|
|
89
97
|
usdCost: number;
|
|
98
|
+
finalRunOutput?: AgentFinalRunOutput;
|
|
90
99
|
usage?: AdapterNormalizedUsage;
|
|
91
100
|
pricingProviderType?: string | null;
|
|
92
101
|
pricingModelId?: string | null;
|
|
93
102
|
outcome?: ExecutionOutcome;
|
|
94
103
|
nextState?: AgentState;
|
|
95
104
|
trace?: AdapterTrace;
|
|
105
|
+
dispositionHint?: {
|
|
106
|
+
kind: "provider_usage_limited";
|
|
107
|
+
persistStatus: "skipped";
|
|
108
|
+
pauseAgent: boolean;
|
|
109
|
+
notifyBoard: boolean;
|
|
110
|
+
message: string;
|
|
111
|
+
};
|
|
96
112
|
}
|
|
97
113
|
|
|
98
114
|
export interface AgentAdapter {
|