bopodev-agent-sdk 0.1.28 → 0.1.30

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.28 build /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
3
+ > bopodev-agent-sdk@0.1.30 build /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
4
4
  > tsc -p tsconfig.json --emitDeclarationOnly
5
5
 
@@ -0,0 +1,4 @@
1
+
2
+ > bopodev-agent-sdk@0.1.15 lint /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
3
+ > tsc -p tsconfig.json --noEmit
4
+
@@ -1,4 +1,4 @@
1
1
 
2
- > bopodev-agent-sdk@0.1.20 typecheck /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
2
+ > bopodev-agent-sdk@0.1.29 typecheck /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
3
3
  > tsc -p tsconfig.json --noEmit
4
4
 
@@ -0,0 +1,2 @@
1
+ import type { AgentRuntimeConfig, AdapterQuotaProbeResult } from "../../../../agent-sdk/src/types";
2
+ export declare function probeQuota(runtime?: AgentRuntimeConfig): Promise<AdapterQuotaProbeResult>;
@@ -0,0 +1 @@
1
+ export declare function formatStdoutEvent(line: string, debug: boolean): void;
@@ -0,0 +1 @@
1
+ export { formatStdoutEvent } from "./format-event";
@@ -0,0 +1,7 @@
1
+ import type { AdapterMetadata, AdapterModule } from "../../../agent-sdk/src/types";
2
+ export declare const type: "openclaw_gateway";
3
+ export declare const label = "OpenClaw Gateway";
4
+ export declare const models: readonly [];
5
+ export declare const agentConfigurationDoc = "Use when:\n- You run a self-hosted [OpenClaw](https://docs.openclaw.ai/) gateway and want Bopo heartbeats to invoke `agent` / `agent.wait` over the documented WebSocket protocol.\n\nConfigure:\n- **Command**: WebSocket URL (e.g. `ws://127.0.0.1:18789`). Alternatively set `OPENCLAW_GATEWAY_URL` in runtime environment.\n- **Runtime environment**: `OPENCLAW_GATEWAY_TOKEN` or `OPENCLAW_GATEWAY_PASSWORD` (required). Optional: `OPENCLAW_AGENT_ID`, `OPENCLAW_SESSION_KEY`, `OPENCLAW_SESSION_KEY_STRATEGY` (`issue` | `run` | `fixed`), `OPENCLAW_AGENT_WAIT_MS`, `OPENCLAW_DEVICE_PRIVATE_KEY_PEM` (stable device identity), `BOPO_OPENCLAW_DISABLE_DEVICE_AUTH=1` only if your gateway explicitly allows insecure mode.\n\nDo not use when:\n- You need a local CLI subprocess adapter \u2014 use Codex, Claude Code, or OpenCode instead.";
6
+ export declare const metadata: AdapterMetadata;
7
+ export declare const openclawGatewayAdapterModule: AdapterModule;
@@ -0,0 +1,25 @@
1
+ export declare function publicKeyRawBase64UrlFromPem(publicKeyPem: string): string;
2
+ export declare function signDevicePayload(privateKeyPem: string, payload: string): string;
3
+ export type OpenClawDeviceIdentity = {
4
+ deviceId: string;
5
+ publicKeyPem: string;
6
+ privateKeyPem: string;
7
+ };
8
+ export declare function generateEphemeralDeviceIdentity(): OpenClawDeviceIdentity;
9
+ export declare function loadDeviceIdentityFromPrivateKeyPem(privateKeyPem: string): OpenClawDeviceIdentity;
10
+ /**
11
+ * OpenClaw gateway device signing payload (v3), compatible with gateway verification.
12
+ * @see https://docs.openclaw.ai/gateway/protocol
13
+ */
14
+ export declare function buildOpenClawDeviceAuthPayloadV3(params: {
15
+ deviceId: string;
16
+ clientId: string;
17
+ clientMode: string;
18
+ role: string;
19
+ scopes: string[];
20
+ signedAtMs: number;
21
+ token?: string | null;
22
+ nonce: string;
23
+ platform?: string | null;
24
+ deviceFamily?: string | null;
25
+ }): string;
@@ -0,0 +1,2 @@
1
+ import type { HeartbeatContext, AdapterExecutionResult } from "../../../../agent-sdk/src/types";
2
+ export declare function execute(context: HeartbeatContext): Promise<AdapterExecutionResult>;
@@ -0,0 +1,56 @@
1
+ import { type ParsedOpenClawSessionUsage } from "./parse";
2
+ export declare class GatewayResponseError extends Error {
3
+ readonly gatewayCode?: string;
4
+ readonly gatewayDetails?: unknown;
5
+ constructor(message: string, gatewayCode?: string, gatewayDetails?: unknown);
6
+ }
7
+ export type OpenClawGatewayRunOptions = {
8
+ gatewayUrl: string;
9
+ token?: string | null;
10
+ password?: string | null;
11
+ devicePrivateKeyPem?: string | null;
12
+ /** When true, omit `device` on `connect` (gateway must allow disabled device auth). */
13
+ disableDeviceAuth?: boolean;
14
+ agentId?: string | null;
15
+ /** For `sessionKeyStrategy: "fixed"` — OpenClaw session key string. */
16
+ openclawSessionKey?: string | null;
17
+ sessionKeyStrategy?: "issue" | "run" | "fixed";
18
+ primaryIssueId?: string | null;
19
+ /** OpenClaw `agent.timeout` (seconds). */
20
+ agentTimeoutSec?: number | null;
21
+ model?: string | null;
22
+ message: string;
23
+ idempotencyKey: string;
24
+ connectTimeoutMs: number;
25
+ agentWaitTimeoutMs: number;
26
+ onStdoutLine: (line: string) => void;
27
+ onTranscriptEvent?: (event: {
28
+ kind: "system" | "assistant" | "thinking" | "tool_call" | "tool_result" | "result" | "stderr";
29
+ label?: string;
30
+ text?: string;
31
+ payload?: string;
32
+ signalLevel?: "high" | "medium" | "low" | "noise";
33
+ groupKey?: string;
34
+ source?: "stdout" | "stderr";
35
+ }) => void;
36
+ abortSignal?: AbortSignal;
37
+ };
38
+ export type OpenClawGatewayRunResult = {
39
+ ok: boolean;
40
+ summary: string;
41
+ runId?: string;
42
+ timedOut?: boolean;
43
+ errorMessage?: string;
44
+ tokenInput: number;
45
+ tokenOutput: number;
46
+ /** Cache / context read tokens when reported by `sessions.usage`. */
47
+ cachedInputTokens: number;
48
+ usdCost: number;
49
+ model?: string | null;
50
+ /** Resolved OpenClaw runtime model provider (e.g. anthropic), when available. */
51
+ openclawModelProvider?: string | null;
52
+ provider?: string | null;
53
+ /** Where token/model/cost metadata came from, for traces. */
54
+ openclawUsageSource?: ParsedOpenClawSessionUsage["source"] | "none";
55
+ };
56
+ export declare function runOpenClawGatewayTurn(options: OpenClawGatewayRunOptions): Promise<OpenClawGatewayRunResult>;
@@ -0,0 +1,2 @@
1
+ export { execute } from "./execute";
2
+ export { testEnvironment } from "./test";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Parse OpenClaw gateway JSON payloads for session usage (tokens, model, cost).
3
+ * Shapes follow `sessions.list` / `sessions.usage` responses on the gateway WebSocket API.
4
+ */
5
+ export type ParsedOpenClawSessionUsage = {
6
+ tokenInput: number;
7
+ tokenOutput: number;
8
+ cachedInputTokens: number;
9
+ usdCost: number;
10
+ model: string | null;
11
+ modelProvider: string | null;
12
+ source: "sessions.list" | "sessions.usage";
13
+ };
14
+ /** Match `GatewaySessionRow` from OpenClaw `sessions.list` (search + exact key). */
15
+ export declare function extractUsageFromSessionsList(payload: unknown, exactSessionKey: string): ParsedOpenClawSessionUsage | null;
16
+ /** Match `SessionUsageEntry` from OpenClaw `sessions.usage` when `key` is scoped. */
17
+ export declare function extractUsageFromSessionsUsage(payload: unknown, exactSessionKey: string): ParsedOpenClawSessionUsage | null;
18
+ /**
19
+ * Map OpenClaw session `modelProvider` strings to Bopo `pricingProviderType` when catalog pricing should apply.
20
+ * Returns `null` when unknown so heartbeat cost logic falls back to gateway-reported USD or `openclaw_gateway` defaults.
21
+ */
22
+ export declare function mapOpenClawModelProviderToBopoPricingType(openclawProvider: string | null | undefined): string | null;
@@ -0,0 +1,2 @@
1
+ import type { AdapterEnvironmentResult, AgentRuntimeConfig } from "../../../../agent-sdk/src/types";
2
+ export declare function testEnvironment(runtime?: AgentRuntimeConfig): Promise<AdapterEnvironmentResult>;
@@ -0,0 +1,3 @@
1
+ export declare function buildAdapterConfig(values: Record<string, unknown>): {
2
+ [x: string]: unknown;
3
+ };
@@ -0,0 +1,2 @@
1
+ export { parseStdoutLine } from "./parse-stdout";
2
+ export { buildAdapterConfig } from "./build-config";
@@ -0,0 +1,6 @@
1
+ export declare function parseStdoutLine(line: string, timestampIso: string): {
2
+ kind: string;
3
+ label: string;
4
+ text: string;
5
+ timestampIso: string;
6
+ }[];
@@ -1,7 +1,11 @@
1
1
  import type { AdapterMetadata, AdapterModule } from "../../../agent-sdk/src/types";
