bopodev-agent-sdk 0.1.25 → 0.1.27

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.
Files changed (43) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/README.md +27 -0
  4. package/dist/adapters/codex/src/server/quota.d.ts +2 -0
  5. package/dist/agent-sdk/src/adapters.d.ts +12 -1
  6. package/dist/agent-sdk/src/provider-failures/anthropic-api.d.ts +5 -0
  7. package/dist/agent-sdk/src/provider-failures/claude-code.d.ts +5 -0
  8. package/dist/agent-sdk/src/provider-failures/codex.d.ts +5 -0
  9. package/dist/agent-sdk/src/provider-failures/common.d.ts +7 -0
  10. package/dist/agent-sdk/src/provider-failures/cursor.d.ts +5 -0
  11. package/dist/agent-sdk/src/provider-failures/gemini-cli.d.ts +5 -0
  12. package/dist/agent-sdk/src/provider-failures/http.d.ts +5 -0
  13. package/dist/agent-sdk/src/provider-failures/index.d.ts +5 -0
  14. package/dist/agent-sdk/src/provider-failures/openai-api.d.ts +5 -0
  15. package/dist/agent-sdk/src/provider-failures/opencode.d.ts +5 -0
  16. package/dist/agent-sdk/src/provider-failures/shell.d.ts +5 -0
  17. package/dist/agent-sdk/src/provider-failures/types.d.ts +20 -0
  18. package/dist/agent-sdk/src/quota.d.ts +4 -0
  19. package/dist/agent-sdk/src/runtime-core.d.ts +1 -1
  20. package/dist/agent-sdk/src/runtime-http.d.ts +4 -2
  21. package/dist/agent-sdk/src/runtime-parsers.d.ts +1 -1
  22. package/dist/agent-sdk/src/runtime.d.ts +13 -0
  23. package/dist/agent-sdk/src/types.d.ts +19 -1
  24. package/dist/contracts/src/index.d.ts +426 -11
  25. package/package.json +2 -2
  26. package/src/adapters.ts +477 -58
  27. package/src/provider-failures/anthropic-api.ts +20 -0
  28. package/src/provider-failures/claude-code.ts +20 -0
  29. package/src/provider-failures/codex.ts +23 -0
  30. package/src/provider-failures/common.ts +86 -0
  31. package/src/provider-failures/cursor.ts +20 -0
  32. package/src/provider-failures/gemini-cli.ts +20 -0
  33. package/src/provider-failures/http.ts +12 -0
  34. package/src/provider-failures/index.ts +54 -0
  35. package/src/provider-failures/openai-api.ts +20 -0
  36. package/src/provider-failures/opencode.ts +20 -0
  37. package/src/provider-failures/shell.ts +12 -0
  38. package/src/provider-failures/types.ts +28 -0
  39. package/src/runtime-core.ts +7 -1
  40. package/src/runtime-http.ts +51 -6
  41. package/src/runtime-parsers.ts +1 -0
  42. package/src/runtime.ts +299 -1
  43. package/src/types.ts +20 -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;
@@ -141,6 +155,16 @@ function providerDefaultArgs(provider: "claude_code" | "codex", config?: AgentRu
141
155
  return ["exec", "--full-auto", "--skip-git-repo-check"];
142
156
  }
143
157
 
