bopodev-agent-sdk 0.1.11 → 0.1.13
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-typecheck.log +1 -1
- package/dist/adapters/anthropic-api/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/anthropic-api/src/cli/index.d.ts +1 -0
- package/dist/adapters/anthropic-api/src/index.d.ts +16 -0
- package/dist/adapters/anthropic-api/src/server/execute.d.ts +2 -0
- package/dist/adapters/anthropic-api/src/server/index.d.ts +6 -0
- package/dist/adapters/anthropic-api/src/server/parse.d.ts +1 -0
- package/dist/adapters/anthropic-api/src/server/test.d.ts +2 -0
- package/dist/adapters/anthropic-api/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/anthropic-api/src/ui/index.d.ts +2 -0
- package/dist/adapters/anthropic-api/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/claude-code/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/claude-code/src/cli/index.d.ts +1 -0
- package/dist/adapters/claude-code/src/index.d.ts +16 -0
- package/dist/adapters/claude-code/src/server/execute.d.ts +2 -0
- package/dist/adapters/claude-code/src/server/index.d.ts +6 -0
- package/dist/adapters/claude-code/src/server/parse.d.ts +2 -0
- package/dist/adapters/claude-code/src/server/test.d.ts +2 -0
- package/dist/adapters/claude-code/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/claude-code/src/ui/index.d.ts +2 -0
- package/dist/adapters/claude-code/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/codex/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/codex/src/cli/index.d.ts +1 -0
- package/dist/adapters/codex/src/index.d.ts +34 -0
- package/dist/adapters/codex/src/server/execute.d.ts +2 -0
- package/dist/adapters/codex/src/server/index.d.ts +6 -0
- package/dist/adapters/codex/src/server/parse.d.ts +2 -0
- package/dist/adapters/codex/src/server/test.d.ts +2 -0
- package/dist/adapters/codex/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/codex/src/ui/index.d.ts +2 -0
- package/dist/adapters/codex/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/cursor/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/cursor/src/cli/index.d.ts +1 -0
- package/dist/adapters/cursor/src/index.d.ts +22 -0
- package/dist/adapters/cursor/src/server/execute.d.ts +2 -0
- package/dist/adapters/cursor/src/server/index.d.ts +6 -0
- package/dist/adapters/cursor/src/server/parse.d.ts +2 -0
- package/dist/adapters/cursor/src/server/test.d.ts +2 -0
- package/dist/adapters/cursor/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/cursor/src/ui/index.d.ts +2 -0
- package/dist/adapters/cursor/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/http/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/http/src/cli/index.d.ts +1 -0
- package/dist/adapters/http/src/index.d.ts +7 -0
- package/dist/adapters/http/src/server/execute.d.ts +2 -0
- package/dist/adapters/http/src/server/index.d.ts +6 -0
- package/dist/adapters/http/src/server/parse.d.ts +1 -0
- package/dist/adapters/http/src/server/test.d.ts +2 -0
- package/dist/adapters/http/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/http/src/ui/index.d.ts +2 -0
- package/dist/adapters/http/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/openai-api/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/openai-api/src/cli/index.d.ts +1 -0
- package/dist/adapters/openai-api/src/index.d.ts +22 -0
- package/dist/adapters/openai-api/src/server/execute.d.ts +2 -0
- package/dist/adapters/openai-api/src/server/index.d.ts +6 -0
- package/dist/adapters/openai-api/src/server/parse.d.ts +1 -0
- package/dist/adapters/openai-api/src/server/test.d.ts +2 -0
- package/dist/adapters/openai-api/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/openai-api/src/ui/index.d.ts +2 -0
- package/dist/adapters/openai-api/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/opencode/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/opencode/src/cli/index.d.ts +1 -0
- package/dist/adapters/opencode/src/index.d.ts +7 -0
- package/dist/adapters/opencode/src/server/execute.d.ts +2 -0
- package/dist/adapters/opencode/src/server/index.d.ts +6 -0
- package/dist/adapters/opencode/src/server/parse.d.ts +1 -0
- package/dist/adapters/opencode/src/server/test.d.ts +2 -0
- package/dist/adapters/opencode/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/opencode/src/ui/index.d.ts +2 -0
- package/dist/adapters/opencode/src/ui/parse-stdout.d.ts +6 -0
- package/dist/adapters/shell/src/cli/format-event.d.ts +1 -0
- package/dist/adapters/shell/src/cli/index.d.ts +1 -0
- package/dist/adapters/shell/src/index.d.ts +7 -0
- package/dist/adapters/shell/src/server/execute.d.ts +2 -0
- package/dist/adapters/shell/src/server/index.d.ts +6 -0
- package/dist/adapters/shell/src/server/parse.d.ts +1 -0
- package/dist/adapters/shell/src/server/test.d.ts +2 -0
- package/dist/adapters/shell/src/ui/build-config.d.ts +3 -0
- package/dist/adapters/shell/src/ui/index.d.ts +2 -0
- package/dist/adapters/shell/src/ui/parse-stdout.d.ts +6 -0
- package/dist/agent-sdk/src/adapters.d.ts +226 -1
- package/dist/agent-sdk/src/index.d.ts +2 -0
- package/dist/agent-sdk/src/registry.d.ts +2 -1
- package/dist/agent-sdk/src/runtime-core.d.ts +2 -0
- package/dist/agent-sdk/src/runtime-http.d.ts +38 -0
- package/dist/agent-sdk/src/runtime-parsers.d.ts +1 -0
- package/dist/agent-sdk/src/runtime.d.ts +36 -0
- package/dist/agent-sdk/src/types.d.ts +55 -0
- package/dist/contracts/src/index.d.ts +889 -12
- package/package.json +2 -2
- package/src/adapters.ts +385 -36
- package/src/index.ts +2 -0
- package/src/registry.ts +67 -18
- package/src/runtime-core.ts +7 -0
- package/src/runtime-http.ts +455 -0
- package/src/runtime-parsers.ts +6 -0
- package/src/runtime.ts +848 -33
- package/src/types.ts +61 -0
package/src/adapters.ts
CHANGED
|
@@ -10,7 +10,13 @@ import type {
|
|
|
10
10
|
HeartbeatContext
|
|
11
11
|
} from "./types";
|
|
12
12
|
import { ExecutionOutcomeSchema, type ExecutionOutcome } from "bopodev-contracts";
|
|
13
|
-
import { checkRuntimeCommandHealth, executeAgentRuntime, executePromptRuntime } from "./runtime";
|
|
13
|
+
import { checkRuntimeCommandHealth, executeAgentRuntime, executePromptRuntime } from "./runtime-core";
|
|
14
|
+
import {
|
|
15
|
+
executeDirectApiRuntime,
|
|
16
|
+
probeDirectApiEnvironment,
|
|
17
|
+
resolveDirectApiCredentials,
|
|
18
|
+
type DirectApiProvider
|
|
19
|
+
} from "./runtime-http";
|
|
14
20
|
import { homedir } from "node:os";
|
|
15
21
|
import { join, resolve } from "node:path";
|
|
16
22
|
|
|
@@ -79,6 +85,28 @@ export class OpenCodeAdapter implements AgentAdapter {
|
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
export class OpenAIApiAdapter implements AgentAdapter {
|
|
89
|
+
providerType = "openai_api" as const;
|
|
90
|
+
|
|
91
|
+
async execute(context: HeartbeatContext): Promise<AdapterExecutionResult> {
|
|
92
|
+
if (context.workItems.length === 0) {
|
|
93
|
+
return createSkippedResult("OpenAI API", "openai_api", context);
|
|
94
|
+
}
|
|
95
|
+
return runDirectApiWork(context, "openai_api");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export class AnthropicApiAdapter implements AgentAdapter {
|
|
100
|
+
providerType = "anthropic_api" as const;
|
|
101
|
+
|
|
102
|
+
async execute(context: HeartbeatContext): Promise<AdapterExecutionResult> {
|
|
103
|
+
if (context.workItems.length === 0) {
|
|
104
|
+
return createSkippedResult("Anthropic API", "anthropic_api", context);
|
|
105
|
+
}
|
|
106
|
+
return runDirectApiWork(context, "anthropic_api");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
82
110
|
export class GenericHeartbeatAdapter implements AgentAdapter {
|
|
83
111
|
constructor(public providerType: "http" | "shell") {}
|
|
84
112
|
|
|
@@ -268,6 +296,24 @@ const staticMetadata: AdapterMetadata[] = [
|
|
|
268
296
|
supportsThinkingEffort: false,
|
|
269
297
|
requiresRuntimeCwd: true
|
|
270
298
|
},
|
|
299
|
+
{
|
|
300
|
+
providerType: "openai_api",
|
|
301
|
+
label: "OpenAI API",
|
|
302
|
+
supportsModelSelection: true,
|
|
303
|
+
supportsEnvironmentTest: true,
|
|
304
|
+
supportsWebSearch: false,
|
|
305
|
+
supportsThinkingEffort: false,
|
|
306
|
+
requiresRuntimeCwd: false
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
providerType: "anthropic_api",
|
|
310
|
+
label: "Anthropic API",
|
|
311
|
+
supportsModelSelection: true,
|
|
312
|
+
supportsEnvironmentTest: true,
|
|
313
|
+
supportsWebSearch: false,
|
|
314
|
+
supportsThinkingEffort: false,
|
|
315
|
+
requiresRuntimeCwd: false
|
|
316
|
+
},
|
|
271
317
|
{
|
|
272
318
|
providerType: "http",
|
|
273
319
|
label: "HTTP",
|
|
@@ -288,6 +334,8 @@ const staticMetadata: AdapterMetadata[] = [
|
|
|
288
334
|
}
|
|
289
335
|
];
|
|
290
336
|
|
|
337
|
+
const metadataByProviderType = new Map(staticMetadata.map((entry) => [entry.providerType, entry] as const));
|
|
338
|
+
|
|
291
339
|
const modelCatalog: Record<Exclude<AgentProviderType, "http" | "shell">, AdapterModelOption[]> = {
|
|
292
340
|
codex: [
|
|
293
341
|
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
|
|
@@ -312,13 +360,33 @@ const modelCatalog: Record<Exclude<AgentProviderType, "http" | "shell">, Adapter
|
|
|
312
360
|
{ id: "sonnet-4.5", label: "sonnet-4.5" },
|
|
313
361
|
{ id: "opus-4.6", label: "opus-4.6" }
|
|
314
362
|
],
|
|
315
|
-
opencode: []
|
|
363
|
+
opencode: [],
|
|
364
|
+
openai_api: [
|
|
365
|
+
{ id: "gpt-5", label: "GPT-5" },
|
|
366
|
+
{ id: "gpt-5-mini", label: "GPT-5 Mini" },
|
|
367
|
+
{ id: "gpt-5-nano", label: "GPT-5 Nano" },
|
|
368
|
+
{ id: "o3", label: "o3" },
|
|
369
|
+
{ id: "o4-mini", label: "o4-mini" }
|
|
370
|
+
],
|
|
371
|
+
anthropic_api: [
|
|
372
|
+
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
|
373
|
+
{ id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
|
|
374
|
+
{ id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" }
|
|
375
|
+
]
|
|
316
376
|
};
|
|
317
377
|
|
|
318
378
|
export function listAdapterMetadata(): AdapterMetadata[] {
|
|
319
379
|
return staticMetadata;
|
|
320
380
|
}
|
|
321
381
|
|
|
382
|
+
export function getAdapterMetadataByProviderType(providerType: AgentProviderType): AdapterMetadata {
|
|
383
|
+
const metadata = metadataByProviderType.get(providerType);
|
|
384
|
+
if (!metadata) {
|
|
385
|
+
throw new Error(`Missing adapter metadata for provider: ${providerType}`);
|
|
386
|
+
}
|
|
387
|
+
return metadata;
|
|
388
|
+
}
|
|
389
|
+
|
|
322
390
|
export async function listAdapterModels(
|
|
323
391
|
providerType: AgentProviderType,
|
|
324
392
|
runtime?: AgentRuntimeConfig
|
|
@@ -331,7 +399,7 @@ export async function listAdapterModels(
|
|
|
331
399
|
return dedupeModels([...discovered, ...modelCatalog.cursor]);
|
|
332
400
|
}
|
|
333
401
|
if (providerType === "opencode") {
|
|
334
|
-
const discovered = await
|
|
402
|
+
const discovered = await discoverOpenCodeModelsCached(runtime);
|
|
335
403
|
return dedupeModels(discovered);
|
|
336
404
|
}
|
|
337
405
|
return modelCatalog[providerType];
|
|
@@ -342,6 +410,9 @@ export async function testAdapterEnvironment(
|
|
|
342
410
|
runtime?: AgentRuntimeConfig
|
|
343
411
|
): Promise<AdapterEnvironmentResult> {
|
|
344
412
|
const checks: AdapterEnvironmentCheck[] = [];
|
|
413
|
+
if (providerType === "openai_api" || providerType === "anthropic_api") {
|
|
414
|
+
return testDirectApiEnvironment(providerType, runtime);
|
|
415
|
+
}
|
|
345
416
|
const command =
|
|
346
417
|
providerType === "cursor"
|
|
347
418
|
? (await resolveCursorLaunchConfig(runtime)).command
|
|
@@ -433,7 +504,7 @@ export async function testAdapterEnvironment(
|
|
|
433
504
|
};
|
|
434
505
|
}
|
|
435
506
|
|
|
436
|
-
function createSkippedResult(providerLabel: string, providerKey: string, context: HeartbeatContext): AdapterExecutionResult {
|
|
507
|
+
export function createSkippedResult(providerLabel: string, providerKey: string, context: HeartbeatContext): AdapterExecutionResult {
|
|
437
508
|
return {
|
|
438
509
|
status: "skipped",
|
|
439
510
|
summary: `${providerLabel} adapter: ${summarizeWork(context)}`,
|
|
@@ -452,7 +523,148 @@ function createSkippedResult(providerLabel: string, providerKey: string, context
|
|
|
452
523
|
};
|
|
453
524
|
}
|
|
454
525
|
|
|
455
|
-
async function
|
|
526
|
+
export async function runDirectApiWork(
|
|
527
|
+
context: HeartbeatContext,
|
|
528
|
+
provider: "openai_api" | "anthropic_api"
|
|
529
|
+
): Promise<AdapterExecutionResult> {
|
|
530
|
+
const prompt = createPrompt(context);
|
|
531
|
+
const runtime = await executeDirectApiRuntime(provider, prompt, context.runtime);
|
|
532
|
+
if (runtime.ok) {
|
|
533
|
+
return {
|
|
534
|
+
status: "ok",
|
|
535
|
+
summary: runtime.summary ?? `${provider} runtime finished in ${runtime.elapsedMs}ms.`,
|
|
536
|
+
tokenInput: runtime.tokenInput ?? 0,
|
|
537
|
+
tokenOutput: runtime.tokenOutput ?? 0,
|
|
538
|
+
usdCost: runtime.usdCost ?? 0,
|
|
539
|
+
outcome: toOutcome({
|
|
540
|
+
kind: "completed",
|
|
541
|
+
issueIdsTouched: issueIdsTouched(context),
|
|
542
|
+
actions: [{ type: "runtime.execute", status: "ok", detail: `${provider} runtime completed.` }],
|
|
543
|
+
blockers: [],
|
|
544
|
+
artifacts: [],
|
|
545
|
+
nextSuggestedState: "in_review"
|
|
546
|
+
}),
|
|
547
|
+
trace: {
|
|
548
|
+
command: runtime.endpoint,
|
|
549
|
+
cwd: context.runtime?.cwd,
|
|
550
|
+
exitCode: runtime.statusCode,
|
|
551
|
+
elapsedMs: runtime.elapsedMs,
|
|
552
|
+
failureType: runtime.failureType,
|
|
553
|
+
usageSource: "structured",
|
|
554
|
+
attemptCount: runtime.attemptCount,
|
|
555
|
+
attempts: runtime.attempts.map((attempt) => ({
|
|
556
|
+
attempt: attempt.attempt,
|
|
557
|
+
code: attempt.statusCode || null,
|
|
558
|
+
timedOut: attempt.failureType === "timeout",
|
|
559
|
+
elapsedMs: attempt.elapsedMs,
|
|
560
|
+
signal: null,
|
|
561
|
+
forcedKill: false
|
|
562
|
+
})),
|
|
563
|
+
stdoutPreview: runtime.responsePreview
|
|
564
|
+
},
|
|
565
|
+
nextState: withProviderMetadata(context, provider, runtime.elapsedMs, runtime.statusCode)
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
const failureDetail = runtime.error ?? "direct API request failed";
|
|
569
|
+
return {
|
|
570
|
+
status: "failed",
|
|
571
|
+
summary: `${provider} runtime failed: ${failureDetail}`,
|
|
572
|
+
tokenInput: 0,
|
|
573
|
+
tokenOutput: 0,
|
|
574
|
+
usdCost: 0,
|
|
575
|
+
outcome: toOutcome({
|
|
576
|
+
kind: "failed",
|
|
577
|
+
issueIdsTouched: issueIdsTouched(context),
|
|
578
|
+
actions: [{ type: "runtime.execute", status: "error", detail: failureDetail }],
|
|
579
|
+
blockers: [{
|
|
580
|
+
code: runtime.failureType ?? "runtime_failed",
|
|
581
|
+
message: failureDetail,
|
|
582
|
+
retryable: runtime.failureType !== "auth" && runtime.failureType !== "bad_response"
|
|
583
|
+
}],
|
|
584
|
+
artifacts: [],
|
|
585
|
+
nextSuggestedState: "blocked"
|
|
586
|
+
}),
|
|
587
|
+
trace: {
|
|
588
|
+
command: runtime.endpoint,
|
|
589
|
+
cwd: context.runtime?.cwd,
|
|
590
|
+
exitCode: runtime.statusCode || null,
|
|
591
|
+
elapsedMs: runtime.elapsedMs,
|
|
592
|
+
failureType: runtime.failureType,
|
|
593
|
+
usageSource: "none",
|
|
594
|
+
attemptCount: runtime.attemptCount,
|
|
595
|
+
attempts: runtime.attempts.map((attempt) => ({
|
|
596
|
+
attempt: attempt.attempt,
|
|
597
|
+
code: attempt.statusCode || null,
|
|
598
|
+
timedOut: attempt.failureType === "timeout",
|
|
599
|
+
elapsedMs: attempt.elapsedMs,
|
|
600
|
+
signal: null,
|
|
601
|
+
forcedKill: false
|
|
602
|
+
})),
|
|
603
|
+
stderrPreview: runtime.error,
|
|
604
|
+
stdoutPreview: runtime.responsePreview
|
|
605
|
+
},
|
|
606
|
+
nextState: context.state
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export async function testDirectApiEnvironment(
|
|
611
|
+
providerType: DirectApiProvider,
|
|
612
|
+
runtime?: AgentRuntimeConfig
|
|
613
|
+
): Promise<AdapterEnvironmentResult> {
|
|
614
|
+
const checks: AdapterEnvironmentCheck[] = [];
|
|
615
|
+
const credentials = resolveDirectApiCredentials(providerType, runtime);
|
|
616
|
+
if (!credentials.key) {
|
|
617
|
+
checks.push({
|
|
618
|
+
code: "api_key_missing",
|
|
619
|
+
level: "error",
|
|
620
|
+
message: `${providerType} API key is missing.`,
|
|
621
|
+
hint:
|
|
622
|
+
providerType === "openai_api"
|
|
623
|
+
? "Set OPENAI_API_KEY or BOPO_OPENAI_API_KEY in runtime env or host environment."
|
|
624
|
+
: "Set ANTHROPIC_API_KEY or BOPO_ANTHROPIC_API_KEY in runtime env or host environment."
|
|
625
|
+
});
|
|
626
|
+
return {
|
|
627
|
+
providerType,
|
|
628
|
+
status: "fail",
|
|
629
|
+
testedAt: new Date().toISOString(),
|
|
630
|
+
checks
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
checks.push({
|
|
634
|
+
code: "api_key_present",
|
|
635
|
+
level: "info",
|
|
636
|
+
message: "API key is present."
|
|
637
|
+
});
|
|
638
|
+
checks.push({
|
|
639
|
+
code: "base_url",
|
|
640
|
+
level: "info",
|
|
641
|
+
message: `Using base URL: ${credentials.baseUrl}`
|
|
642
|
+
});
|
|
643
|
+
const probe = await probeDirectApiEnvironment(providerType, runtime);
|
|
644
|
+
if (probe.ok) {
|
|
645
|
+
checks.push({
|
|
646
|
+
code: "api_probe_ok",
|
|
647
|
+
level: "info",
|
|
648
|
+
message: `${providerType} API probe succeeded.`
|
|
649
|
+
});
|
|
650
|
+
} else {
|
|
651
|
+
checks.push({
|
|
652
|
+
code: "api_probe_failed",
|
|
653
|
+
level: probe.statusCode === 401 || probe.statusCode === 403 ? "error" : "warn",
|
|
654
|
+
message: `${providerType} API probe failed.`,
|
|
655
|
+
detail: probe.message,
|
|
656
|
+
hint: probe.statusCode === 401 || probe.statusCode === 403 ? "Verify API key and organization/project access." : undefined
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
providerType,
|
|
661
|
+
status: toEnvironmentStatus(checks),
|
|
662
|
+
testedAt: new Date().toISOString(),
|
|
663
|
+
checks
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export async function runProviderWork(
|
|
456
668
|
context: HeartbeatContext,
|
|
457
669
|
provider: "claude_code" | "codex",
|
|
458
670
|
pricing: { inputRate: number; outputRate: number }
|
|
@@ -615,12 +827,13 @@ async function runProviderWork(
|
|
|
615
827
|
};
|
|
616
828
|
}
|
|
617
829
|
|
|
618
|
-
async function runCursorWork(context: HeartbeatContext): Promise<AdapterExecutionResult> {
|
|
830
|
+
export async function runCursorWork(context: HeartbeatContext): Promise<AdapterExecutionResult> {
|
|
619
831
|
const prompt = createPrompt(context);
|
|
620
832
|
const cursorLaunch = await resolveCursorLaunchConfig(context.runtime);
|
|
621
833
|
const cwd = context.runtime?.cwd?.trim() || process.cwd();
|
|
622
834
|
const resumeState = resolveCursorResumeState(context.state, cwd);
|
|
623
|
-
const runtimeTimeoutMs =
|
|
835
|
+
const runtimeTimeoutMs =
|
|
836
|
+
context.runtime?.timeoutMs && context.runtime.timeoutMs > 0 ? context.runtime.timeoutMs : 15 * 60 * 1000;
|
|
624
837
|
const buildArgs = (resumeSessionId: string | null) => {
|
|
625
838
|
const baseArgs = [...cursorLaunch.prefixArgs, "-p", "--output-format", "stream-json", "--workspace", cwd];
|
|
626
839
|
if (resumeSessionId) {
|
|
@@ -685,9 +898,11 @@ async function runCursorWork(context: HeartbeatContext): Promise<AdapterExecutio
|
|
|
685
898
|
});
|
|
686
899
|
}
|
|
687
900
|
|
|
688
|
-
async function runOpenCodeWork(context: HeartbeatContext): Promise<AdapterExecutionResult> {
|
|
901
|
+
export async function runOpenCodeWork(context: HeartbeatContext): Promise<AdapterExecutionResult> {
|
|
689
902
|
const prompt = createPrompt(context);
|
|
690
903
|
const model = context.runtime?.model?.trim();
|
|
904
|
+
const runtimeTimeoutMs =
|
|
905
|
+
context.runtime?.timeoutMs && context.runtime.timeoutMs > 0 ? context.runtime.timeoutMs : 5 * 60 * 1000;
|
|
691
906
|
if (!model) {
|
|
692
907
|
return {
|
|
693
908
|
status: "failed",
|
|
@@ -706,6 +921,32 @@ async function runOpenCodeWork(context: HeartbeatContext): Promise<AdapterExecut
|
|
|
706
921
|
nextState: context.state
|
|
707
922
|
};
|
|
708
923
|
}
|
|
924
|
+
try {
|
|
925
|
+
await ensureOpenCodeModelConfiguredAndAvailable({
|
|
926
|
+
model,
|
|
927
|
+
command: context.runtime?.command,
|
|
928
|
+
cwd: context.runtime?.cwd,
|
|
929
|
+
env: context.runtime?.env
|
|
930
|
+
});
|
|
931
|
+
} catch (error) {
|
|
932
|
+
const message = error instanceof Error ? error.message : "OpenCode model validation failed.";
|
|
933
|
+
return {
|
|
934
|
+
status: "failed",
|
|
935
|
+
summary: message,
|
|
936
|
+
tokenInput: 0,
|
|
937
|
+
tokenOutput: 0,
|
|
938
|
+
usdCost: 0,
|
|
939
|
+
outcome: toOutcome({
|
|
940
|
+
kind: "blocked",
|
|
941
|
+
issueIdsTouched: issueIdsTouched(context),
|
|
942
|
+
actions: [{ type: "runtime.validate", status: "error", detail: message }],
|
|
943
|
+
blockers: [{ code: "model_unavailable", message, retryable: false }],
|
|
944
|
+
artifacts: [],
|
|
945
|
+
nextSuggestedState: "blocked"
|
|
946
|
+
}),
|
|
947
|
+
nextState: context.state
|
|
948
|
+
};
|
|
949
|
+
}
|
|
709
950
|
const resumeSessionId = context.state.sessionId?.trim();
|
|
710
951
|
const baseArgs = ["run", "--format", "json", "--model", model];
|
|
711
952
|
if (resumeSessionId) {
|
|
@@ -716,6 +957,7 @@ async function runOpenCodeWork(context: HeartbeatContext): Promise<AdapterExecut
|
|
|
716
957
|
prompt,
|
|
717
958
|
{
|
|
718
959
|
...context.runtime,
|
|
960
|
+
timeoutMs: runtimeTimeoutMs,
|
|
719
961
|
args: [...baseArgs, ...(context.runtime?.args ?? [])]
|
|
720
962
|
},
|
|
721
963
|
{ provider: "opencode" }
|
|
@@ -727,6 +969,7 @@ async function runOpenCodeWork(context: HeartbeatContext): Promise<AdapterExecut
|
|
|
727
969
|
prompt,
|
|
728
970
|
{
|
|
729
971
|
...context.runtime,
|
|
972
|
+
timeoutMs: runtimeTimeoutMs,
|
|
730
973
|
args: ["run", "--format", "json", "--model", model, ...(context.runtime?.args ?? [])]
|
|
731
974
|
},
|
|
732
975
|
{ provider: "opencode" }
|
|
@@ -744,7 +987,7 @@ async function runOpenCodeWork(context: HeartbeatContext): Promise<AdapterExecut
|
|
|
744
987
|
});
|
|
745
988
|
}
|
|
746
989
|
|
|
747
|
-
function resolveFailedUsage(
|
|
990
|
+
export function resolveFailedUsage(
|
|
748
991
|
runtime: {
|
|
749
992
|
parsedUsage?: {
|
|
750
993
|
tokenInput?: number;
|
|
@@ -786,7 +1029,7 @@ function resolveFailedUsage(
|
|
|
786
1029
|
};
|
|
787
1030
|
}
|
|
788
1031
|
|
|
789
|
-
function toProviderResult(
|
|
1032
|
+
export function toProviderResult(
|
|
790
1033
|
context: HeartbeatContext,
|
|
791
1034
|
provider: AgentProviderType,
|
|
792
1035
|
prompt: string,
|
|
@@ -979,7 +1222,7 @@ function toProviderResult(
|
|
|
979
1222
|
};
|
|
980
1223
|
}
|
|
981
1224
|
|
|
982
|
-
function resolveRuntimeFailureDetail(runtime: {
|
|
1225
|
+
export function resolveRuntimeFailureDetail(runtime: {
|
|
983
1226
|
stderr: string;
|
|
984
1227
|
stdout: string;
|
|
985
1228
|
code: number | null;
|
|
@@ -1010,7 +1253,7 @@ function resolveRuntimeFailureDetail(runtime: {
|
|
|
1010
1253
|
return "runtime exited without diagnostic output.";
|
|
1011
1254
|
}
|
|
1012
1255
|
|
|
1013
|
-
function parseOpenCodeOutput(stdout: string) {
|
|
1256
|
+
export function parseOpenCodeOutput(stdout: string) {
|
|
1014
1257
|
let sessionId: string | null = null;
|
|
1015
1258
|
for (const line of stdout.split(/\r?\n/)) {
|
|
1016
1259
|
const trimmed = line.trim();
|
|
@@ -1031,7 +1274,7 @@ function parseOpenCodeOutput(stdout: string) {
|
|
|
1031
1274
|
return { sessionId };
|
|
1032
1275
|
}
|
|
1033
1276
|
|
|
1034
|
-
function resolveCursorResumeState(state: HeartbeatContext["state"], cwd: string) {
|
|
1277
|
+
export function resolveCursorResumeState(state: HeartbeatContext["state"], cwd: string) {
|
|
1035
1278
|
const savedSessionId = state.cursorSession?.sessionId?.trim() || state.sessionId?.trim() || null;
|
|
1036
1279
|
const savedCwd = state.cursorSession?.cwd?.trim() || state.cwd?.trim() || null;
|
|
1037
1280
|
if (!savedSessionId) {
|
|
@@ -1055,7 +1298,7 @@ function resolveCursorResumeState(state: HeartbeatContext["state"], cwd: string)
|
|
|
1055
1298
|
};
|
|
1056
1299
|
}
|
|
1057
1300
|
|
|
1058
|
-
function readRuntimeSessionId(
|
|
1301
|
+
export function readRuntimeSessionId(
|
|
1059
1302
|
runtime: {
|
|
1060
1303
|
structuredOutputDiagnostics?: {
|
|
1061
1304
|
claudeSessionId?: string;
|
|
@@ -1067,7 +1310,7 @@ function readRuntimeSessionId(
|
|
|
1067
1310
|
return runtime.structuredOutputDiagnostics?.cursorSessionId?.trim() || runtime.structuredOutputDiagnostics?.claudeSessionId?.trim() || fallback;
|
|
1068
1311
|
}
|
|
1069
1312
|
|
|
1070
|
-
function isUnknownSessionError(stderr: string, stdout: string) {
|
|
1313
|
+
export function isUnknownSessionError(stderr: string, stdout: string) {
|
|
1071
1314
|
const haystack = `${stderr}\n${stdout}`.toLowerCase();
|
|
1072
1315
|
return (
|
|
1073
1316
|
haystack.includes("unknown session") ||
|
|
@@ -1076,11 +1319,11 @@ function isUnknownSessionError(stderr: string, stdout: string) {
|
|
|
1076
1319
|
);
|
|
1077
1320
|
}
|
|
1078
1321
|
|
|
1079
|
-
function hasTrustFlag(args: string[]) {
|
|
1322
|
+
export function hasTrustFlag(args: string[]) {
|
|
1080
1323
|
return args.includes("--trust") || args.includes("--yolo") || args.includes("-f");
|
|
1081
1324
|
}
|
|
1082
1325
|
|
|
1083
|
-
function resolveRuntimeCommand(providerType: AgentProviderType, runtime?: AgentRuntimeConfig) {
|
|
1326
|
+
export function resolveRuntimeCommand(providerType: AgentProviderType, runtime?: AgentRuntimeConfig) {
|
|
1084
1327
|
if (runtime?.command?.trim()) return runtime.command.trim();
|
|
1085
1328
|
if (providerType === "claude_code") return "claude";
|
|
1086
1329
|
if (providerType === "codex") return "codex";
|
|
@@ -1089,7 +1332,7 @@ function resolveRuntimeCommand(providerType: AgentProviderType, runtime?: AgentR
|
|
|
1089
1332
|
return providerType;
|
|
1090
1333
|
}
|
|
1091
1334
|
|
|
1092
|
-
async function runRuntimeProbe(providerType: AgentProviderType, runtime?: AgentRuntimeConfig) {
|
|
1335
|
+
export async function runRuntimeProbe(providerType: AgentProviderType, runtime?: AgentRuntimeConfig) {
|
|
1093
1336
|
const prompt = "Respond with hello.";
|
|
1094
1337
|
if (providerType === "claude_code" || providerType === "codex") {
|
|
1095
1338
|
return executeAgentRuntime(providerType, prompt, {
|
|
@@ -1136,7 +1379,7 @@ async function runRuntimeProbe(providerType: AgentProviderType, runtime?: AgentR
|
|
|
1136
1379
|
});
|
|
1137
1380
|
}
|
|
1138
1381
|
|
|
1139
|
-
async function discoverCursorModels(runtime?: AgentRuntimeConfig): Promise<AdapterModelOption[]> {
|
|
1382
|
+
export async function discoverCursorModels(runtime?: AgentRuntimeConfig): Promise<AdapterModelOption[]> {
|
|
1140
1383
|
const cursorLaunch = await resolveCursorLaunchConfig(runtime);
|
|
1141
1384
|
const probe = await executePromptRuntime(
|
|
1142
1385
|
cursorLaunch.command,
|
|
@@ -1155,25 +1398,98 @@ async function discoverCursorModels(runtime?: AgentRuntimeConfig): Promise<Adapt
|
|
|
1155
1398
|
return parseModelLines(`${probe.stdout}\n${probe.stderr}`);
|
|
1156
1399
|
}
|
|
1157
1400
|
|
|
1158
|
-
async function discoverOpenCodeModels(runtime?: AgentRuntimeConfig): Promise<AdapterModelOption[]> {
|
|
1401
|
+
export async function discoverOpenCodeModels(runtime?: AgentRuntimeConfig): Promise<AdapterModelOption[]> {
|
|
1159
1402
|
const probe = await executePromptRuntime(
|
|
1160
1403
|
resolveRuntimeCommand("opencode", runtime),
|
|
1161
|
-
"
|
|
1404
|
+
"",
|
|
1162
1405
|
{
|
|
1163
1406
|
...runtime,
|
|
1164
1407
|
args: ["models"],
|
|
1165
|
-
timeoutMs:
|
|
1408
|
+
timeoutMs: 120_000,
|
|
1166
1409
|
retryCount: 0
|
|
1167
1410
|
},
|
|
1168
1411
|
{ provider: "opencode" }
|
|
1169
1412
|
);
|
|
1170
|
-
if (!probe.ok && !probe.stdout.trim()) {
|
|
1413
|
+
if (!probe.ok && !probe.stdout.trim() && !probe.stderr.trim()) {
|
|
1171
1414
|
return [];
|
|
1172
1415
|
}
|
|
1173
|
-
return parseModelLines(probe.stdout).filter((entry) => entry.id.includes("/"));
|
|
1416
|
+
return parseModelLines(`${probe.stdout}\n${probe.stderr}`).filter((entry) => entry.id.includes("/"));
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
const OPENCODE_MODEL_DISCOVERY_TTL_MS = 60_000;
|
|
1420
|
+
const openCodeModelDiscoveryCache = new Map<string, { expiresAt: number; models: AdapterModelOption[] }>();
|
|
1421
|
+
|
|
1422
|
+
function normalizeRuntimeEnv(env: unknown): Record<string, string> {
|
|
1423
|
+
if (!env || typeof env !== "object" || Array.isArray(env)) {
|
|
1424
|
+
return {};
|
|
1425
|
+
}
|
|
1426
|
+
const out: Record<string, string> = {};
|
|
1427
|
+
for (const [key, value] of Object.entries(env as Record<string, unknown>)) {
|
|
1428
|
+
if (typeof value === "string") {
|
|
1429
|
+
out[key] = value;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return out;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
function openCodeModelCacheKey(runtime?: AgentRuntimeConfig) {
|
|
1436
|
+
const command = resolveRuntimeCommand("opencode", runtime);
|
|
1437
|
+
const cwd = runtime?.cwd?.trim() || process.cwd();
|
|
1438
|
+
const env = normalizeRuntimeEnv(runtime?.env);
|
|
1439
|
+
const envSignature = Object.entries(env)
|
|
1440
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
1441
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
1442
|
+
.join("\n");
|
|
1443
|
+
return `${command}\n${cwd}\n${envSignature}`;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
export async function discoverOpenCodeModelsCached(runtime?: AgentRuntimeConfig): Promise<AdapterModelOption[]> {
|
|
1447
|
+
const key = openCodeModelCacheKey(runtime);
|
|
1448
|
+
const now = Date.now();
|
|
1449
|
+
for (const [cacheKey, cacheValue] of openCodeModelDiscoveryCache.entries()) {
|
|
1450
|
+
if (cacheValue.expiresAt <= now) {
|
|
1451
|
+
openCodeModelDiscoveryCache.delete(cacheKey);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
const cached = openCodeModelDiscoveryCache.get(key);
|
|
1455
|
+
if (cached && cached.expiresAt > now) {
|
|
1456
|
+
return cached.models;
|
|
1457
|
+
}
|
|
1458
|
+
const models = await discoverOpenCodeModels(runtime);
|
|
1459
|
+
openCodeModelDiscoveryCache.set(key, {
|
|
1460
|
+
expiresAt: now + OPENCODE_MODEL_DISCOVERY_TTL_MS,
|
|
1461
|
+
models
|
|
1462
|
+
});
|
|
1463
|
+
return models;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
export async function ensureOpenCodeModelConfiguredAndAvailable(input: {
|
|
1467
|
+
model?: string;
|
|
1468
|
+
command?: string;
|
|
1469
|
+
cwd?: string;
|
|
1470
|
+
env?: Record<string, string>;
|
|
1471
|
+
}) {
|
|
1472
|
+
const normalizedModel = input.model?.trim();
|
|
1473
|
+
if (!normalizedModel) {
|
|
1474
|
+
throw new Error("OpenCode requires runtimeModel in provider/model format.");
|
|
1475
|
+
}
|
|
1476
|
+
const models = await discoverOpenCodeModelsCached({
|
|
1477
|
+
command: input.command,
|
|
1478
|
+
cwd: input.cwd,
|
|
1479
|
+
env: input.env
|
|
1480
|
+
});
|
|
1481
|
+
if (models.length === 0) {
|
|
1482
|
+
throw new Error("OpenCode returned no models. Run `opencode models` and verify provider auth.");
|
|
1483
|
+
}
|
|
1484
|
+
if (!models.some((entry) => entry.id === normalizedModel)) {
|
|
1485
|
+
const sample = models.slice(0, 12).map((entry) => entry.id).join(", ");
|
|
1486
|
+
throw new Error(
|
|
1487
|
+
`Configured OpenCode model is unavailable: ${normalizedModel}. Available models: ${sample}${models.length > 12 ? ", ..." : ""}`
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1174
1490
|
}
|
|
1175
1491
|
|
|
1176
|
-
function parseModelLines(text: string): AdapterModelOption[] {
|
|
1492
|
+
export function parseModelLines(text: string): AdapterModelOption[] {
|
|
1177
1493
|
const out: AdapterModelOption[] = [];
|
|
1178
1494
|
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1179
1495
|
for (const line of lines) {
|
|
@@ -1203,7 +1519,7 @@ function parseModelLines(text: string): AdapterModelOption[] {
|
|
|
1203
1519
|
return dedupeModels(out);
|
|
1204
1520
|
}
|
|
1205
1521
|
|
|
1206
|
-
function dedupeModels(models: AdapterModelOption[]) {
|
|
1522
|
+
export function dedupeModels(models: AdapterModelOption[]) {
|
|
1207
1523
|
const seen = new Set<string>();
|
|
1208
1524
|
const deduped: AdapterModelOption[] = [];
|
|
1209
1525
|
for (const model of models) {
|
|
@@ -1215,7 +1531,7 @@ function dedupeModels(models: AdapterModelOption[]) {
|
|
|
1215
1531
|
return deduped;
|
|
1216
1532
|
}
|
|
1217
1533
|
|
|
1218
|
-
async function resolveCursorLaunchConfig(runtime?: AgentRuntimeConfig): Promise<{ command: string; prefixArgs: string[] }> {
|
|
1534
|
+
export async function resolveCursorLaunchConfig(runtime?: AgentRuntimeConfig): Promise<{ command: string; prefixArgs: string[] }> {
|
|
1219
1535
|
const configuredCommand = runtime?.command?.trim();
|
|
1220
1536
|
if (configuredCommand) {
|
|
1221
1537
|
const commandToken = configuredCommand.split(/[\\/]/).pop()?.toLowerCase() ?? configuredCommand.toLowerCase();
|
|
@@ -1256,13 +1572,13 @@ async function resolveCursorLaunchConfig(runtime?: AgentRuntimeConfig): Promise<
|
|
|
1256
1572
|
return { command: "agent", prefixArgs: [] };
|
|
1257
1573
|
}
|
|
1258
1574
|
|
|
1259
|
-
function toEnvironmentStatus(checks: AdapterEnvironmentCheck[]): "pass" | "warn" | "fail" {
|
|
1575
|
+
export function toEnvironmentStatus(checks: AdapterEnvironmentCheck[]): "pass" | "warn" | "fail" {
|
|
1260
1576
|
if (checks.some((check) => check.level === "error")) return "fail";
|
|
1261
1577
|
if (checks.some((check) => check.level === "warn")) return "warn";
|
|
1262
1578
|
return "pass";
|
|
1263
1579
|
}
|
|
1264
1580
|
|
|
1265
|
-
function createPrompt(context: HeartbeatContext) {
|
|
1581
|
+
export function createPrompt(context: HeartbeatContext) {
|
|
1266
1582
|
const bootstrapPrompt = context.runtime?.bootstrapPrompt?.trim();
|
|
1267
1583
|
const companyGoals = context.goalContext?.companyGoals.length
|
|
1268
1584
|
? context.goalContext.companyGoals.map((goal) => `- ${goal}`).join("\n")
|
|
@@ -1283,13 +1599,33 @@ function createPrompt(context: HeartbeatContext) {
|
|
|
1283
1599
|
item.priority ? ` Priority: ${item.priority}` : null,
|
|
1284
1600
|
item.body ? ` Body: ${item.body}` : null,
|
|
1285
1601
|
item.labels?.length ? ` Labels: ${item.labels.join(", ")}` : null,
|
|
1286
|
-
item.tags?.length ? ` Tags: ${item.tags.join(", ")}` : null
|
|
1602
|
+
item.tags?.length ? ` Tags: ${item.tags.join(", ")}` : null,
|
|
1603
|
+
item.attachments?.length
|
|
1604
|
+
? [
|
|
1605
|
+
" Attachments:",
|
|
1606
|
+
...item.attachments.map((attachment) =>
|
|
1607
|
+
` - ${attachment.fileName} | path: ${attachment.absolutePath} | relative: ${attachment.relativePath}`
|
|
1608
|
+
)
|
|
1609
|
+
].join("\n")
|
|
1610
|
+
: null
|
|
1287
1611
|
]
|
|
1288
1612
|
.filter(Boolean)
|
|
1289
1613
|
.join("\n")
|
|
1290
1614
|
)
|
|
1291
1615
|
.join("\n")
|
|
1292
1616
|
: "- No assigned work";
|
|
1617
|
+
const memoryContext = context.memoryContext;
|
|
1618
|
+
const memoryTacitNotes = memoryContext?.tacitNotes?.trim()
|
|
1619
|
+
? memoryContext.tacitNotes.trim()
|
|
1620
|
+
: "No tacit notes were recorded yet.";
|
|
1621
|
+
const memoryDurableFacts =
|
|
1622
|
+
memoryContext?.durableFacts && memoryContext.durableFacts.length > 0
|
|
1623
|
+
? memoryContext.durableFacts.map((fact) => `- ${fact}`).join("\n")
|
|
1624
|
+
: "- No durable facts available.";
|
|
1625
|
+
const memoryDailyNotes =
|
|
1626
|
+
memoryContext?.dailyNotes && memoryContext.dailyNotes.length > 0
|
|
1627
|
+
? memoryContext.dailyNotes.map((note) => `- ${note}`).join("\n")
|
|
1628
|
+
: "- No recent daily notes.";
|
|
1293
1629
|
|
|
1294
1630
|
const executionDirectives = [
|
|
1295
1631
|
"Execution directives:",
|
|
@@ -1299,11 +1635,15 @@ function createPrompt(context: HeartbeatContext) {
|
|
|
1299
1635
|
"- Prefer completing assigned issue work in this repository over non-essential coordination tasks.",
|
|
1300
1636
|
"- Keep command usage minimal and task-focused; avoid broad repository scans unless strictly required for the assigned issue.",
|
|
1301
1637
|
"- Shell commands run under zsh on macOS; avoid Bash-only features such as `local -n`, `declare -n`, `mapfile`, and `readarray`.",
|
|
1302
|
-
"- Prefer POSIX/zsh-compatible shell snippets, direct `curl` headers, `jq
|
|
1638
|
+
"- Prefer POSIX/zsh-compatible shell snippets, direct `curl` headers, and `jq`.",
|
|
1639
|
+
"- Prefer heredoc/stdin payloads (for example `curl --data-binary @- <<'JSON' ... JSON`) so cleanup is not blocked by runtime policy.",
|
|
1640
|
+
"- 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.",
|
|
1303
1641
|
"- If control-plane API connectivity fails, report the exact failing command/error once and stop retry loops for the same endpoint.",
|
|
1304
1642
|
"- If any command fails, avoid further exploratory commands and still return the required final JSON summary.",
|
|
1305
1643
|
"- Do not stop after planning. You must execute concrete steps for assigned issues in this run (file edits, API calls, or other verifiable actions).",
|
|
1306
1644
|
"- If you cannot complete concrete execution, set summary to include the blocker explicitly instead of claiming success.",
|
|
1645
|
+
"- 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.",
|
|
1646
|
+
"- Avoid writing duplicate durable facts when existing memory already contains the same lesson.",
|
|
1307
1647
|
"- Your final output must be only the JSON object below, with no prose before or after it.",
|
|
1308
1648
|
"- Do not invent token or cost values; the runtime records usage separately."
|
|
1309
1649
|
].join("\n");
|
|
@@ -1326,6 +1666,15 @@ ${agentGoals}
|
|
|
1326
1666
|
Assigned issues:
|
|
1327
1667
|
${workItems}
|
|
1328
1668
|
|
|
1669
|
+
Memory context:
|
|
1670
|
+
- Memory root: ${memoryContext?.memoryRoot ?? "Unavailable"}
|
|
1671
|
+
- Tacit notes:
|
|
1672
|
+
${memoryTacitNotes}
|
|
1673
|
+
- Durable facts:
|
|
1674
|
+
${memoryDurableFacts}
|
|
1675
|
+
- Recent daily notes:
|
|
1676
|
+
${memoryDailyNotes}
|
|
1677
|
+
|
|
1329
1678
|
${executionDirectives}
|
|
1330
1679
|
|
|
1331
1680
|
At the end of your response, output exactly one JSON object on a single line and nothing else:
|
|
@@ -1333,7 +1682,7 @@ At the end of your response, output exactly one JSON object on a single line and
|
|
|
1333
1682
|
`;
|
|
1334
1683
|
}
|
|
1335
1684
|
|
|
1336
|
-
function withProviderMetadata(
|
|
1685
|
+
export function withProviderMetadata(
|
|
1337
1686
|
context: HeartbeatContext,
|
|
1338
1687
|
provider: string,
|
|
1339
1688
|
lastRuntimeMs?: number,
|
|
@@ -1351,7 +1700,7 @@ function withProviderMetadata(
|
|
|
1351
1700
|
};
|
|
1352
1701
|
}
|
|
1353
1702
|
|
|
1354
|
-
function applyProviderSessionState(
|
|
1703
|
+
export function applyProviderSessionState(
|
|
1355
1704
|
context: HeartbeatContext,
|
|
1356
1705
|
provider: AgentProviderType,
|
|
1357
1706
|
sessionUpdate?: ProviderSessionUpdate,
|
|
@@ -1385,14 +1734,14 @@ function applyProviderSessionState(
|
|
|
1385
1734
|
return nextState;
|
|
1386
1735
|
}
|
|
1387
1736
|
|
|
1388
|
-
function toPreview(value: string, max = 1600) {
|
|
1737
|
+
export function toPreview(value: string, max = 1600) {
|
|
1389
1738
|
if (value.length <= max) {
|
|
1390
1739
|
return value;
|
|
1391
1740
|
}
|
|
1392
1741
|
return `${value.slice(0, max)}\n...[truncated]`;
|
|
1393
1742
|
}
|
|
1394
1743
|
|
|
1395
|
-
function buildMissingStructuredOutputDetail(
|
|
1744
|
+
export function buildMissingStructuredOutputDetail(
|
|
1396
1745
|
provider: string,
|
|
1397
1746
|
runtime: {
|
|
1398
1747
|
structuredOutputSource?: "stdout" | "stderr";
|
|
@@ -1476,7 +1825,7 @@ function buildMissingStructuredOutputDetail(
|
|
|
1476
1825
|
return hints.length > 0 ? `${base} Diagnostics: ${[...hints, ...diagnostics].join("; ")}.` : `${base} Diagnostics: ${diagnostics.join("; ")}.`;
|
|
1477
1826
|
}
|
|
1478
1827
|
|
|
1479
|
-
function isClaudeRunIncomplete(runtime: {
|
|
1828
|
+
export function isClaudeRunIncomplete(runtime: {
|
|
1480
1829
|
structuredOutputDiagnostics?: {
|
|
1481
1830
|
claudeStopReason?: string;
|
|
1482
1831
|
claudeResultSubtype?: string;
|