2
2
  export declare const type: "opencode";
3
3
  export declare const label = "OpenCode";
4
- export declare const models: readonly [];
4
+ /** Shown when `opencode models` is empty (e.g. CLI missing on the API host). Discovery merges on top of this. */
5
+ export declare const models: readonly [{
6
+ readonly id: "opencode/big-pickle";
7
+ readonly label: "Big Pickle";
8
+ }];
5
9
  export declare const agentConfigurationDoc = "Use when:\n- You need OpenCode CLI execution with provider/model selection.\n- You want OpenCode session reuse.\n\nDo not use when:\n- No runtime model is configured.\n- The host cannot run OpenCode CLI.";
6
10
  export declare const metadata: AdapterMetadata;
7
11
  export declare const opencodeAdapterModule: AdapterModule;
@@ -0,0 +1,4 @@
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;
@@ -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;
@@ -75,6 +79,15 @@ export interface HeartbeatContext {
75
79
  commentBody?: string | null;
76
80
  issueIds?: string[];
77
81
  };
82
+ /** Company agents (non-terminated), sorted by name; used for delegation and task routing hints. */
83
+ teamRoster?: Array<{
84
+ id: string;
85
+ name: string;
86
+ role: string;
87
+ title?: string | null;
88
+ capabilities?: string | null;
89
+ status: string;
90
+ }>;
78
91
  }
