bopodev-agent-sdk 0.1.27 → 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.
@@ -1,5 +1,5 @@
1
1
 
2
2
  
3
- > bopodev-agent-sdk@0.1.27 build /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
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
 
@@ -1,4 +1,4 @@
1
1
 
2
- > bopodev-agent-sdk@0.1.15 lint /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
2
+ > bopodev-agent-sdk@0.1.28 lint /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
3
3
  > tsc -p tsconfig.json --noEmit
4
4
 
@@ -1,4 +1,4 @@
1
1
 
2
- > bopodev-agent-sdk@0.1.26 typecheck /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
2
+ > bopodev-agent-sdk@0.1.20 typecheck /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
3
3
  > tsc -p tsconfig.json --noEmit
4
4
 
@@ -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";
@@ -4422,6 +4427,15 @@ export declare const AuditEventSchema: z.ZodObject<{
4422
4427
  payload: z.ZodRecord<z.ZodString, z.ZodUnknown>;
4423
4428
  createdAt: z.ZodString;
4424
4429
  }, z.core.$strip>;
4430
+ export declare const HeartbeatRunTypeSchema: z.ZodEnum<{
4431
+ failed: "failed";
4432
+ running: "running";
4433
+ no_assigned_work: "no_assigned_work";
4434
+ work: "work";
4435
+ budget_skip: "budget_skip";
4436
+ overlap_skip: "overlap_skip";
4437
+ other_skip: "other_skip";
4438
+ }>;
4425
4439
  export declare const HeartbeatRunSchema: z.ZodObject<{
4426
4440
  id: z.ZodString;
4427
4441
  companyId: z.ZodString;
@@ -4440,6 +4454,15 @@ export declare const HeartbeatRunSchema: z.ZodObject<{
4440
4454
  startedAt: z.ZodString;
4441
4455
  finishedAt: z.ZodNullable<z.ZodString>;
4442
4456
  message: z.ZodOptional<z.ZodString>;
4457
+ runType: z.ZodOptional<z.ZodEnum<{
4458
+ failed: "failed";
4459
+ running: "running";
4460
+ no_assigned_work: "no_assigned_work";
4461
+ work: "work";
4462
+ budget_skip: "budget_skip";
4463
+ overlap_skip: "overlap_skip";
4464
+ other_skip: "other_skip";
4465
+ }>>;
4443
4466
  }, z.core.$strip>;
4444
4467
  export declare const HeartbeatRunMessageSchema: z.ZodObject<{
4445
4468
  id: z.ZodString;
@@ -4528,6 +4551,15 @@ export declare const HeartbeatRunDetailSchema: z.ZodObject<{
4528
4551
  startedAt: z.ZodString;
4529
4552
  finishedAt: z.ZodNullable<z.ZodString>;
4530
4553
  message: z.ZodOptional<z.ZodString>;
4554
+ runType: z.ZodOptional<z.ZodEnum<{
4555
+ failed: "failed";
4556
+ running: "running";
4557
+ no_assigned_work: "no_assigned_work";
4558
+ work: "work";
4559
+ budget_skip: "budget_skip";
4560
+ overlap_skip: "overlap_skip";
4561
+ other_skip: "other_skip";
4562
+ }>>;
4531
4563
  }, z.core.$strip>;
4532
4564
  details: z.ZodNullable<z.ZodObject<{
4533
4565
  status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "bopodev-agent-sdk",
3
- "version": "0.1.27",
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.27"
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 { ExecutionOutcomeSchema, type ExecutionOutcome } from "bopodev-contracts";
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
- if (!runtime.finalRunOutput) {
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: runtime.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 bootstrapPrompt = context.runtime?.bootstrapPrompt?.trim();
2651
- return `${bootstrapPrompt ? `${bootstrapPrompt}\n\n` : ""}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.
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 bootstrapPrompt = context.runtime?.bootstrapPrompt?.trim();
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 `${bootstrapPrompt ? `${bootstrapPrompt}\n\n` : ""}You are ${context.agent.name} (${context.agent.role}), agent ${context.agentId}.
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;
@@ -1,2 +0,0 @@
1
- import type { AgentRuntimeConfig, AdapterQuotaProbeResult } from "../../../../agent-sdk/src/types";
2
- export declare function probeQuota(runtime?: AgentRuntimeConfig): Promise<AdapterQuotaProbeResult>;
@@ -1,4 +0,0 @@
1
- import type { AgentProviderType, AdapterQuotaProbeResult, AgentRuntimeConfig } from "./types";
2
- export declare function createUnsupportedQuotaProbeResult(providerType: AgentProviderType, message: string): AdapterQuotaProbeResult;
3
- export declare function createAuthRequiredQuotaProbeResult(providerType: AgentProviderType, message: string): AdapterQuotaProbeResult;
4
- export declare function resolveMergedEnv(runtime?: AgentRuntimeConfig): NodeJS.ProcessEnv;