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/adapters.ts
CHANGED
|
@@ -11,7 +11,12 @@ import type {
|
|
|
11
11
|
HeartbeatContext
|
|
12
12
|
} from "./types";
|
|
13
13
|
import { ExecutionOutcomeSchema, type ExecutionOutcome } from "bopodev-contracts";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
checkRuntimeCommandHealth,
|
|
16
|
+
containsUsageLimitHardStopFailure,
|
|
17
|
+
executeAgentRuntime,
|
|
18
|
+
executePromptRuntime
|
|
19
|
+
} from "./runtime-core";
|
|
15
20
|
import {
|
|
16
21
|
parseClaudeStreamOutput,
|
|
17
22
|
parseCursorStreamOutput,
|
|
@@ -24,6 +29,10 @@ import {
|
|
|
24
29
|
resolveDirectApiCredentials,
|
|
25
30
|
type DirectApiProvider
|
|
26
31
|
} from "./runtime-http";
|
|
32
|
+
import {
|
|
33
|
+
classifyProviderFailure as classifyProviderFailureByProvider,
|
|
34
|
+
normalizeProviderFailureDetail as normalizeProviderFailureDetailByProvider
|
|
35
|
+
} from "./provider-failures";
|
|
27
36
|
import { homedir } from "node:os";
|
|
28
37
|
import { basename, join, resolve } from "node:path";
|
|
29
38
|
|
|
@@ -42,8 +51,39 @@ function toOutcome(outcome: ExecutionOutcome): ExecutionOutcome {
|
|
|
42
51
|
return ExecutionOutcomeSchema.parse(outcome);
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
function
|
|
46
|
-
return
|
|
54
|
+
function isProviderUsageLimitedRuntimeFailure(runtime: { stdout: string; stderr: string }, detail?: string) {
|
|
55
|
+
return containsUsageLimitHardStopFailure(`${detail ?? ""}\n${runtime.stderr}\n${runtime.stdout}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildProviderUsageLimitedDispositionHint(
|
|
59
|
+
provider: string,
|
|
60
|
+
detail: string
|
|
61
|
+
): NonNullable<AdapterExecutionResult["dispositionHint"]> {
|
|
62
|
+
const normalizedDetail = detail.replace(/\s+/g, " ").trim();
|
|
63
|
+
const message = normalizedDetail ? `${provider} usage limit reached: ${normalizedDetail}` : `${provider} usage limit reached.`;
|
|
64
|
+
return {
|
|
65
|
+
kind: "provider_usage_limited",
|
|
66
|
+
persistStatus: "skipped",
|
|
67
|
+
pauseAgent: true,
|
|
68
|
+
notifyBoard: true,
|
|
69
|
+
message
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function normalizeProviderFailureDetail(provider: AgentProviderType, detail: string) {
|
|
74
|
+
return normalizeProviderFailureDetailByProvider(provider, detail);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function classifyProviderFailure(
|
|
78
|
+
provider: AgentProviderType,
|
|
79
|
+
input: {
|
|
80
|
+
detail: string;
|
|
81
|
+
stderr?: string;
|
|
82
|
+
stdout?: string;
|
|
83
|
+
failureType?: string | null;
|
|
84
|
+
}
|
|
85
|
+
): ReturnType<typeof classifyProviderFailureByProvider> {
|
|
86
|
+
return classifyProviderFailureByProvider(provider, input);
|
|
47
87
|
}
|
|
48
88
|
|
|
49
89
|
type RuntimeParsedUsage = {
|
|
@@ -130,6 +170,53 @@ function usageTokenInputTotal(usage: RuntimeParsedUsage | undefined) {
|
|
|
130
170
|
return Math.max(0, usage.tokenInput ?? 0);
|
|
131
171
|
}
|
|
132
172
|
|
|
173
|
+
function resolveFinalRunOutputContractDetail(input: {
|
|
174
|
+
provider: string;
|
|
175
|
+
runtime: {
|
|
176
|
+
structuredOutputDiagnostics?: {
|
|
177
|
+
finalRunOutputError?: string;
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
}) {
|
|
181
|
+
const detail = input.runtime.structuredOutputDiagnostics?.finalRunOutputError?.trim();
|
|
182
|
+
return detail || `${input.provider} runtime did not return a valid final JSON object.`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function createContractInvalidResult(input: {
|
|
186
|
+
context: HeartbeatContext;
|
|
187
|
+
provider: AgentProviderType;
|
|
188
|
+
summary: string;
|
|
189
|
+
tokenInput: number;
|
|
190
|
+
tokenOutput: number;
|
|
191
|
+
usdCost: number;
|
|
192
|
+
usage?: AdapterNormalizedUsage;
|
|
193
|
+
pricingProviderType?: string | null;
|
|
194
|
+
pricingModelId?: string | null;
|
|
195
|
+
trace: NonNullable<AdapterExecutionResult["trace"]>;
|
|
196
|
+
nextState: HeartbeatContext["state"];
|
|
197
|
+
}): AdapterExecutionResult {
|
|
198
|
+
return {
|
|
199
|
+
status: "failed",
|
|
200
|
+
summary: input.summary,
|
|
201
|
+
tokenInput: input.tokenInput,
|
|
202
|
+
tokenOutput: input.tokenOutput,
|
|
203
|
+
usdCost: input.usdCost,
|
|
204
|
+
...(input.usage ? { usage: input.usage } : {}),
|
|
205
|
+
pricingProviderType: input.pricingProviderType,
|
|
206
|
+
pricingModelId: input.pricingModelId,
|
|
207
|
+
outcome: toOutcome({
|
|
208
|
+
kind: "failed",
|
|
209
|
+
issueIdsTouched: issueIdsTouched(input.context),
|
|
210
|
+
actions: [{ type: "runtime.contract", status: "error", detail: input.summary }],
|
|
211
|
+
blockers: [{ code: "contract_invalid", message: input.summary, retryable: true }],
|
|
212
|
+
artifacts: [],
|
|
213
|
+
nextSuggestedState: "blocked"
|
|
214
|
+
}),
|
|
215
|
+
trace: input.trace,
|
|
216
|
+
nextState: input.nextState
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
133
220
|
function hasUsageMetrics(usage: RuntimeParsedUsage | undefined) {
|
|
134
221
|
if (!usage) {
|
|
135
222
|
return false;
|
|
@@ -372,12 +459,46 @@ export class GenericHeartbeatAdapter implements AgentAdapter {
|
|
|
372
459
|
nextState: context.state
|
|
373
460
|
};
|
|
374
461
|
}
|
|
462
|
+
if (!runtime.finalRunOutput) {
|
|
463
|
+
const usage = toNormalizedUsage(runtime.parsedUsage);
|
|
464
|
+
const detail = resolveFinalRunOutputContractDetail({ provider: this.providerType, runtime });
|
|
465
|
+
return createContractInvalidResult({
|
|
466
|
+
context,
|
|
467
|
+
provider: this.providerType,
|
|
468
|
+
summary: `${this.providerType} runtime failed contract validation: ${detail}`,
|
|
469
|
+
tokenInput: usageTokenInputTotal(runtime.parsedUsage),
|
|
470
|
+
tokenOutput: runtime.parsedUsage?.tokenOutput ?? 0,
|
|
471
|
+
usdCost: runtime.parsedUsage?.usdCost ?? 0,
|
|
472
|
+
...(usage ? { usage } : {}),
|
|
473
|
+
pricingProviderType: resolveCanonicalPricingProviderKey(this.providerType),
|
|
474
|
+
pricingModelId: context.runtime?.model?.trim() || null,
|
|
475
|
+
trace: {
|
|
476
|
+
command: runtime.commandUsed ?? context.runtime.command,
|
|
477
|
+
args: runtime.argsUsed,
|
|
478
|
+
cwd: context.runtime?.cwd,
|
|
479
|
+
exitCode: runtime.code,
|
|
480
|
+
elapsedMs: runtime.elapsedMs,
|
|
481
|
+
timedOut: runtime.timedOut,
|
|
482
|
+
failureType: "contract_invalid",
|
|
483
|
+
timeoutSource: runtime.timedOut ? "runtime" : null,
|
|
484
|
+
attemptCount: runtime.attemptCount,
|
|
485
|
+
attempts: runtime.attempts,
|
|
486
|
+
structuredOutputSource: runtime.structuredOutputSource,
|
|
487
|
+
structuredOutputDiagnostics: runtime.structuredOutputDiagnostics,
|
|
488
|
+
stdoutPreview: toPreview(runtime.stdout),
|
|
489
|
+
stderrPreview: toPreview(runtime.stderr),
|
|
490
|
+
transcript: runtime.transcript
|
|
491
|
+
},
|
|
492
|
+
nextState: context.state
|
|
493
|
+
});
|
|
494
|
+
}
|
|
375
495
|
return {
|
|
376
496
|
status: "ok",
|
|
377
497
|
summary: runtime.parsedUsage?.summary ?? `${this.providerType} runtime finished in ${runtime.elapsedMs}ms.`,
|
|
378
498
|
tokenInput: runtime.parsedUsage?.tokenInput ?? 0,
|
|
379
499
|
tokenOutput: runtime.parsedUsage?.tokenOutput ?? 0,
|
|
380
500
|
usdCost: runtime.parsedUsage?.usdCost ?? 0,
|
|
501
|
+
finalRunOutput: runtime.finalRunOutput,
|
|
381
502
|
outcome: toOutcome({
|
|
382
503
|
kind: "completed",
|
|
383
504
|
issueIdsTouched: issueIdsTouched(context),
|
|
@@ -409,23 +530,27 @@ export class GenericHeartbeatAdapter implements AgentAdapter {
|
|
|
409
530
|
}
|
|
410
531
|
|
|
411
532
|
const failedUsage = resolveFailedUsage(runtime);
|
|
412
|
-
const
|
|
413
|
-
|
|
533
|
+
const failure = classifyProviderFailure(this.providerType, {
|
|
534
|
+
detail: resolveRuntimeFailureDetail(runtime, this.providerType),
|
|
535
|
+
stdout: runtime.stdout,
|
|
536
|
+
stderr: runtime.stderr,
|
|
537
|
+
failureType: runtime.failureType
|
|
538
|
+
});
|
|
414
539
|
return {
|
|
415
540
|
status: "failed",
|
|
416
|
-
summary: runtime.parsedUsage?.summary ?? `${this.providerType} runtime failed: ${
|
|
541
|
+
summary: runtime.parsedUsage?.summary ?? `${this.providerType} runtime failed: ${failure.detail}`,
|
|
417
542
|
tokenInput: failedUsage.tokenInput,
|
|
418
543
|
tokenOutput: failedUsage.tokenOutput,
|
|
419
544
|
usdCost: failedUsage.usdCost,
|
|
420
545
|
outcome: toOutcome({
|
|
421
546
|
kind: "failed",
|
|
422
547
|
issueIdsTouched: issueIdsTouched(context),
|
|
423
|
-
actions: [{ type: "runtime.execute", status: "error", detail:
|
|
548
|
+
actions: [{ type: "runtime.execute", status: "error", detail: failure.detail }],
|
|
424
549
|
blockers: [
|
|
425
550
|
{
|
|
426
|
-
code:
|
|
427
|
-
message:
|
|
428
|
-
retryable:
|
|
551
|
+
code: failure.blockerCode,
|
|
552
|
+
message: failure.detail,
|
|
553
|
+
retryable: failure.retryable
|
|
429
554
|
}
|
|
430
555
|
],
|
|
431
556
|
artifacts: [],
|
|
@@ -449,6 +574,9 @@ export class GenericHeartbeatAdapter implements AgentAdapter {
|
|
|
449
574
|
stderrPreview: toPreview(runtime.stderr),
|
|
450
575
|
transcript: runtime.transcript
|
|
451
576
|
},
|
|
577
|
+
...(failure.providerUsageLimited
|
|
578
|
+
? { dispositionHint: buildProviderUsageLimitedDispositionHint(this.providerType, failure.detail) }
|
|
579
|
+
: {}),
|
|
452
580
|
nextState: context.state
|
|
453
581
|
};
|
|
454
582
|
}
|
|
@@ -838,12 +966,51 @@ export async function runDirectApiWork(
|
|
|
838
966
|
const prompt = createPrompt(context);
|
|
839
967
|
const runtime = await executeDirectApiRuntime(provider, prompt, context.runtime);
|
|
840
968
|
if (runtime.ok) {
|
|
969
|
+
if (!runtime.finalRunOutput) {
|
|
970
|
+
return createContractInvalidResult({
|
|
971
|
+
context,
|
|
972
|
+
provider,
|
|
973
|
+
summary: `${provider} runtime failed contract validation: ${runtime.summary ?? "Missing final JSON object."}`,
|
|
974
|
+
tokenInput: runtime.tokenInput ?? 0,
|
|
975
|
+
tokenOutput: runtime.tokenOutput ?? 0,
|
|
976
|
+
usdCost: runtime.usdCost ?? 0,
|
|
977
|
+
usage: {
|
|
978
|
+
inputTokens: runtime.tokenInput ?? 0,
|
|
979
|
+
cachedInputTokens: 0,
|
|
980
|
+
outputTokens: runtime.tokenOutput ?? 0,
|
|
981
|
+
...(runtime.usdCost !== undefined ? { costUsd: runtime.usdCost } : {}),
|
|
982
|
+
...(runtime.summary ? { summary: runtime.summary } : {})
|
|
983
|
+
},
|
|
984
|
+
pricingProviderType: runtime.provider,
|
|
985
|
+
pricingModelId: runtime.model,
|
|
986
|
+
trace: {
|
|
987
|
+
command: runtime.endpoint,
|
|
988
|
+
cwd: context.runtime?.cwd,
|
|
989
|
+
exitCode: runtime.statusCode,
|
|
990
|
+
elapsedMs: runtime.elapsedMs,
|
|
991
|
+
failureType: "contract_invalid",
|
|
992
|
+
usageSource: "structured",
|
|
993
|
+
attemptCount: runtime.attemptCount,
|
|
994
|
+
attempts: runtime.attempts.map((attempt) => ({
|
|
995
|
+
attempt: attempt.attempt,
|
|
996
|
+
code: attempt.statusCode || null,
|
|
997
|
+
timedOut: attempt.failureType === "timeout",
|
|
998
|
+
elapsedMs: attempt.elapsedMs,
|
|
999
|
+
signal: null,
|
|
1000
|
+
forcedKill: false
|
|
1001
|
+
})),
|
|
1002
|
+
stdoutPreview: runtime.responsePreview
|
|
1003
|
+
},
|
|
1004
|
+
nextState: context.state
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
841
1007
|
return {
|
|
842
1008
|
status: "ok",
|
|
843
1009
|
summary: runtime.summary ?? `${provider} runtime finished in ${runtime.elapsedMs}ms.`,
|
|
844
1010
|
tokenInput: runtime.tokenInput ?? 0,
|
|
845
1011
|
tokenOutput: runtime.tokenOutput ?? 0,
|
|
846
1012
|
usdCost: runtime.usdCost ?? 0,
|
|
1013
|
+
finalRunOutput: runtime.finalRunOutput,
|
|
847
1014
|
pricingProviderType: runtime.provider,
|
|
848
1015
|
pricingModelId: runtime.model,
|
|
849
1016
|
outcome: toOutcome({
|
|
@@ -875,12 +1042,15 @@ export async function runDirectApiWork(
|
|
|
875
1042
|
nextState: withProviderMetadata(context, provider, runtime.elapsedMs, runtime.statusCode)
|
|
876
1043
|
};
|
|
877
1044
|
}
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
1045
|
+
const failure = classifyProviderFailure(provider, {
|
|
1046
|
+
detail: runtime.error ?? "direct API request failed",
|
|
1047
|
+
stderr: runtime.error,
|
|
1048
|
+
stdout: runtime.responsePreview ?? "",
|
|
1049
|
+
failureType: runtime.failureType
|
|
1050
|
+
});
|
|
881
1051
|
return {
|
|
882
1052
|
status: "failed",
|
|
883
|
-
summary: `${provider} runtime failed: ${
|
|
1053
|
+
summary: `${provider} runtime failed: ${failure.detail}`,
|
|
884
1054
|
tokenInput: 0,
|
|
885
1055
|
tokenOutput: 0,
|
|
886
1056
|
usdCost: 0,
|
|
@@ -889,11 +1059,11 @@ export async function runDirectApiWork(
|
|
|
889
1059
|
outcome: toOutcome({
|
|
890
1060
|
kind: "failed",
|
|
891
1061
|
issueIdsTouched: issueIdsTouched(context),
|
|
892
|
-
actions: [{ type: "runtime.execute", status: "error", detail:
|
|
1062
|
+
actions: [{ type: "runtime.execute", status: "error", detail: failure.detail }],
|
|
893
1063
|
blockers: [{
|
|
894
|
-
code:
|
|
895
|
-
message:
|
|
896
|
-
retryable:
|
|
1064
|
+
code: failure.blockerCode,
|
|
1065
|
+
message: failure.detail,
|
|
1066
|
+
retryable: failure.retryable
|
|
897
1067
|
}],
|
|
898
1068
|
artifacts: [],
|
|
899
1069
|
nextSuggestedState: "blocked"
|
|
@@ -917,6 +1087,9 @@ export async function runDirectApiWork(
|
|
|
917
1087
|
stderrPreview: runtime.error,
|
|
918
1088
|
stdoutPreview: runtime.responsePreview
|
|
919
1089
|
},
|
|
1090
|
+
...(failure.providerUsageLimited
|
|
1091
|
+
? { dispositionHint: buildProviderUsageLimitedDispositionHint(provider, failure.detail) }
|
|
1092
|
+
: {}),
|
|
920
1093
|
nextState: context.state
|
|
921
1094
|
};
|
|
922
1095
|
}
|
|
@@ -1042,6 +1215,40 @@ export async function runProviderWork(
|
|
|
1042
1215
|
nextState: context.state
|
|
1043
1216
|
};
|
|
1044
1217
|
}
|
|
1218
|
+
if (!runtime.finalRunOutput) {
|
|
1219
|
+
const usage = toNormalizedUsage(runtime.parsedUsage);
|
|
1220
|
+
const detail = resolveFinalRunOutputContractDetail({ provider, runtime });
|
|
1221
|
+
return createContractInvalidResult({
|
|
1222
|
+
context,
|
|
1223
|
+
provider,
|
|
1224
|
+
summary: `${provider} runtime failed contract validation: ${detail}`,
|
|
1225
|
+
tokenInput: usageTokenInputTotal(runtime.parsedUsage),
|
|
1226
|
+
tokenOutput: runtime.parsedUsage?.outputTokens ?? runtime.parsedUsage?.tokenOutput ?? 0,
|
|
1227
|
+
usdCost: runtime.parsedUsage?.costUsd ?? runtime.parsedUsage?.usdCost ?? 0,
|
|
1228
|
+
...(usage ? { usage } : {}),
|
|
1229
|
+
pricingProviderType,
|
|
1230
|
+
pricingModelId,
|
|
1231
|
+
trace: {
|
|
1232
|
+
command: runtime.commandUsed ?? context.runtime?.command ?? provider,
|
|
1233
|
+
args: runtime.argsUsed,
|
|
1234
|
+
cwd: context.runtime?.cwd,
|
|
1235
|
+
exitCode: runtime.code,
|
|
1236
|
+
elapsedMs: runtime.elapsedMs,
|
|
1237
|
+
timedOut: runtime.timedOut,
|
|
1238
|
+
failureType: "contract_invalid",
|
|
1239
|
+
timeoutSource: runtime.timedOut ? "runtime" : null,
|
|
1240
|
+
usageSource: "structured",
|
|
1241
|
+
attemptCount: runtime.attemptCount,
|
|
1242
|
+
attempts: runtime.attempts,
|
|
1243
|
+
structuredOutputSource: runtime.structuredOutputSource,
|
|
1244
|
+
structuredOutputDiagnostics: runtime.structuredOutputDiagnostics,
|
|
1245
|
+
stdoutPreview: toPreview(runtime.stdout),
|
|
1246
|
+
stderrPreview: toPreview(runtime.stderr),
|
|
1247
|
+
transcript: runtime.transcript
|
|
1248
|
+
},
|
|
1249
|
+
nextState: context.state
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1045
1252
|
if (provider === "claude_code" && isClaudeRunIncomplete(runtime)) {
|
|
1046
1253
|
const detail = "Claude run reached max-turns before completing execution for this issue.";
|
|
1047
1254
|
const usage = toNormalizedUsage(runtime.parsedUsage);
|
|
@@ -1095,6 +1302,7 @@ export async function runProviderWork(
|
|
|
1095
1302
|
tokenInput,
|
|
1096
1303
|
tokenOutput,
|
|
1097
1304
|
usdCost,
|
|
1305
|
+
finalRunOutput: runtime.finalRunOutput,
|
|
1098
1306
|
usage,
|
|
1099
1307
|
pricingProviderType,
|
|
1100
1308
|
pricingModelId,
|
|
@@ -1128,11 +1336,15 @@ export async function runProviderWork(
|
|
|
1128
1336
|
};
|
|
1129
1337
|
}
|
|
1130
1338
|
const failedUsage = resolveFailedUsage(runtime);
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1339
|
+
const failure = classifyProviderFailure(provider, {
|
|
1340
|
+
detail: resolveRuntimeFailureDetail(runtime, provider),
|
|
1341
|
+
stdout: runtime.stdout,
|
|
1342
|
+
stderr: runtime.stderr,
|
|
1343
|
+
failureType: runtime.failureType
|
|
1344
|
+
});
|
|
1133
1345
|
return {
|
|
1134
1346
|
status: "failed",
|
|
1135
|
-
summary: runtime.parsedUsage?.summary ?? `${provider} runtime failed: ${
|
|
1347
|
+
summary: runtime.parsedUsage?.summary ?? `${provider} runtime failed: ${failure.detail}`,
|
|
1136
1348
|
tokenInput: failedUsage.tokenInput,
|
|
1137
1349
|
tokenOutput: failedUsage.tokenOutput,
|
|
1138
1350
|
usdCost: failedUsage.usdCost,
|
|
@@ -1142,12 +1354,12 @@ export async function runProviderWork(
|
|
|
1142
1354
|
outcome: toOutcome({
|
|
1143
1355
|
kind: "failed",
|
|
1144
1356
|
issueIdsTouched: issueIdsTouched(context),
|
|
1145
|
-
actions: [{ type: "runtime.execute", status: "error", detail:
|
|
1357
|
+
actions: [{ type: "runtime.execute", status: "error", detail: failure.detail }],
|
|
1146
1358
|
blockers: [
|
|
1147
1359
|
{
|
|
1148
|
-
code:
|
|
1149
|
-
message:
|
|
1150
|
-
retryable:
|
|
1360
|
+
code: failure.blockerCode,
|
|
1361
|
+
message: failure.detail,
|
|
1362
|
+
retryable: failure.retryable
|
|
1151
1363
|
}
|
|
1152
1364
|
],
|
|
1153
1365
|
artifacts: [],
|
|
@@ -1171,6 +1383,9 @@ export async function runProviderWork(
|
|
|
1171
1383
|
stderrPreview: toPreview(runtime.stderr),
|
|
1172
1384
|
transcript: runtime.transcript
|
|
1173
1385
|
},
|
|
1386
|
+
...(failure.providerUsageLimited
|
|
1387
|
+
? { dispositionHint: buildProviderUsageLimitedDispositionHint(provider, failure.detail) }
|
|
1388
|
+
: {}),
|
|
1174
1389
|
nextState: context.state
|
|
1175
1390
|
};
|
|
1176
1391
|
}
|
|
@@ -1220,7 +1435,7 @@ export async function runCursorWork(
|
|
|
1220
1435
|
if (
|
|
1221
1436
|
!runtime.ok &&
|
|
1222
1437
|
resumeState.resumeSessionId &&
|
|
1223
|
-
!
|
|
1438
|
+
!isProviderUsageLimitedRuntimeFailure(runtime) &&
|
|
1224
1439
|
isUnknownSessionError(runtime.stderr, runtime.stdout)
|
|
1225
1440
|
) {
|
|
1226
1441
|
const retry = withResolvedRuntimeUsage(
|
|
@@ -1331,7 +1546,12 @@ export async function runOpenCodeWork(context: HeartbeatContext): Promise<Adapte
|
|
|
1331
1546
|
{ provider: "opencode" }
|
|
1332
1547
|
);
|
|
1333
1548
|
const parsed = parseOpenCodeOutput(runtime.stdout);
|
|
1334
|
-
if (
|
|
1549
|
+
if (
|
|
1550
|
+
!runtime.ok &&
|
|
1551
|
+
resumeSessionId &&
|
|
1552
|
+
!isProviderUsageLimitedRuntimeFailure(runtime) &&
|
|
1553
|
+
isUnknownSessionError(runtime.stderr, runtime.stdout)
|
|
1554
|
+
) {
|
|
1335
1555
|
const retry = await executePromptRuntime(
|
|
1336
1556
|
context.runtime?.command ?? "opencode",
|
|
1337
1557
|
prompt,
|
|
@@ -1426,7 +1646,7 @@ export async function runGeminiCliWork(
|
|
|
1426
1646
|
if (
|
|
1427
1647
|
!runtime.ok &&
|
|
1428
1648
|
resumeState.resumeSessionId &&
|
|
1429
|
-
!
|
|
1649
|
+
!isProviderUsageLimitedRuntimeFailure(runtime) &&
|
|
1430
1650
|
isGeminiUnknownSessionError(runtime.stdout, runtime.stderr)
|
|
1431
1651
|
) {
|
|
1432
1652
|
const retry = withResolvedRuntimeUsage(
|
|
@@ -1535,6 +1755,7 @@ export function toProviderResult(
|
|
|
1535
1755
|
forcedKill: boolean;
|
|
1536
1756
|
}>;
|
|
1537
1757
|
parsedUsage?: RuntimeParsedUsage;
|
|
1758
|
+
finalRunOutput?: AdapterExecutionResult["finalRunOutput"];
|
|
1538
1759
|
structuredOutputSource?: "stdout" | "stderr";
|
|
1539
1760
|
structuredOutputDiagnostics?: {
|
|
1540
1761
|
stdoutJsonObjectCount: number;
|
|
@@ -1550,6 +1771,8 @@ export function toProviderResult(
|
|
|
1550
1771
|
| "json_missing"
|
|
1551
1772
|
| "json_on_stderr_only"
|
|
1552
1773
|
| "schema_or_shape_mismatch";
|
|
1774
|
+
finalRunOutputStatus?: "valid" | "missing" | "malformed" | "schema_mismatch";
|
|
1775
|
+
finalRunOutputError?: string;
|
|
1553
1776
|
claudeStopReason?: string;
|
|
1554
1777
|
claudeResultSubtype?: string;
|
|
1555
1778
|
claudeSessionId?: string;
|
|
@@ -1626,6 +1849,41 @@ export function toProviderResult(
|
|
|
1626
1849
|
nextState: applyProviderSessionState(context, provider, sessionUpdate)
|
|
1627
1850
|
};
|
|
1628
1851
|
}
|
|
1852
|
+
if (!runtime.finalRunOutput) {
|
|
1853
|
+
const usage = toNormalizedUsage(runtime.parsedUsage);
|
|
1854
|
+
const detail = resolveFinalRunOutputContractDetail({ provider, runtime });
|
|
1855
|
+
return createContractInvalidResult({
|
|
1856
|
+
context,
|
|
1857
|
+
provider,
|
|
1858
|
+
summary: `${provider} runtime failed contract validation: ${detail}`,
|
|
1859
|
+
tokenInput: usageTokenInputTotal(runtime.parsedUsage),
|
|
1860
|
+
tokenOutput: runtime.parsedUsage?.outputTokens ?? runtime.parsedUsage?.tokenOutput ?? 0,
|
|
1861
|
+
usdCost: runtime.parsedUsage?.costUsd ?? runtime.parsedUsage?.usdCost ?? 0,
|
|
1862
|
+
...(usage ? { usage } : {}),
|
|
1863
|
+
pricingProviderType,
|
|
1864
|
+
pricingModelId,
|
|
1865
|
+
trace: {
|
|
1866
|
+
command: runtime.commandUsed ?? context.runtime?.command ?? provider,
|
|
1867
|
+
args: runtime.argsUsed,
|
|
1868
|
+
cwd: context.runtime?.cwd,
|
|
1869
|
+
exitCode: runtime.code,
|
|
1870
|
+
elapsedMs: runtime.elapsedMs,
|
|
1871
|
+
timedOut: runtime.timedOut,
|
|
1872
|
+
failureType: "contract_invalid",
|
|
1873
|
+
timeoutSource: runtime.timedOut ? "runtime" : null,
|
|
1874
|
+
usageSource: "structured",
|
|
1875
|
+
attemptCount: runtime.attemptCount,
|
|
1876
|
+
attempts: runtime.attempts,
|
|
1877
|
+
session: sessionUpdate,
|
|
1878
|
+
structuredOutputSource: runtime.structuredOutputSource,
|
|
1879
|
+
structuredOutputDiagnostics: runtime.structuredOutputDiagnostics,
|
|
1880
|
+
stdoutPreview: toPreview(runtime.stdout),
|
|
1881
|
+
stderrPreview: toPreview(runtime.stderr),
|
|
1882
|
+
transcript: runtime.transcript
|
|
1883
|
+
},
|
|
1884
|
+
nextState: applyProviderSessionState(context, provider, sessionUpdate)
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1629
1887
|
const tokenOutput = runtime.parsedUsage?.outputTokens ?? runtime.parsedUsage?.tokenOutput ?? 0;
|
|
1630
1888
|
const usdCost = runtime.parsedUsage?.costUsd ?? runtime.parsedUsage?.usdCost ?? 0;
|
|
1631
1889
|
const usage = toNormalizedUsage(runtime.parsedUsage);
|
|
@@ -1636,6 +1894,7 @@ export function toProviderResult(
|
|
|
1636
1894
|
tokenInput: usageTokenInputTotal(runtime.parsedUsage),
|
|
1637
1895
|
tokenOutput,
|
|
1638
1896
|
usdCost,
|
|
1897
|
+
finalRunOutput: runtime.finalRunOutput,
|
|
1639
1898
|
usage,
|
|
1640
1899
|
pricingProviderType,
|
|
1641
1900
|
pricingModelId,
|
|
@@ -1670,11 +1929,15 @@ export function toProviderResult(
|
|
|
1670
1929
|
};
|
|
1671
1930
|
}
|
|
1672
1931
|
const failedUsage = resolveFailedUsage(runtime);
|
|
1673
|
-
const
|
|
1674
|
-
|
|
1932
|
+
const failure = classifyProviderFailure(provider, {
|
|
1933
|
+
detail: resolveRuntimeFailureDetail(runtime, provider),
|
|
1934
|
+
stdout: runtime.stdout,
|
|
1935
|
+
stderr: runtime.stderr,
|
|
1936
|
+
failureType: runtime.failureType
|
|
1937
|
+
});
|
|
1675
1938
|
return {
|
|
1676
1939
|
status: "failed",
|
|
1677
|
-
summary: runtime.parsedUsage?.summary ?? `${provider} runtime failed: ${
|
|
1940
|
+
summary: runtime.parsedUsage?.summary ?? `${provider} runtime failed: ${failure.detail}`,
|
|
1678
1941
|
tokenInput: failedUsage.tokenInput,
|
|
1679
1942
|
tokenOutput: failedUsage.tokenOutput,
|
|
1680
1943
|
usdCost: failedUsage.usdCost,
|
|
@@ -1684,12 +1947,12 @@ export function toProviderResult(
|
|
|
1684
1947
|
outcome: toOutcome({
|
|
1685
1948
|
kind: "failed",
|
|
1686
1949
|
issueIdsTouched: issueIdsTouched(context),
|
|
1687
|
-
actions: [{ type: "runtime.execute", status: "error", detail:
|
|
1950
|
+
actions: [{ type: "runtime.execute", status: "error", detail: failure.detail }],
|
|
1688
1951
|
blockers: [
|
|
1689
1952
|
{
|
|
1690
|
-
code:
|
|
1691
|
-
message:
|
|
1692
|
-
retryable:
|
|
1953
|
+
code: failure.blockerCode,
|
|
1954
|
+
message: failure.detail,
|
|
1955
|
+
retryable: failure.retryable
|
|
1693
1956
|
}
|
|
1694
1957
|
],
|
|
1695
1958
|
artifacts: [],
|
|
@@ -1714,6 +1977,9 @@ export function toProviderResult(
|
|
|
1714
1977
|
stderrPreview: toPreview(runtime.stderr),
|
|
1715
1978
|
transcript: runtime.transcript
|
|
1716
1979
|
},
|
|
1980
|
+
...(failure.providerUsageLimited
|
|
1981
|
+
? { dispositionHint: buildProviderUsageLimitedDispositionHint(provider, failure.detail) }
|
|
1982
|
+
: {}),
|
|
1717
1983
|
nextState: applyProviderSessionState(context, provider, sessionUpdate)
|
|
1718
1984
|
};
|
|
1719
1985
|
}
|
|
@@ -1724,29 +1990,99 @@ export function resolveRuntimeFailureDetail(runtime: {
|
|
|
1724
1990
|
code: number | null;
|
|
1725
1991
|
failureType?: "timeout" | "spawn_error" | "nonzero_exit";
|
|
1726
1992
|
attempts: Array<{ spawnErrorCode?: string }>;
|
|
1727
|
-
}) {
|
|
1993
|
+
}, provider?: AgentProviderType) {
|
|
1728
1994
|
const stderr = runtime.stderr.trim();
|
|
1995
|
+
const normalize = (detail: string) => (provider ? normalizeProviderFailureDetail(provider, detail) : detail);
|
|
1729
1996
|
if (stderr.length > 0) {
|
|
1730
|
-
return stderr;
|
|
1997
|
+
return normalize(extractStructuredRuntimeErrorDetail(stderr) ?? stderr);
|
|
1731
1998
|
}
|
|
1732
1999
|
const lastAttempt = runtime.attempts[runtime.attempts.length - 1];
|
|
1733
2000
|
if (runtime.failureType === "spawn_error") {
|
|
1734
2001
|
if (lastAttempt?.spawnErrorCode) {
|
|
1735
|
-
return `failed to launch runtime command (${lastAttempt.spawnErrorCode}). Verify the CLI is installed and on PATH
|
|
2002
|
+
return normalize(`failed to launch runtime command (${lastAttempt.spawnErrorCode}). Verify the CLI is installed and on PATH.`);
|
|
1736
2003
|
}
|
|
1737
|
-
return "failed to launch runtime command. Verify the CLI is installed and on PATH.";
|
|
2004
|
+
return normalize("failed to launch runtime command. Verify the CLI is installed and on PATH.");
|
|
1738
2005
|
}
|
|
1739
2006
|
if (runtime.failureType === "timeout") {
|
|
1740
|
-
return "timed out before completion. Increase runtimeTimeoutSec for this agent/runtime.";
|
|
2007
|
+
return normalize("timed out before completion. Increase runtimeTimeoutSec for this agent/runtime.");
|
|
1741
2008
|
}
|
|
1742
2009
|
if (runtime.code !== null) {
|
|
1743
|
-
return `process exited with code ${runtime.code} without stderr output
|
|
2010
|
+
return normalize(`process exited with code ${runtime.code} without stderr output.`);
|
|
1744
2011
|
}
|
|
1745
2012
|
const stdout = runtime.stdout.trim();
|
|
1746
2013
|
if (stdout.length > 0) {
|
|
1747
|
-
|
|
2014
|
+
const structuredStdoutDetail = extractStructuredRuntimeErrorDetail(stdout);
|
|
2015
|
+
if (structuredStdoutDetail) {
|
|
2016
|
+
return normalize(structuredStdoutDetail);
|
|
2017
|
+
}
|
|
2018
|
+
return normalize(`no stderr output; stdout preview: ${toPreview(stdout, 320)}`);
|
|
2019
|
+
}
|
|
2020
|
+
return normalize("runtime exited without diagnostic output.");
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
function extractStructuredRuntimeErrorDetail(text: string) {
|
|
2024
|
+
const normalized = text.trim();
|
|
2025
|
+
if (!normalized) {
|
|
2026
|
+
return null;
|
|
2027
|
+
}
|
|
2028
|
+
const candidatePayloads = collectJsonObjectCandidates(normalized);
|
|
2029
|
+
for (const candidate of candidatePayloads) {
|
|
2030
|
+
try {
|
|
2031
|
+
const parsed = JSON.parse(candidate) as unknown;
|
|
2032
|
+
const detail = extractErrorDetailFromUnknown(parsed);
|
|
2033
|
+
if (detail) {
|
|
2034
|
+
return detail;
|
|
2035
|
+
}
|
|
2036
|
+
} catch {
|
|
2037
|
+
// ignore malformed JSON fragments
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return null;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
function collectJsonObjectCandidates(text: string) {
|
|
2044
|
+
const candidates: string[] = [];
|
|
2045
|
+
if (text.startsWith("{") && text.endsWith("}")) {
|
|
2046
|
+
candidates.push(text);
|
|
2047
|
+
}
|
|
2048
|
+
for (const line of text.split(/\r?\n/)) {
|
|
2049
|
+
const trimmed = line.trim();
|
|
2050
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
2051
|
+
candidates.push(trimmed);
|
|
2052
|
+
}
|
|
1748
2053
|
}
|
|
1749
|
-
return
|
|
2054
|
+
return candidates;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
function extractErrorDetailFromUnknown(value: unknown): string | null {
|
|
2058
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2059
|
+
return null;
|
|
2060
|
+
}
|
|
2061
|
+
const record = value as Record<string, unknown>;
|
|
2062
|
+
const directCandidates = [
|
|
2063
|
+
record.detail,
|
|
2064
|
+
record.message,
|
|
2065
|
+
record.summary,
|
|
2066
|
+
record.reason,
|
|
2067
|
+
record.error,
|
|
2068
|
+
record.description
|
|
2069
|
+
];
|
|
2070
|
+
for (const candidate of directCandidates) {
|
|
2071
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
2072
|
+
return candidate.trim();
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
const nestedError = record.error;
|
|
2076
|
+
if (nestedError && typeof nestedError === "object" && !Array.isArray(nestedError)) {
|
|
2077
|
+
const nestedRecord = nestedError as Record<string, unknown>;
|
|
2078
|
+
const nestedCandidates = [nestedRecord.detail, nestedRecord.message, nestedRecord.reason, nestedRecord.description];
|
|
2079
|
+
for (const candidate of nestedCandidates) {
|
|
2080
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
2081
|
+
return candidate.trim();
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
return null;
|
|
1750
2086
|
}
|
|
1751
2087
|
|
|
1752
2088
|
export function parseOpenCodeOutput(stdout: string) {
|
|
@@ -2291,12 +2627,15 @@ export function createPrompt(context: HeartbeatContext) {
|
|
|
2291
2627
|
const agentGoals = context.goalContext?.agentGoals.length
|
|
2292
2628
|
? context.goalContext.agentGoals.map((goal) => `- ${goal}`).join("\n")
|
|
2293
2629
|
: "- No active agent goals";
|
|
2630
|
+
const isCommentOrderRun = context.wakeContext?.reason === "issue_comment_recipient";
|
|
2294
2631
|
const workItems = context.workItems.length
|
|
2295
2632
|
? context.workItems
|
|
2296
2633
|
.map((item) =>
|
|
2297
2634
|
[
|
|
2298
2635
|
`- [${item.issueId}] ${item.title}`,
|
|
2299
2636
|
` Project: ${item.projectName ?? item.projectId}`,
|
|
2637
|
+
item.parentIssueId ? ` Parent issue: ${item.parentIssueId}` : null,
|
|
2638
|
+
item.childIssueIds?.length ? ` Sub-issues: ${item.childIssueIds.join(", ")}` : null,
|
|
2300
2639
|
item.status ? ` Status: ${item.status}` : null,
|
|
2301
2640
|
item.priority ? ` Priority: ${item.priority}` : null,
|
|
2302
2641
|
item.body ? ` Body: ${item.body}` : null,
|
|
@@ -2316,6 +2655,25 @@ export function createPrompt(context: HeartbeatContext) {
|
|
|
2316
2655
|
)
|
|
2317
2656
|
.join("\n")
|
|
2318
2657
|
: "- No assigned work";
|
|
2658
|
+
const wakeContextLines = context.wakeContext
|
|
2659
|
+
? [
|
|
2660
|
+
"Wake context:",
|
|
2661
|
+
`- Reason: ${context.wakeContext.reason ?? "unspecified"}`,
|
|
2662
|
+
`- Trigger comment: ${context.wakeContext.commentId ?? "none"}`,
|
|
2663
|
+
`- Comment order: ${context.wakeContext.commentBody ?? "none"}`,
|
|
2664
|
+
`- Linked issues: ${context.wakeContext.issueIds?.length ? context.wakeContext.issueIds.join(", ") : "none"}`
|
|
2665
|
+
].join("\n")
|
|
2666
|
+
: "";
|
|
2667
|
+
const commentOrderDirectives =
|
|
2668
|
+
isCommentOrderRun
|
|
2669
|
+
? [
|
|
2670
|
+
"Comment-order directives:",
|
|
2671
|
+
"- The triggering comment is the primary order for this run.",
|
|
2672
|
+
"- Treat linked issue details as read-only context unless explicitly asked for broader issue updates.",
|
|
2673
|
+
"- Do not rerun full issue backlogs/checklists by default.",
|
|
2674
|
+
"- Apply only the requested delta from the comment unless explicitly asked to do more."
|
|
2675
|
+
].join("\n")
|
|
2676
|
+
: "";
|
|
2319
2677
|
const memoryContext = context.memoryContext;
|
|
2320
2678
|
const memoryTacitNotes = memoryContext?.tacitNotes?.trim()
|
|
2321
2679
|
? memoryContext.tacitNotes.trim()
|
|
@@ -2353,20 +2711,32 @@ export function createPrompt(context: HeartbeatContext) {
|
|
|
2353
2711
|
"- You are running inside a BopoDev heartbeat for local repository work.",
|
|
2354
2712
|
"- Use BopoDev-specific injected skills only (bopodev-control-plane, bopodev-create-agent, para-memory-files) when relevant.",
|
|
2355
2713
|
"- Ignore unrelated third-party control-plane skills even if they exist in the runtime environment.",
|
|
2356
|
-
|
|
2357
|
-
|
|
2714
|
+
isCommentOrderRun
|
|
2715
|
+
? "- Prioritize the triggering comment order over general issue backlog work."
|
|
2716
|
+
: "- Prefer completing assigned issue work in this repository over non-essential coordination tasks.",
|
|
2717
|
+
isCommentOrderRun
|
|
2718
|
+
? "- Keep command usage narrowly focused on the comment request and required context."
|
|
2719
|
+
: "- Keep command usage minimal and task-focused; avoid broad repository scans unless strictly required for the assigned issue.",
|
|
2358
2720
|
"- Shell commands run under zsh on macOS; avoid Bash-only features such as `local -n`, `declare -n`, `mapfile`, and `readarray`.",
|
|
2359
2721
|
"- Prefer POSIX/zsh-compatible shell snippets, direct `curl` headers, and `jq`.",
|
|
2360
2722
|
"- Prefer heredoc/stdin payloads (for example `curl --data-binary @- <<'JSON' ... JSON`) so cleanup is not blocked by runtime policy.",
|
|
2361
2723
|
"- If payload files are required, write under `agents/<agent-id>/tmp/` (or OS temp via `mktemp`) and do not treat cleanup command failures as task blockers.",
|
|
2362
2724
|
"- If control-plane API connectivity fails, report the exact failing command/error once and stop retry loops for the same endpoint.",
|
|
2363
2725
|
"- For write_todos status values, only use: todo, in_progress, blocked, in_review, done, canceled (US spelling, not cancelled).",
|
|
2364
|
-
"- If any command fails, avoid further exploratory commands and still return the required final JSON
|
|
2365
|
-
"- Do not
|
|
2366
|
-
|
|
2726
|
+
"- If any command fails, avoid further exploratory commands and still return the required final JSON object.",
|
|
2727
|
+
"- Do not use emojis in issue comments, summaries, or status messages.",
|
|
2728
|
+
isCommentOrderRun
|
|
2729
|
+
? "- Do not stop after planning. Execute concrete steps only for the triggering comment order."
|
|
2730
|
+
: "- Do not stop after planning. You must execute concrete steps for assigned issues in this run (file edits, API calls, or other verifiable actions).",
|
|
2731
|
+
"- If you cannot complete concrete execution, explain the blocker plainly in `employee_comment` and add it to `errors` instead of claiming success.",
|
|
2367
2732
|
"- Treat file memory as source of truth for long-term context: append raw observations to daily notes first, then promote stable patterns to durable facts.",
|
|
2368
2733
|
"- Avoid writing duplicate durable facts when existing memory already contains the same lesson.",
|
|
2369
|
-
"- Your final output must be
|
|
2734
|
+
"- Your final output must be exactly one JSON object and nothing else.",
|
|
2735
|
+
"- Do not include any fields besides `employee_comment`, `results`, `errors`, and `artifacts`.",
|
|
2736
|
+
"- `employee_comment` must be markdown written like a concise employee updating a manager with concrete actions, outcome, and blocker or next step when relevant.",
|
|
2737
|
+
"- `results` must list concrete completed outcomes as short strings.",
|
|
2738
|
+
"- `errors` must list concrete blockers or failures as short strings and be empty on clean success.",
|
|
2739
|
+
"- `artifacts` must contain objects like {\"kind\":\"file\",\"path\":\"relative/path\"}.",
|
|
2370
2740
|
"- Do not invent token or cost values; the runtime records usage separately."
|
|
2371
2741
|
].join("\n");
|
|
2372
2742
|
|
|
@@ -2385,9 +2755,13 @@ ${projectGoals}
|
|
|
2385
2755
|
Agent goals:
|
|
2386
2756
|
${agentGoals}
|
|
2387
2757
|
|
|
2388
|
-
Assigned issues:
|
|
2758
|
+
${isCommentOrderRun ? "Linked issue context (read-only):" : "Assigned issues:"}
|
|
2389
2759
|
${workItems}
|
|
2390
2760
|
|
|
2761
|
+
${wakeContextLines}
|
|
2762
|
+
|
|
2763
|
+
${commentOrderDirectives}
|
|
2764
|
+
|
|
2391
2765
|
Memory context:
|
|
2392
2766
|
- Memory root: ${memoryContext?.memoryRoot ?? "Unavailable"}
|
|
2393
2767
|
- Tacit notes:
|
|
@@ -2401,8 +2775,8 @@ ${executionDirectives}
|
|
|
2401
2775
|
|
|
2402
2776
|
${controlPlaneDirectives}
|
|
2403
2777
|
|
|
2404
|
-
At the end of your response, output exactly one JSON object on a single line and nothing else:
|
|
2405
|
-
{"
|
|
2778
|
+
At the end of your response, output exactly one JSON object on a single line and nothing else. Use this exact schema:
|
|
2779
|
+
{"employee_comment":"markdown update to the manager","results":["short concrete outcome"],"errors":[],"artifacts":[{"kind":"file","path":"relative/path"}]}
|
|
2406
2780
|
`;
|
|
2407
2781
|
}
|
|
2408
2782
|
|