79
92
  /**
80
93
  * Normalized usage contract produced by adapter execution.
@@ -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";
@@ -400,6 +405,7 @@ export declare const TemplateManifestAgentSchema: z.ZodObject<{
400
405
  general: "general";
401
406
  }>>;
402
407
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
408
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
403
409
  name: z.ZodString;
404
410
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
405
411
  claude_code: "claude_code";
@@ -409,6 +415,7 @@ export declare const TemplateManifestAgentSchema: z.ZodObject<{
409
415
  gemini_cli: "gemini_cli";
410
416
  openai_api: "openai_api";
411
417
  anthropic_api: "anthropic_api";
418
+ openclaw_gateway: "openclaw_gateway";
412
419
  http: "http";
413
420
  shell: "shell";
414
421
  }>>>;
@@ -527,6 +534,7 @@ export declare const TemplateManifestSchema: z.ZodObject<{
527
534
  general: "general";
528
535
  }>>;
529
536
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
537
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
530
538
  name: z.ZodString;
531
539
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
532
540
  claude_code: "claude_code";
@@ -536,6 +544,7 @@ export declare const TemplateManifestSchema: z.ZodObject<{
536
544
  gemini_cli: "gemini_cli";
537
545
  openai_api: "openai_api";
538
546
  anthropic_api: "anthropic_api";
547
+ openclaw_gateway: "openclaw_gateway";
539
548
  http: "http";
540
549
  shell: "shell";
541
550
  }>>>;
@@ -687,6 +696,7 @@ export declare const TemplateSchema: z.ZodObject<{
687
696
  general: "general";
688
697
  }>>;
689
698
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
699
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
690
700
  name: z.ZodString;
691
701
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
692
702
  claude_code: "claude_code";
@@ -696,6 +706,7 @@ export declare const TemplateSchema: z.ZodObject<{
696
706
  gemini_cli: "gemini_cli";
697
707
  openai_api: "openai_api";
698
708
  anthropic_api: "anthropic_api";
709
+ openclaw_gateway: "openclaw_gateway";
699
710
  http: "http";
700
711
  shell: "shell";
701
712
  }>>>;
@@ -847,6 +858,7 @@ export declare const TemplateCreateRequestSchema: z.ZodObject<{
847
858
  general: "general";
848
859
  }>>;
849
860
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
861
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
850
862
  name: z.ZodString;
851
863
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
852
864
  claude_code: "claude_code";
@@ -856,6 +868,7 @@ export declare const TemplateCreateRequestSchema: z.ZodObject<{
856
868
  gemini_cli: "gemini_cli";
857
869
  openai_api: "openai_api";
858
870
  anthropic_api: "anthropic_api";
871
+ openclaw_gateway: "openclaw_gateway";
859
872
  http: "http";
860
873
  shell: "shell";
861
874
  }>>>;
@@ -1005,6 +1018,7 @@ export declare const TemplateUpdateRequestSchema: z.ZodObject<{
1005
1018
  general: "general";
1006
1019
  }>>;
1007
1020
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1021
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1008
1022
  name: z.ZodString;
1009
1023
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
1010
1024
  claude_code: "claude_code";
@@ -1014,6 +1028,7 @@ export declare const TemplateUpdateRequestSchema: z.ZodObject<{
1014
1028
  gemini_cli: "gemini_cli";
1015
1029
  openai_api: "openai_api";
1016
1030
  anthropic_api: "anthropic_api";
1031
+ openclaw_gateway: "openclaw_gateway";
1017
1032
  http: "http";
1018
1033
  shell: "shell";
1019
1034
  }>>>;
@@ -1214,6 +1229,7 @@ export declare const TemplateExportSchema: z.ZodObject<{
1214
1229
  general: "general";
1215
1230
  }>>;
1216
1231
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1232
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1217
1233
  name: z.ZodString;
1218
1234
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
1219
1235
  claude_code: "claude_code";
@@ -1223,6 +1239,7 @@ export declare const TemplateExportSchema: z.ZodObject<{
1223
1239
  gemini_cli: "gemini_cli";
1224
1240
  openai_api: "openai_api";
1225
1241
  anthropic_api: "anthropic_api";
1242
+ openclaw_gateway: "openclaw_gateway";
1226
1243
  http: "http";
1227
1244
  shell: "shell";
1228
1245
  }>>>;
@@ -1376,6 +1393,7 @@ export declare const TemplateImportRequestSchema: z.ZodObject<{
1376
1393
  general: "general";
1377
1394
  }>>;
1378
1395
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1396
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1379
1397
  name: z.ZodString;
1380
1398
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
1381
1399
  claude_code: "claude_code";
@@ -1385,6 +1403,7 @@ export declare const TemplateImportRequestSchema: z.ZodObject<{
1385
1403
  gemini_cli: "gemini_cli";
1386
1404
  openai_api: "openai_api";
1387
1405
  anthropic_api: "anthropic_api";
1406
+ openclaw_gateway: "openclaw_gateway";
1388
1407
  http: "http";
1389
1408
  shell: "shell";
1390
1409
  }>>>;
@@ -1515,6 +1534,7 @@ export declare const TemplateVersionSchema: z.ZodObject<{
1515
1534
  general: "general";
1516
1535
  }>>;
1517
1536
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1537
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1518
1538
  name: z.ZodString;
1519
1539
  providerType: z.ZodDefault<z.ZodLazy<z.ZodEnum<{
1520
1540
  claude_code: "claude_code";
@@ -1524,6 +1544,7 @@ export declare const TemplateVersionSchema: z.ZodObject<{
1524
1544
  gemini_cli: "gemini_cli";
1525
1545
  openai_api: "openai_api";
1526
1546
  anthropic_api: "anthropic_api";
1547
+ openclaw_gateway: "openclaw_gateway";
1527
1548
  http: "http";
1528
1549
  shell: "shell";
1529
1550
  }>>>;
@@ -1629,6 +1650,7 @@ export declare const ProviderTypeSchema: z.ZodEnum<{
1629
1650
  gemini_cli: "gemini_cli";
1630
1651
  openai_api: "openai_api";
1631
1652
  anthropic_api: "anthropic_api";
1653
+ openclaw_gateway: "openclaw_gateway";
1632
1654
  http: "http";
1633
1655
  shell: "shell";
1634
1656
  }>;
@@ -1827,6 +1849,7 @@ export declare const RunManagerReportSchema: z.ZodObject<{
1827
1849
  gemini_cli: "gemini_cli";
1828
1850
  openai_api: "openai_api";
1829
1851
  anthropic_api: "anthropic_api";
1852
+ openclaw_gateway: "openclaw_gateway";
1830
1853
  http: "http";
1831
1854
  shell: "shell";
1832
1855
  }>;
@@ -1904,6 +1927,7 @@ export declare const RunCompletionReportSchema: z.ZodObject<{
1904
1927
  gemini_cli: "gemini_cli";
1905
1928
  openai_api: "openai_api";
1906
1929
  anthropic_api: "anthropic_api";
1930
+ openclaw_gateway: "openclaw_gateway";
1907
1931
  http: "http";
1908
1932
  shell: "shell";
1909
1933
  }>;
@@ -2160,6 +2184,7 @@ export declare const AgentCreateRequestSchema: z.ZodObject<{
2160
2184
  general: "general";
2161
2185
  }>>;
2162
2186
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2187
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2163
2188
  name: z.ZodString;
2164
2189
  providerType: z.ZodEnum<{
2165
2190
  claude_code: "claude_code";
@@ -2169,6 +2194,7 @@ export declare const AgentCreateRequestSchema: z.ZodObject<{
2169
2194
  gemini_cli: "gemini_cli";
2170
2195
  openai_api: "openai_api";
2171
2196
  anthropic_api: "anthropic_api";
2197
+ openclaw_gateway: "openclaw_gateway";
2172
2198
  http: "http";
2173
2199
  shell: "shell";
2174
2200
  }>;
@@ -2204,10 +2230,12 @@ export declare const AgentCreateRequestSchema: z.ZodObject<{
2204
2230
  gemini_cli: "gemini_cli";
2205
2231
  openai_api: "openai_api";
2206
2232
  anthropic_api: "anthropic_api";
2233
+ openclaw_gateway: "openclaw_gateway";
2207
2234
  http: "http";
2208
2235
  shell: "shell";
2209
2236
  }>>>;
2210
2237
  requestedRuntimeModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2238
+ requestedCapabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2211
2239
  }, z.core.$strip>>;
2212
2240
  requestApproval: z.ZodDefault<z.ZodBoolean>;
2213
2241
  runtimeConfig: z.ZodDefault<z.ZodObject<{
@@ -2252,6 +2280,7 @@ export declare const AgentUpdateRequestSchema: z.ZodObject<{
2252
2280
  general: "general";
2253
2281
  }>>>;
2254
2282
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2283
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2255
2284
  name: z.ZodOptional<z.ZodString>;
2256
2285
  providerType: z.ZodOptional<z.ZodEnum<{
2257
2286
  claude_code: "claude_code";
@@ -2261,6 +2290,7 @@ export declare const AgentUpdateRequestSchema: z.ZodObject<{
2261
2290
  gemini_cli: "gemini_cli";
2262
2291
  openai_api: "openai_api";
2263
2292
  anthropic_api: "anthropic_api";
2293
+ openclaw_gateway: "openclaw_gateway";
2264
2294
  http: "http";
2265
2295
  shell: "shell";
2266
2296
  }>>;
@@ -2317,6 +2347,7 @@ export declare const AgentSchema: z.ZodObject<{
2317
2347
  general: "general";
2318
2348
  }>>>;
2319
2349
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2350
+ capabilities: z.ZodOptional<z.ZodNullable<z.ZodString>>;
2320
2351
  name: z.ZodString;
2321
2352
  providerType: z.ZodEnum<{
2322
2353
  claude_code: "claude_code";
@@ -2326,6 +2357,7 @@ export declare const AgentSchema: z.ZodObject<{
2326
2357
  gemini_cli: "gemini_cli";
2327
2358
  openai_api: "openai_api";
2328
2359
  anthropic_api: "anthropic_api";
2360
+ openclaw_gateway: "openclaw_gateway";
2329
2361
  http: "http";
2330
2362
  shell: "shell";
2331
2363
  }>;
@@ -2823,6 +2855,7 @@ export declare const OfficeOccupantSchema: z.ZodObject<{
2823
2855
  gemini_cli: "gemini_cli";
2824
2856
  openai_api: "openai_api";
2825
2857
  anthropic_api: "anthropic_api";
2858
+ openclaw_gateway: "openclaw_gateway";
2826
2859
  http: "http";
2827
2860
  shell: "shell";
2828
2861
  }>>;
@@ -2870,6 +2903,7 @@ export declare const OfficeSpaceEventSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
2870
2903
  gemini_cli: "gemini_cli";
2871
2904
  openai_api: "openai_api";
2872
2905
  anthropic_api: "anthropic_api";
2906
+ openclaw_gateway: "openclaw_gateway";
2873
2907
  http: "http";
2874
2908
  shell: "shell";
2875
2909
  }>>;
@@ -2916,6 +2950,7 @@ export declare const OfficeSpaceEventSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
2916
2950
  gemini_cli: "gemini_cli";
2917
2951
  openai_api: "openai_api";
2918
2952
  anthropic_api: "anthropic_api";
2953
+ openclaw_gateway: "openclaw_gateway";
2919
2954
  http: "http";
2920
2955
  shell: "shell";
2921
2956
  }>>;
@@ -3209,6 +3244,7 @@ export declare const RealtimeEventEnvelopeSchema: z.ZodDiscriminatedUnion<[z.Zod
3209
3244
  gemini_cli: "gemini_cli";
3210
3245
  openai_api: "openai_api";
3211
3246
  anthropic_api: "anthropic_api";
3247
+ openclaw_gateway: "openclaw_gateway";
3212
3248
  http: "http";
3213
3249
  shell: "shell";
3214
3250
  }>>;
@@ -3255,6 +3291,7 @@ export declare const RealtimeEventEnvelopeSchema: z.ZodDiscriminatedUnion<[z.Zod
3255
3291
  gemini_cli: "gemini_cli";
3256
3292
  openai_api: "openai_api";
3257
3293
  anthropic_api: "anthropic_api";
3294
+ openclaw_gateway: "openclaw_gateway";
3258
3295
  http: "http";
3259
3296
  shell: "shell";
3260
3297
  }>>;
@@ -3636,6 +3673,7 @@ export declare const RealtimeEventMessageSchema: z.ZodDiscriminatedUnion<[z.ZodO
3636
3673
  gemini_cli: "gemini_cli";
3637
3674
  openai_api: "openai_api";
3638
3675
  anthropic_api: "anthropic_api";
3676
+ openclaw_gateway: "openclaw_gateway";
3639
3677
  http: "http";
3640
3678
  shell: "shell";
3641
3679
  }>>;
@@ -3682,6 +3720,7 @@ export declare const RealtimeEventMessageSchema: z.ZodDiscriminatedUnion<[z.ZodO
3682
3720
  gemini_cli: "gemini_cli";
3683
3721
  openai_api: "openai_api";
3684
3722
  anthropic_api: "anthropic_api";
3723
+ openclaw_gateway: "openclaw_gateway";
3685
3724
  http: "http";
3686
3725
  shell: "shell";
3687
3726
  }>>;
@@ -4065,6 +4104,7 @@ export declare const RealtimeMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
4065
4104
  gemini_cli: "gemini_cli";
4066
4105
  openai_api: "openai_api";
4067
4106
  anthropic_api: "anthropic_api";
4107
+ openclaw_gateway: "openclaw_gateway";
4068
4108
  http: "http";
4069
4109
  shell: "shell";
4070
4110
  }>>;
@@ -4111,6 +4151,7 @@ export declare const RealtimeMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
4111
4151
  gemini_cli: "gemini_cli";
4112
4152
  openai_api: "openai_api";
4113
4153
  anthropic_api: "anthropic_api";
4154
+ openclaw_gateway: "openclaw_gateway";
4114
4155
  http: "http";
4115
4156
  shell: "shell";
4116
4157
  }>>;
@@ -4381,6 +4422,7 @@ export declare const CostLedgerEntrySchema: z.ZodObject<{
4381
4422
  gemini_cli: "gemini_cli";
4382
4423
  openai_api: "openai_api";
4383
4424
  anthropic_api: "anthropic_api";
4425
+ openclaw_gateway: "openclaw_gateway";
4384
4426
  http: "http";
4385
4427
  shell: "shell";
4386
4428
  }>;
@@ -4663,6 +4705,7 @@ export declare const HeartbeatRunDetailSchema: z.ZodObject<{
4663
4705
  gemini_cli: "gemini_cli";
4664
4706
  openai_api: "openai_api";
4665
4707
  anthropic_api: "anthropic_api";
4708
+ openclaw_gateway: "openclaw_gateway";
4666
4709
  http: "http";
4667
4710
  shell: "shell";
4668
4711
  }>;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "bopodev-agent-sdk",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
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.28"
9
+ "bopodev-contracts": "0.1.30"
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;
@@ -656,6 +686,15 @@ const staticMetadata: AdapterMetadata[] = [
656
686
  supportsThinkingEffort: false,
657
687
  requiresRuntimeCwd: false
658
688
  },
689
+ {
690
+ providerType: "openclaw_gateway",
691
+ label: "OpenClaw Gateway",
692
+ supportsModelSelection: false,
693
+ supportsEnvironmentTest: true,
694
+ supportsWebSearch: false,
695
+ supportsThinkingEffort: false,
696
+ requiresRuntimeCwd: false
697
+ },
659
698
  {
660
699
  providerType: "http",
661
700
  label: "HTTP",
@@ -678,7 +717,7 @@ const staticMetadata: AdapterMetadata[] = [
678
717
 
679
718
  const metadataByProviderType = new Map(staticMetadata.map((entry) => [entry.providerType, entry] as const));
680
719
 
681
- const modelCatalog: Record<Exclude<AgentProviderType, "http" | "shell">, AdapterModelOption[]> = {
720
+ const modelCatalog: Record<Exclude<AgentProviderType, "http" | "shell" | "openclaw_gateway">, AdapterModelOption[]> = {
682
721
  codex: [
683
722
  { id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
684
723
  { id: "gpt-5.4", label: "GPT-5.4" },
@@ -741,7 +780,7 @@ export async function listAdapterModels(
741
780
  providerType: AgentProviderType,
742
781
  runtime?: AgentRuntimeConfig
743
782
  ): Promise<AdapterModelOption[]> {
744
- if (providerType === "http" || providerType === "shell") {
783
+ if (providerType === "http" || providerType === "shell" || providerType === "openclaw_gateway") {
745
784
  return [];
746
785
  }
747
786
  if (providerType === "cursor") {
@@ -870,7 +909,10 @@ export async function testAdapterEnvironment(
870
909
 
871
910
  function detectProviderCommandMismatch(providerType: AgentProviderType, command: string) {
872
911
  const normalized = basename(command).toLowerCase();
873
- const known: Record<Exclude<AgentProviderType, "http" | "shell" | "openai_api" | "anthropic_api">, string[]> = {
912
+ const known: Record<
913
+ Exclude<AgentProviderType, "http" | "shell" | "openai_api" | "anthropic_api" | "openclaw_gateway">,
914
+ string[]
915
+ > = {
874
916
  claude_code: ["claude", "claude.exe", "claude.cmd"],
875
917
  codex: ["codex", "codex.exe", "codex.cmd"],
876
918
  cursor: ["cursor", "cursor.exe", "cursor.cmd"],
@@ -1850,7 +1892,9 @@ export function toProviderResult(
1850
1892
  nextState: applyProviderSessionState(context, provider, sessionUpdate)
1851
1893
  };
1852
1894
  }
1853
- if (!runtime.finalRunOutput) {
1895
+ const resolvedFinalRunOutput =
1896
+ runtime.finalRunOutput ?? synthesizeStreamFinalRunOutput(provider, runtime.parsedUsage);
1897
+ if (!resolvedFinalRunOutput) {
1854
1898
  const usage = toNormalizedUsage(runtime.parsedUsage);
1855
1899
  const detail = resolveFinalRunOutputContractDetail({ provider, runtime });
1856
1900
  return createContractInvalidResult({
@@ -1895,7 +1939,7 @@ export function toProviderResult(
1895
1939
  tokenInput: usageTokenInputTotal(runtime.parsedUsage),
1896
1940
  tokenOutput,
1897
1941
  usdCost,
1898
- finalRunOutput: runtime.finalRunOutput,
1942
+ finalRunOutput: resolvedFinalRunOutput,
1899
1943
  usage,
1900
1944
  pricingProviderType,
1901
1945
  pricingModelId,
@@ -2646,9 +2690,65 @@ function clipPromptText(text: string, max: number | null): string {
2646
2690
  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
2691
  {"employee_comment":"markdown update to the manager","results":["short concrete outcome"],"errors":[],"artifacts":[{"kind":"file","path":"relative/path"}]}`;
2648
2692
 
2693
+ /** Injected every heartbeat when the runtime sets BOPODEV_AGENT_OPERATING_DIR (no need to store paths in bootstrapPrompt). */
2694
+ function buildOperatingDirectoryPromptPrefix(context: HeartbeatContext): string {
2695
+ const dir = context.runtime?.env?.BOPODEV_AGENT_OPERATING_DIR?.trim();
2696
+ if (!dir) {
2697
+ return "";
2698
+ }
2699
+ return [
2700
+ "Operating context:",
2701
+ `- Your agent operating directory (read AGENTS.md, HEARTBEAT.md, SOUL.md, TOOLS.md here when they exist): ${dir}`,
2702
+ ""
2703
+ ].join("\n");
2704
+ }
2705
+
2706
+ function composeBootstrapPreamble(context: HeartbeatContext): string {
2707
+ const operating = buildOperatingDirectoryPromptPrefix(context);
2708
+ const user = context.runtime?.bootstrapPrompt?.trim();
2709
+ if (!user) {
2710
+ return operating;
2711
+ }
2712
+ return operating ? `${operating}${user}\n\n` : `${user}\n\n`;
2713
+ }
2714
+
2715
+ function formatTeamRosterSection(context: HeartbeatContext): string {
2716
+ const roster = context.teamRoster;
2717
+ if (!roster?.length) {
2718
+ return "";
2719
+ }
2720
+ const mode = resolveHeartbeatPromptModeForPrompt(context);
2721
+ const isCompact = mode === "compact";
2722
+ const clipCapabilities = (raw: string | null | undefined) => {
2723
+ const t = raw?.trim();
2724
+ if (!t) {
2725
+ return null;
2726
+ }
2727
+ if (isCompact && t.length > 200) {
2728
+ return `${t.slice(0, 200)}…`;
2729
+ }
2730
+ return t;
2731
+ };
2732
+ const lines = roster.map((member) => {
2733
+ const caps = clipCapabilities(member.capabilities);
2734
+ const parts = [member.id, member.name, member.role];
2735
+ const titled = member.title?.trim();
2736
+ if (titled) {
2737
+ parts.push(`title: ${titled}`);
2738
+ }
2739
+ if (caps) {
2740
+ parts.push(`capabilities: ${caps}`);
2741
+ }
2742
+ const you = member.id === context.agentId ? " (you)" : "";
2743
+ return `- ${parts.join(" | ")}${you}`;
2744
+ });
2745
+ return ["Team roster (for delegation):", ...lines, ""].join("\n");
2746
+ }
2747
+
2649
2748
  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.