158
+ function shouldPassCodexReasoningEffortFlag(config?: AgentRuntimeConfig): boolean {
159
+ const raw = (
160
+ config?.env?.BOPO_CODEX_PASS_REASONING_EFFORT ?? process.env.BOPO_CODEX_PASS_REASONING_EFFORT ??
161
+ ""
162
+ )
163
+ .trim()
164
+ .toLowerCase();
165
+ return raw === "1" || raw === "true" || raw === "yes";
166
+ }
167
+
144
168
  function providerConfigArgs(provider: "claude_code" | "codex", config?: AgentRuntimeConfig) {
145
169
  const args: string[] = [];
146
170
  if (provider === "codex") {
@@ -151,7 +175,12 @@ function providerConfigArgs(provider: "claude_code" | "codex", config?: AgentRun
151
175
  // Codex JSON events carry usage metrics we need for reliable cost accounting.
152
176
  args.push("--json");
153
177
  }
154
- if (config?.thinkingEffort && config.thinkingEffort !== "auto") {
178
+ // Many `codex` builds reject `--reasoning-effort`; only pass when explicitly enabled.
179
+ if (
180
+ shouldPassCodexReasoningEffortFlag(config) &&
181
+ config?.thinkingEffort &&
182
+ config.thinkingEffort !== "auto"
183
+ ) {
155
184
  args.push("--reasoning-effort", config.thinkingEffort);
156
185
  }
157
186
  if (config?.runPolicy?.allowWebSearch) {
@@ -380,6 +409,7 @@ export async function executePromptRuntime(
380
409
  const stdoutUsage = parseStructuredUsage(stdout);
381
410
  const stderrUsage = parseStructuredUsage(stderr);
382
411
  const usageResolution = resolveUsageSource(stdoutUsage, stderrUsage);
412
+ const finalRunOutputResolution = resolveFinalRunOutput(stdout, stderr);
383
413
  return {
384
414
  ok: true,
385
415
  code: attemptResult.code,
@@ -390,6 +420,8 @@ export async function executePromptRuntime(
390
420
  attemptCount: attempts.length,
391
421
  attempts,
392
422
  parsedUsage: usageResolution.usage,
423
+ finalRunOutput: finalRunOutputResolution.finalRunOutput,
424
+ finalRunOutputSource: finalRunOutputResolution.source,
393
425
  structuredOutputSource: usageResolution.source,
394
426
  structuredOutputDiagnostics: {
395
427
  stdoutJsonObjectCount: extractJsonObjectBlocks(stdout).length,
@@ -401,6 +433,12 @@ export async function executePromptRuntime(
401
433
  lastStdoutLine: tailLine(stdout),
402
434
  lastStderrLine: tailLine(stderr),
403
435
  likelyCause: classifyStructuredOutputLikelyCause(stdout, stderr, stdoutUsage, stderrUsage),
436
+ finalRunOutputStatus: finalRunOutputResolution.finalRunOutput
437
+ ? "valid"
438
+ : finalRunOutputResolution.error,
439
+ ...(finalRunOutputResolution.detail
440
+ ? { finalRunOutputError: finalRunOutputResolution.detail }
441
+ : {}),
404
442
  ...(claudeStream?.stopReason ? { claudeStopReason: claudeStream.stopReason } : {}),
405
443
  ...(claudeStream?.resultSubtype ? { claudeResultSubtype: claudeStream.resultSubtype } : {}),
406
444
  ...(claudeStream?.sessionId ? { claudeSessionId: claudeStream.sessionId } : {}),
@@ -439,6 +477,7 @@ export async function executePromptRuntime(
439
477
  const stdoutUsage = parseStructuredUsage(stdout);
440
478
  const stderrUsage = parseStructuredUsage(stderr);
441
479
  const usageResolution = resolveUsageSource(stdoutUsage, stderrUsage);
480
+ const finalRunOutputResolution = resolveFinalRunOutput(stdout, stderr);
442
481
  return {
443
482
  ok: false,
444
483
  code: lastResult?.code ?? null,
@@ -450,6 +489,8 @@ export async function executePromptRuntime(
450
489
  attempts,
451
490
  failureType: classifyFailure(lastResult?.timedOut ?? false, lastResult?.spawnErrorCode, lastResult?.code ?? null),
452
491
  parsedUsage: usageResolution.usage,
492
+ finalRunOutput: finalRunOutputResolution.finalRunOutput,
493
+ finalRunOutputSource: finalRunOutputResolution.source,
453
494
  structuredOutputSource: usageResolution.source,
454
495
  structuredOutputDiagnostics: {
455
496
  stdoutJsonObjectCount: extractJsonObjectBlocks(stdout).length,
@@ -461,6 +502,10 @@ export async function executePromptRuntime(
461
502
  lastStdoutLine: tailLine(stdout),
462
503
  lastStderrLine: tailLine(stderr),
463
504
  likelyCause: classifyStructuredOutputLikelyCause(stdout, stderr, stdoutUsage, stderrUsage),
505
+ finalRunOutputStatus: finalRunOutputResolution.finalRunOutput
506
+ ? "valid"
507
+ : finalRunOutputResolution.error,
508
+ ...(finalRunOutputResolution.detail ? { finalRunOutputError: finalRunOutputResolution.detail } : {}),
464
509
  ...(claudeStream?.stopReason ? { claudeStopReason: claudeStream.stopReason } : {}),
465
510
  ...(claudeStream?.resultSubtype ? { claudeResultSubtype: claudeStream.resultSubtype } : {}),
466
511
  ...(claudeStream?.sessionId ? { claudeSessionId: claudeStream.sessionId } : {}),
@@ -1320,6 +1365,24 @@ export function containsRateLimitFailure(text: string) {
1320
1365
  );
1321
1366
  }
1322
1367
 
1368
+ export function containsUsageLimitHardStopFailure(text: string) {
1369
+ const normalized = text.toLowerCase();
1370
+ return (
1371
+ normalized.includes("api usage limits") ||
1372
+ normalized.includes("you have reached your specified api usage limits") ||
1373
+ normalized.includes("you will regain access on") ||
1374
+ normalized.includes("regain access on") ||
1375
+ normalized.includes("insufficient_quota") ||
1376
+ normalized.includes("billing_hard_limit_reached") ||
1377
+ normalized.includes("exceeded your current quota") ||
1378
+ normalized.includes("usage limit reached")
1379
+ );
1380
+ }
1381
+
1382
+ export function containsProviderUsageLimitFailure(text: string) {
1383
+ return containsUsageLimitHardStopFailure(text);
1384
+ }
1385
+
1323
1386
  export async function checkRuntimeCommandHealth(
1324
1387
  command: string,
1325
1388
  options?: { cwd?: string; timeoutMs?: number; env?: Record<string, string> }
@@ -3240,6 +3303,241 @@ function extractJsonObjectBlocks(text: string) {
3240
3303
  return blocks;
3241
3304
  }
3242
3305
 
3306
+ export function parseAgentFinalRunOutput(text: string): {
3307
+ output?: AgentFinalRunOutput;
3308
+ error?: FinalRunOutputParseError;
3309
+ detail?: string;
3310
+ } {
3311
+ const candidates = extractFinalRunOutputCandidates(text);
3312
+ if (candidates.length === 0) {
3313
+ return { error: "missing", detail: "No final JSON object was found in runtime output." };
3314
+ }
3315
+ let sawMalformedJson = false;
3316
+ let lastSchemaError = "";
3317
+ for (let index = candidates.length - 1; index >= 0; index -= 1) {
3318
+ const candidate = candidates[index] ?? "";
3319
+ try {
3320
+ const parsed = JSON.parse(candidate) as unknown;
3321
+ const normalizedCandidate = normalizeAgentFinalRunOutputCandidate(parsed);
3322
+ const validated = AgentFinalRunOutputSchema.safeParse(normalizedCandidate ?? parsed);
3323
+ if (validated.success) {
3324
+ const canonical = toCanonicalFinalRunOutput(validated.data);
3325
+ if (!isPromptTemplateFinalRunOutput(canonical)) {
3326
+ return { output: canonical };
3327
+ }
3328
+ } else {
3329
+ lastSchemaError = formatFinalRunOutputSchemaError(validated.error);
3330
+ }
3331
+ const legacyOutput = toLegacyFinalRunOutput(parsed);
3332
+ if (legacyOutput) {
3333
+ return { output: legacyOutput };
3334
+ }
3335
+ } catch (error) {
3336
+ sawMalformedJson = true;
3337
+ lastSchemaError = error instanceof Error ? error.message : String(error);
3338
+ }
3339
+ }
3340
+ return {
3341
+ error: sawMalformedJson ? "malformed" : "schema_mismatch",
3342
+ detail: lastSchemaError || "Final JSON object did not match the required schema."
3343
+ };
3344
+ }
3345
+
3346
+ function extractFinalRunOutputCandidates(text: string) {
3347
+ const candidates: string[] = [];
3348
+ const trimmed = text.trim();
3349
+ if (looksLikeFinalRunOutputText(trimmed)) {
3350
+ candidates.push(trimmed);
3351
+ }
3352
+ for (const rawLine of text.split(/\r?\n/)) {
3353
+ const line = rawLine.trim();
3354
+ if (!line) {
3355
+ continue;
3356
+ }
3357
+ if (looksLikeFinalRunOutputText(line)) {
3358
+ candidates.push(line);
3359
+ }
3360
+ const parsed = parseJsonRecord(line);
3361
+ if (!parsed) {
3362
+ continue;
3363
+ }
3364
+ candidates.push(...collectPotentialFinalOutputStrings(parsed));
3365
+ }
3366
+ for (const block of extractJsonObjectBlocks(text)) {
3367
+ if (looksLikeFinalRunOutputText(block)) {
3368
+ candidates.push(block);
3369
+ }
3370
+ }
3371
+ return Array.from(new Set(candidates.map((value) => value.trim()).filter(Boolean)));
3372
+ }
3373
+
3374
+ function normalizeAgentFinalRunOutputCandidate(parsed: unknown): AgentFinalRunOutput | null {
3375
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3376
+ return null;
3377
+ }
3378
+ const record = parsed as Record<string, unknown>;
3379
+ const employeeComment =
3380
+ typeof record.employee_comment === "string" && record.employee_comment.trim().length > 0
3381
+ ? record.employee_comment.trim()
3382
+ : null;
3383
+ if (!employeeComment) {
3384
+ return null;
3385
+ }
3386
+ const results = normalizeStringList(record.results);
3387
+ const errors = normalizeStringList(record.errors);
3388
+ const artifacts = normalizeArtifactList(record.artifacts);
3389
+ return {
3390
+ employee_comment: employeeComment,
3391
+ results,
3392
+ errors,
3393
+ artifacts
3394
+ };
3395
+ }
3396
+
3397
+ function normalizeStringList(value: unknown) {
3398
+ if (typeof value === "string") {
3399
+ const trimmed = value.trim();
3400
+ return trimmed ? [trimmed] : [];
3401
+ }
3402
+ if (!Array.isArray(value)) {
3403
+ return [];
3404
+ }
3405
+ return value
3406
+ .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
3407
+ .filter((entry): entry is string => Boolean(entry));
3408
+ }
3409
+
3410
+ function normalizeArtifactList(value: unknown): AgentFinalRunOutput["artifacts"] {
3411
+ if (!Array.isArray(value)) {
3412
+ return [];
3413
+ }
3414
+ return value
3415
+ .map((entry) => normalizeArtifactEntry(entry))
3416
+ .filter((entry): entry is AgentFinalRunOutput["artifacts"][number] => Boolean(entry));
3417
+ }
3418
+
3419
+ function normalizeArtifactEntry(value: unknown): AgentFinalRunOutput["artifacts"][number] | null {
3420
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
3421
+ return null;
3422
+ }
3423
+ const record = value as Record<string, unknown>;
3424
+ const pathCandidate =
3425
+ (typeof record.path === "string" && record.path.trim()) ||
3426
+ (typeof record.relativePath === "string" && record.relativePath.trim()) ||
3427
+ (typeof record.absolutePath === "string" && record.absolutePath.trim()) ||
3428
+ "";
3429
+ if (!pathCandidate) {
3430
+ return null;
3431
+ }
3432
+ const explicitKind = typeof record.kind === "string" && record.kind.trim() ? record.kind.trim() : "";
3433
+ return {
3434
+ kind: explicitKind || inferArtifactKind(pathCandidate),
3435
+ path: pathCandidate
3436
+ };
3437
+ }
3438
+
3439
+ function inferArtifactKind(path: string) {
3440
+ const normalized = path.toLowerCase();
3441
+ if (/\.[a-z0-9]{1,10}$/i.test(normalized)) {
3442
+ return "file";
3443
+ }
3444
+ if (normalized.endsWith("/") || normalized.includes("/dist") || normalized.includes("/build")) {
3445
+ return "directory";
3446
+ }
3447
+ return "artifact";
3448
+ }
3449
+
3450
+ function collectPotentialFinalOutputStrings(value: unknown, depth = 0): string[] {
3451
+ if (depth > 6 || value === null || value === undefined) {
3452
+ return [];
3453
+ }
3454
+ if (typeof value === "string") {
3455
+ return looksLikeFinalRunOutputText(value) ? [value.trim()] : [];
3456
+ }
3457
+ if (Array.isArray(value)) {
3458
+ return value.flatMap((entry) => collectPotentialFinalOutputStrings(entry, depth + 1));
3459
+ }
3460
+ if (typeof value === "object") {
3461
+ return Object.values(value).flatMap((entry) => collectPotentialFinalOutputStrings(entry, depth + 1));
3462
+ }
3463
+ return [];
3464
+ }
3465
+
3466
+ function looksLikeFinalRunOutputText(value: string) {
3467
+ const trimmed = value.trim();
3468
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
3469
+ return false;
3470
+ }
3471
+ return trimmed.includes("\"employee_comment\"") || trimmed.includes("\"summary\"");
3472
+ }
3473
+
3474
+ function formatFinalRunOutputSchemaError(output: {
3475
+ issues: Array<{
3476
+ path: PropertyKey[];
3477
+ message: string;
3478
+ }>;
3479
+ }) {
3480
+ const issue = output.issues[0];
3481
+ if (!issue) {
3482
+ return "Final JSON object did not match the required schema.";
3483
+ }
3484
+ const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
3485
+ return `${path}${issue.message}`;
3486
+ }
3487
+
3488
+ function toCanonicalFinalRunOutput(output: AgentFinalRunOutput): AgentFinalRunOutput {
3489
+ return {
3490
+ employee_comment: output.employee_comment,
3491
+ results: output.results,
3492
+ errors: output.errors,
3493
+ artifacts: output.artifacts
3494
+ };
3495
+ }
3496
+
3497
+ function isPromptTemplateFinalRunOutput(output: AgentFinalRunOutput) {
3498
+ return (
3499
+ output.employee_comment.trim().toLowerCase() === "markdown update to the manager" &&
3500
+ output.results.length === 1 &&
3501
+ output.results[0]?.trim().toLowerCase() === "short concrete outcome" &&
3502
+ output.errors.length === 0 &&
3503
+ output.artifacts.length === 1 &&
3504
+ output.artifacts[0]?.kind === "file" &&
3505
+ output.artifacts[0]?.path === "relative/path"
3506
+ );
3507
+ }
3508
+
3509
+ function toLegacyFinalRunOutput(parsed: unknown): AgentFinalRunOutput | undefined {
3510
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3511
+ return undefined;
3512
+ }
3513
+ const record = parsed as Record<string, unknown>;
3514
+ const summary = typeof record.summary === "string" ? record.summary.trim() : "";
3515
+ if (!summary) {
3516
+ return undefined;
3517
+ }
3518
+ return {
3519
+ employee_comment: summary,
3520
+ results: [summary],
3521
+ errors: [],
3522
+ artifacts: []
3523
+ };
3524
+ }
3525
+
3526
+ function resolveFinalRunOutput(stdout: string, stderr: string): FinalRunOutputResolution {
3527
+ const stdoutResolution = parseAgentFinalRunOutput(stdout);
3528
+ if (stdoutResolution.output) {
3529
+ return { finalRunOutput: stdoutResolution.output, source: "stdout" };
3530
+ }
3531
+ const stderrResolution = parseAgentFinalRunOutput(stderr);
3532
+ if (stderrResolution.output) {
3533
+ return { finalRunOutput: stderrResolution.output, source: "stderr" };
3534
+ }
3535
+ return {
3536
+ error: stdoutResolution.error ?? stderrResolution.error ?? "missing",
3537
+ detail: stdoutResolution.detail ?? stderrResolution.detail ?? "No final JSON object was found in runtime output."
3538
+ };
3539
+ }
3540
+
3243
3541
  function tryParseUsage(candidate: string) {
3244
3542
  try {
3245
3543
  const parsed = JSON.parse(candidate) as Record<string, unknown>;
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
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
 
@@ -21,6 +21,8 @@ export interface AgentWorkItem {
21
21
  fileSizeBytes: number;
22
22
  relativePath: string;
23
23
  absolutePath: string;
24
+ /** API-relative path, e.g. `/issues/{issueId}/attachments/{id}/download` */
25
+ downloadPath?: string;
24
26
  }>;
25
27
  }
26
28
 
@@ -41,11 +43,20 @@ export interface AgentMemoryContext {
41
43
  dailyNotes: string[];
42
44
  }
43
45
 
46
+ export type HeartbeatPromptMode = "full" | "compact";
47
+
44
48
  export interface HeartbeatContext {
45
49
  companyId: string;
46
50
  agentId: string;
47
51
  providerType: AgentProviderType;
48
52
  heartbeatRunId: string;
53
+ /** Controls how much issue/memory text is inlined in the heartbeat prompt. Default when omitted: full. */
54
+ promptMode?: HeartbeatPromptMode;
55
+ /**
56
+ * When true, emit a minimal idle prompt (no assigned work). Set by the API when
57
+ * `BOPO_HEARTBEAT_IDLE_POLICY=micro_prompt` and there are no work items.
58
+ */
59
+ idleMicroPrompt?: boolean;
49
60
  company: {
50
61
  name: string;
51
62
  mission?: string | null;
@@ -95,12 +106,20 @@ export interface AdapterExecutionResult {
95
106
  tokenInput: number;
96
107
  tokenOutput: number;
97
108
  usdCost: number;
109
+ finalRunOutput?: AgentFinalRunOutput;
98
110
  usage?: AdapterNormalizedUsage;
99
111
  pricingProviderType?: string | null;
100
112
  pricingModelId?: string | null;
101
113
  outcome?: ExecutionOutcome;
102
114
  nextState?: AgentState;
103
115
  trace?: AdapterTrace;
116
+ dispositionHint?: {
117
+ kind: "provider_usage_limited";
118
+ persistStatus: "skipped";
119
+ pauseAgent: boolean;
120
+ notifyBoard: boolean;
121
+ message: string;
122
+ };
104
123
  }
105
124
 
106
125
  export interface AgentAdapter {