bopodev-agent-sdk 0.1.28 → 0.1.29
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/dist/agent-sdk/src/types.d.ts +4 -0
- package/dist/contracts/src/index.d.ts +5 -0
- package/package.json +2 -2
- package/src/adapters.ts +66 -7
- package/src/types.ts +4 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> bopodev-agent-sdk@0.1.
|
|
3
|
+
> bopodev-agent-sdk@0.1.29 build /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
|
|
4
4
|
> tsc -p tsconfig.json --emitDeclarationOnly
|
|
5
5
|
|
|
@@ -4,6 +4,10 @@ export interface AgentWorkItem {
|
|
|
4
4
|
issueId: string;
|
|
5
5
|
projectId: string;
|
|
6
6
|
parentIssueId?: string | null;
|
|
7
|
+
/** Linked planning goals for this issue, if any. */
|
|
8
|
+
goalIds?: string[];
|
|
9
|
+
/** Root-to-leaf chain per linked goal (same order as goalIds). */
|
|
10
|
+
goalAncestryChains?: string[][];
|
|
7
11
|
childIssueIds?: string[];
|
|
8
12
|
projectName?: string | null;
|
|
9
13
|
title: string;
|
|
@@ -127,8 +127,10 @@ export declare const IssueSchema: z.ZodObject<{
|
|
|
127
127
|
companyId: z.ZodString;
|
|
128
128
|
projectId: z.ZodString;
|
|
129
129
|
parentIssueId: z.ZodNullable<z.ZodString>;
|
|
130
|
+
goalIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
130
131
|
title: z.ZodString;
|
|
131
132
|
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
133
|
+
externalLink: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
132
134
|
status: z.ZodEnum<{
|
|
133
135
|
blocked: "blocked";
|
|
134
136
|
todo: "todo";
|
|
@@ -192,8 +194,10 @@ export declare const IssueDetailSchema: z.ZodObject<{
|
|
|
192
194
|
companyId: z.ZodString;
|
|
193
195
|
projectId: z.ZodString;
|
|
194
196
|
parentIssueId: z.ZodNullable<z.ZodString>;
|
|
197
|
+
goalIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
195
198
|
title: z.ZodString;
|
|
196
199
|
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
200
|
+
externalLink: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
197
201
|
status: z.ZodEnum<{
|
|
198
202
|
blocked: "blocked";
|
|
199
203
|
todo: "todo";
|
|
@@ -310,6 +314,7 @@ export declare const GoalSchema: z.ZodObject<{
|
|
|
310
314
|
companyId: z.ZodString;
|
|
311
315
|
projectId: z.ZodNullable<z.ZodString>;
|
|
312
316
|
parentGoalId: z.ZodNullable<z.ZodString>;
|
|
317
|
+
ownerAgentId: z.ZodNullable<z.ZodString>;
|
|
313
318
|
level: z.ZodEnum<{
|
|
314
319
|
agent: "agent";
|
|
315
320
|
company: "company";
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bopodev-agent-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"types": "src/index.ts",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"bopodev-contracts": "0.1.
|
|
9
|
+
"bopodev-contracts": "0.1.29"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc -p tsconfig.json --emitDeclarationOnly",
|
package/src/adapters.ts
CHANGED
|
@@ -11,7 +11,11 @@ import type {
|
|
|
11
11
|
HeartbeatContext,
|
|
12
12
|
HeartbeatPromptMode
|
|
13
13
|
} from "./types";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
ExecutionOutcomeSchema,
|
|
16
|
+
type AgentFinalRunOutput,
|
|
17
|
+
type ExecutionOutcome
|
|
18
|
+
} from "bopodev-contracts";
|
|
15
19
|
import {
|
|
16
20
|
checkRuntimeCommandHealth,
|
|
17
21
|
containsUsageLimitHardStopFailure,
|
|
@@ -161,6 +165,32 @@ function toNormalizedUsage(usage: RuntimeParsedUsage | undefined): AdapterNormal
|
|
|
161
165
|
};
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Cursor and Gemini CLIs emit stream-json transcripts with usage, not a standalone heartbeat JSON blob.
|
|
170
|
+
* When structured usage was resolved from the stream but no final JSON object exists, synthesize the
|
|
171
|
+
* minimal contract shape so downstream heartbeat persistence stays consistent.
|
|
172
|
+
*/
|
|
173
|
+
function synthesizeStreamFinalRunOutput(
|
|
174
|
+
provider: AgentProviderType,
|
|
175
|
+
parsedUsage: RuntimeParsedUsage | undefined
|
|
176
|
+
): AgentFinalRunOutput | null {
|
|
177
|
+
if (provider !== "cursor" && provider !== "gemini_cli") {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
if (!parsedUsage) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const summary =
|
|
184
|
+
(typeof parsedUsage.summary === "string" && parsedUsage.summary.trim()) ||
|
|
185
|
+
`${provider} runtime completed.`;
|
|
186
|
+
return {
|
|
187
|
+
employee_comment: summary,
|
|
188
|
+
results: [],
|
|
189
|
+
errors: [],
|
|
190
|
+
artifacts: []
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
164
194
|
function usageTokenInputTotal(usage: RuntimeParsedUsage | undefined) {
|
|
165
195
|
if (!usage) {
|
|
166
196
|
return 0;
|
|
@@ -1850,7 +1880,9 @@ export function toProviderResult(
|
|
|
1850
1880
|
nextState: applyProviderSessionState(context, provider, sessionUpdate)
|
|
1851
1881
|
};
|
|
1852
1882
|
}
|
|
1853
|
-
|
|
1883
|
+
const resolvedFinalRunOutput =
|
|
1884
|
+
runtime.finalRunOutput ?? synthesizeStreamFinalRunOutput(provider, runtime.parsedUsage);
|
|
1885
|
+
if (!resolvedFinalRunOutput) {
|
|
1854
1886
|
const usage = toNormalizedUsage(runtime.parsedUsage);
|
|
1855
1887
|
const detail = resolveFinalRunOutputContractDetail({ provider, runtime });
|
|
1856
1888
|
return createContractInvalidResult({
|
|
@@ -1895,7 +1927,7 @@ export function toProviderResult(
|
|
|
1895
1927
|
tokenInput: usageTokenInputTotal(runtime.parsedUsage),
|
|
1896
1928
|
tokenOutput,
|
|
1897
1929
|
usdCost,
|
|
1898
|
-
finalRunOutput:
|
|
1930
|
+
finalRunOutput: resolvedFinalRunOutput,
|
|
1899
1931
|
usage,
|
|
1900
1932
|
pricingProviderType,
|
|
1901
1933
|
pricingModelId,
|
|
@@ -2646,9 +2678,31 @@ function clipPromptText(text: string, max: number | null): string {
|
|
|
2646
2678
|
const HEARTBEAT_JSON_SCHEMA_FOOTER = `At the end of your response, output exactly one JSON object on a single line and nothing else. Use this exact schema:
|
|
2647
2679
|
{"employee_comment":"markdown update to the manager","results":["short concrete outcome"],"errors":[],"artifacts":[{"kind":"file","path":"relative/path"}]}`;
|
|
2648
2680
|
|
|
2681
|
+
/** Injected every heartbeat when the runtime sets BOPODEV_AGENT_OPERATING_DIR (no need to store paths in bootstrapPrompt). */
|
|
2682
|
+
function buildOperatingDirectoryPromptPrefix(context: HeartbeatContext): string {
|
|
2683
|
+
const dir = context.runtime?.env?.BOPODEV_AGENT_OPERATING_DIR?.trim();
|
|
2684
|
+
if (!dir) {
|
|
2685
|
+
return "";
|
|
2686
|
+
}
|
|
2687
|
+
return [
|
|
2688
|
+
"Operating context:",
|
|
2689
|
+
`- Your agent operating directory (read AGENTS.md, HEARTBEAT.md, SOUL.md, TOOLS.md here when they exist): ${dir}`,
|
|
2690
|
+
""
|
|
2691
|
+
].join("\n");
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
function composeBootstrapPreamble(context: HeartbeatContext): string {
|
|
2695
|
+
const operating = buildOperatingDirectoryPromptPrefix(context);
|
|
2696
|
+
const user = context.runtime?.bootstrapPrompt?.trim();
|
|
2697
|
+
if (!user) {
|
|
2698
|
+
return operating;
|
|
2699
|
+
}
|
|
2700
|
+
return operating ? `${operating}${user}\n\n` : `${user}\n\n`;
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2649
2703
|
function buildIdleMicroPrompt(context: HeartbeatContext): string {
|
|
2650
|
-
const
|
|
2651
|
-
return `${
|
|
2704
|
+
const preamble = composeBootstrapPreamble(context);
|
|
2705
|
+
return `${preamble}Idle heartbeat (micro prompt): agent ${context.agentId} (${context.agent.name}) has no assigned issues this run. Summarize readiness in \`employee_comment\`; leave \`results\` empty unless you completed verifiable work. Use \`BOPODEV_*\` for control-plane API calls when needed.
|
|
2652
2706
|
|
|
2653
2707
|
${HEARTBEAT_JSON_SCHEMA_FOOTER}
|
|
2654
2708
|
`;
|
|
@@ -2673,7 +2727,7 @@ export function createPrompt(context: HeartbeatContext) {
|
|
|
2673
2727
|
if (context.idleMicroPrompt && context.workItems.length === 0 && !isCommentOrderRunEarly) {
|
|
2674
2728
|
return buildIdleMicroPrompt(context);
|
|
2675
2729
|
}
|
|
2676
|
-
const
|
|
2730
|
+
const bootstrapPreamble = composeBootstrapPreamble(context);
|
|
2677
2731
|
const promptMode = resolveHeartbeatPromptModeForPrompt(context);
|
|
2678
2732
|
const isCompact = promptMode === "compact";
|
|
2679
2733
|
const memoryCap = resolveMemorySectionMaxChars(promptMode);
|
|
@@ -2696,6 +2750,11 @@ export function createPrompt(context: HeartbeatContext) {
|
|
|
2696
2750
|
`- [${item.issueId}] ${item.title}`,
|
|
2697
2751
|
` Project: ${item.projectName ?? item.projectId}`,
|
|
2698
2752
|
item.parentIssueId ? ` Parent issue: ${item.parentIssueId}` : null,
|
|
2753
|
+
...(item.goalAncestryChains && item.goalAncestryChains.length > 0
|
|
2754
|
+
? item.goalAncestryChains.map(
|
|
2755
|
+
(chain, idx) => ` Linked goal ${idx + 1} (root → leaf): ${chain.join(" → ")}`
|
|
2756
|
+
)
|
|
2757
|
+
: []),
|
|
2699
2758
|
item.childIssueIds?.length ? ` Sub-issues: ${item.childIssueIds.join(", ")}` : null,
|
|
2700
2759
|
item.status ? ` Status: ${item.status}` : null,
|
|
2701
2760
|
item.priority ? ` Priority: ${item.priority}` : null,
|
|
@@ -2819,7 +2878,7 @@ export function createPrompt(context: HeartbeatContext) {
|
|
|
2819
2878
|
"- Do not invent token or cost values; the runtime records usage separately."
|
|
2820
2879
|
].join("\n");
|
|
2821
2880
|
|
|
2822
|
-
return `${
|
|
2881
|
+
return `${bootstrapPreamble}You are ${context.agent.name} (${context.agent.role}), agent ${context.agentId}.
|
|
2823
2882
|
Heartbeat run ${context.heartbeatRunId}.
|
|
2824
2883
|
${isCompact ? "Prompt profile: compact (issue bodies are not inlined—use GET /issues/:id to hydrate).\n" : ""}
|
|
2825
2884
|
Company:
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,10 @@ export interface AgentWorkItem {
|
|
|
6
6
|
issueId: string;
|
|
7
7
|
projectId: string;
|
|
8
8
|
parentIssueId?: string | null;
|
|
9
|
+
/** Linked planning goals for this issue, if any. */
|
|
10
|
+
goalIds?: string[];
|
|
11
|
+
/** Root-to-leaf chain per linked goal (same order as goalIds). */
|
|
12
|
+
goalAncestryChains?: string[][];
|
|
9
13
|
childIssueIds?: string[];
|
|
10
14
|
projectName?: string | null;
|
|
11
15
|
title: string;
|