2749
+ const preamble = composeBootstrapPreamble(context);
2750
+ const rosterBlock = formatTeamRosterSection(context);
2751
+ return `${preamble}${rosterBlock}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
2752
 
2653
2753
  ${HEARTBEAT_JSON_SCHEMA_FOOTER}
2654
2754
  `;
@@ -2673,7 +2773,7 @@ export function createPrompt(context: HeartbeatContext) {
2673
2773
  if (context.idleMicroPrompt && context.workItems.length === 0 && !isCommentOrderRunEarly) {
2674
2774
  return buildIdleMicroPrompt(context);
2675
2775
  }
2676
- const bootstrapPrompt = context.runtime?.bootstrapPrompt?.trim();
2776
+ const bootstrapPreamble = composeBootstrapPreamble(context);
2677
2777
  const promptMode = resolveHeartbeatPromptModeForPrompt(context);
2678
2778
  const isCompact = promptMode === "compact";
2679
2779
  const memoryCap = resolveMemorySectionMaxChars(promptMode);
@@ -2696,6 +2796,11 @@ export function createPrompt(context: HeartbeatContext) {
2696
2796
  `- [${item.issueId}] ${item.title}`,
2697
2797
  ` Project: ${item.projectName ?? item.projectId}`,
2698
2798
  item.parentIssueId ? ` Parent issue: ${item.parentIssueId}` : null,
2799
+ ...(item.goalAncestryChains && item.goalAncestryChains.length > 0
2800
+ ? item.goalAncestryChains.map(
2801
+ (chain, idx) => ` Linked goal ${idx + 1} (root → leaf): ${chain.join(" → ")}`
2802
+ )
2803
+ : []),
2699
2804
  item.childIssueIds?.length ? ` Sub-issues: ${item.childIssueIds.join(", ")}` : null,
2700
2805
  item.status ? ` Status: ${item.status}` : null,
2701
2806
  item.priority ? ` Priority: ${item.priority}` : null,
@@ -2785,6 +2890,8 @@ export function createPrompt(context: HeartbeatContext) {
2785
2890
  : "- BOPODEV_REQUEST_HEADERS_JSON is missing. Report this as blocker."
2786
2891
  ].join("\n");
2787
2892
 
2893
+ const teamRosterBlock = formatTeamRosterSection(context);
2894
+
2788
2895
  const executionDirectives = [
2789
2896
  "Execution directives:",
2790
2897
  "- You are running inside a BopoDev heartbeat for local repository work.",
@@ -2819,14 +2926,14 @@ export function createPrompt(context: HeartbeatContext) {
2819
2926
  "- Do not invent token or cost values; the runtime records usage separately."
2820
2927
  ].join("\n");
2821
2928
 
2822
- return `${bootstrapPrompt ? `${bootstrapPrompt}\n\n` : ""}You are ${context.agent.name} (${context.agent.role}), agent ${context.agentId}.
2929
+ return `${bootstrapPreamble}You are ${context.agent.name} (${context.agent.role}), agent ${context.agentId}.
2823
2930
  Heartbeat run ${context.heartbeatRunId}.
2824
2931
  ${isCompact ? "Prompt profile: compact (issue bodies are not inlined—use GET /issues/:id to hydrate).\n" : ""}
2825
2932
  Company:
2826
2933
  - Name: ${context.company.name}
2827
2934
  - Mission: ${context.company.mission ?? "No mission set"}
2828
2935
 
2829
- Goal context:
2936
+ ${teamRosterBlock}Goal context:
2830
2937
  Company goals:
2831
2938
  ${companyGoals}
2832
2939
  Project goals:
package/src/registry.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { dedupeModels } from "./adapters";
1
2
  import type {
2
3
  AdapterEnvironmentResult,
3
4
  AdapterMetadata,
@@ -16,6 +17,7 @@ import { openaiapiAdapterModule } from "../../adapters/openai-api/src";
16
17
  import { anthropicapiAdapterModule } from "../../adapters/anthropic-api/src";
17
18
  import { httpAdapterModule } from "../../adapters/http/src";
18
19
  import { shellAdapterModule } from "../../adapters/shell/src";
20
+ import { openclawGatewayAdapterModule } from "../../adapters/openclaw-gateway/src";
19
21
 
20
22
  const adapterModules: Record<AgentProviderType, AdapterModule> = {
21
23
  claude_code: claudecodeAdapterModule,
@@ -25,6 +27,7 @@ const adapterModules: Record<AgentProviderType, AdapterModule> = {
25
27
  gemini_cli: geminiCliAdapterModule,
26
28
  openai_api: openaiapiAdapterModule,
27
29
  anthropic_api: anthropicapiAdapterModule,
30
+ openclaw_gateway: openclawGatewayAdapterModule,
28
31
  http: httpAdapterModule,
29
32
  shell: shellAdapterModule
30
33
  };
@@ -62,6 +65,10 @@ const adapters: Record<AgentProviderType, AgentAdapter> = {
62
65
  providerType: "anthropic_api",
63
66
  execute: (context) => adapterModules.anthropic_api.server.execute(context)
64
67
  },
68
+ openclaw_gateway: {
69
+ providerType: "openclaw_gateway",
70
+ execute: (context) => adapterModules.openclaw_gateway.server.execute(context)
71
+ },
65
72
  http: {
66
73
  providerType: "http",
67
74
  execute: (context) => adapterModules.http.server.execute(context)
@@ -80,11 +87,19 @@ export async function getAdapterModels(
80
87
  providerType: AgentProviderType,
81
88
  runtime?: AgentRuntimeConfig
82
89
  ): Promise<AdapterModelOption[]> {
83
- const fromModule = await adapterModules[providerType].server.listModels?.(runtime);
84
- if (fromModule) {
85
- return fromModule;
90
+ const mod = adapterModules[providerType];
91
+ const staticModels: AdapterModelOption[] = mod.models ? [...mod.models] : [];
92
+ const listModels = mod.server.listModels;
93
+ if (!listModels) {
94
+ return staticModels;
95
+ }
96
+ const discovered = await listModels(runtime);
97
+ const disc = Array.isArray(discovered) ? discovered : [];
98
+ if (disc.length > 0) {
99
+ return dedupeModels([...disc, ...staticModels]);
86
100
  }
87
- return adapterModules[providerType].models ? [...adapterModules[providerType].models] : [];
101
+ // Empty discovery (CLI missing, auth, timeout): keep static catalog so UIs are not stuck on client-only allowlists.
102
+ return staticModels.length > 0 ? staticModels : disc;
88
103
  }
89
104
 
90
105
  export function getAdapterMetadata(): AdapterMetadata[] {
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;
@@ -81,6 +85,15 @@ export interface HeartbeatContext {
81
85
  commentBody?: string | null;
82
86
  issueIds?: string[];
83
87
  };
88
+ /** Company agents (non-terminated), sorted by name; used for delegation and task routing hints. */
89
+ teamRoster?: Array<{
90
+ id: string;
91
+ name: string;
92
+ role: string;
93
+ title?: string | null;
94
+ capabilities?: string | null;
95
+ status: string;
96
+ }>;
84
97
  }
85
98
 
86
99
  /**