@bunny-agent/sdk 0.9.62 → 0.10.0-beta.1

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/README.md CHANGED
@@ -333,7 +333,7 @@ const model = bunnyAgent("claude-sonnet-4-20250514");
333
333
 
334
334
  #### Daemon HTTP transport (same provider)
335
335
 
336
- **With any sandbox adapter** (E2B, Sandock, `LocalMachine`, etc.): pass **`sandbox` + `daemonUrl`**. The URL is resolved **inside** the sandbox (the `vikadata/bunny-agent` image starts `bunny-agent-daemon` on port 3080). The SDK streams via `streamCodingRunFromSandbox` (`curl -N` in the sandbox, including `LocalMachine`), not `fetch` from your server. It does **not** call `/healthz` for you — use `isBunnyAgentDaemonHealthy` from `@bunny-agent/sdk` when you want a probe before setting `daemonUrl` (e.g. to fall back to the CLI runner).
336
+ **With any sandbox adapter** (E2B, Sandock, `LocalMachine`, etc.): pass **`sandbox` + `daemonUrl`**. The URL is resolved **inside** the sandbox (the `vikadata/bunny-agent` image starts `bunny-agent-daemon` on port 3080). The SDK streams via `streamCodingRunFromSandbox` (`curl -N` in the sandbox, including `LocalMachine`), not `fetch` from your server. It does not call `/healthz` unless you select Pi search contract `"auto"`, as described below. Use `isBunnyAgentDaemonHealthy` from `@bunny-agent/sdk` for an explicit readiness probe before setting `daemonUrl` (e.g. to fall back to the CLI runner).
337
337
 
338
338
  ```typescript
339
339
  import { createBunnyAgent, DEFAULT_BUNNY_AGENT_DAEMON_URL } from "@bunny-agent/sdk";
@@ -348,6 +348,35 @@ const bunnyAgent = createBunnyAgent({
348
348
 
349
349
  Omit `daemonUrl` to use the **CLI runner** in the same sandbox. `createBunnyAgent` always requires a `sandbox` adapter.
350
350
 
351
+ #### Structured web search and answer citations
352
+
353
+ For Pi daemon execution, select `searchContractVersion: "auto"` on the provider
354
+ or an individual model. The SDK discovers the attached runtime's capabilities
355
+ before starting the turn:
356
+
357
+ ```typescript
358
+ const bunnyAgent = createBunnyAgent({
359
+ sandbox: mySandboxAdapter,
360
+ daemonUrl: DEFAULT_BUNNY_AGENT_DAEMON_URL,
361
+ runnerType: "pi",
362
+ searchContractVersion: "auto",
363
+ });
364
+ ```
365
+
366
+ - A capable daemon uses v1 structured search and answer annotations, with a
367
+ required stream acknowledgement. A healthy older daemon uses legacy search.
368
+ - Discovery sends a read-only `/healthz` GET through the same sandbox transport.
369
+ The turn executes once; it is never replayed to negotiate support. Failed or
370
+ malformed discovery rejects before execution instead of silently downgrading.
371
+ - Healthy results are cached for 30 seconds per attached handle, sandbox ID,
372
+ and daemon URL. An in-place runtime restart can take that long to be discovered.
373
+ - Omitting the option preserves legacy behavior without discovery. Explicit `1`
374
+ preserves strict acknowledgement requirements without discovery. CLI execution
375
+ and other runners remain legacy when `"auto"` is selected.
376
+
377
+ See the [search citation contract](../../docs/search-citation-contract.md) for
378
+ the capability schema, AI SDK requirements, and citation metadata fields.
379
+
351
380
  ### Exports
352
381
 
353
382
  | Entry Point | Exports |
@@ -0,0 +1,106 @@
1
+ // src/contracts.ts
2
+ import {
3
+ codePointLength,
4
+ codePointOffsetToUtf16Index,
5
+ enforceSourceLimit,
6
+ isAnnotationRangeValidForText,
7
+ isCitationBinding,
8
+ isSafeHttpUrl,
9
+ isSupportedSearchContractVersion,
10
+ isUrlCitationAnnotation,
11
+ isValidCitationContractMetadata as isValidCitationContractMetadata2,
12
+ isValidCodePointRange,
13
+ isValidWebSearchCallRecord,
14
+ isWebSearchAction,
15
+ isWebSearchError,
16
+ isWebSearchResultItem,
17
+ isWebSearchSource,
18
+ projectOutputText as projectOutputText2,
19
+ SEARCH_CONTRACT_LIMITS,
20
+ SEARCH_CONTRACT_VERSION_1,
21
+ sliceByCodePointRange,
22
+ utf16IndexToCodePointOffset,
23
+ utf16RangeToCodePointRange
24
+ } from "@bunny-agent/manager/contracts";
25
+
26
+ // src/ui-message-projection.ts
27
+ import {
28
+ isValidCitationContractMetadata,
29
+ projectOutputText
30
+ } from "@bunny-agent/manager/contracts";
31
+ function isSearchContractCapabilities(value) {
32
+ if (value === null || typeof value !== "object") return false;
33
+ const v = value;
34
+ return v.version === 1 && typeof v.structured_retrieval === "boolean" && typeof v.native_annotations === "boolean" && typeof v.explicit_source_marker_annotations === "boolean";
35
+ }
36
+ function record(value) {
37
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
38
+ }
39
+ function projectUIMessageOutputText(message) {
40
+ return message.parts.flatMap((value) => {
41
+ const part = record(value);
42
+ if (part?.type !== "text" || typeof part.text !== "string") return [];
43
+ const bunny = record(record(part.providerMetadata)?.["bunny-agent"]);
44
+ const identity = typeof bunny?.textPartId === "string" && bunny.textPartId.length > 0 ? bunny.textPartId : void 0;
45
+ const contract = record(bunny?.citation_contract);
46
+ const acknowledgement = isSearchContractCapabilities(bunny?.searchContract) ? bunny.searchContract : void 0;
47
+ if (acknowledgement && bunny?.citation_contract !== void 0 && contract?.schema_version === void 0) {
48
+ throw new TypeError(
49
+ "Invalid durable Bunny citation contract v1 text part"
50
+ );
51
+ }
52
+ if (contract?.schema_version === 1) {
53
+ if (!identity || !acknowledgement || !isValidCitationContractMetadata(contract, { text: part.text })) {
54
+ throw new TypeError(
55
+ "Invalid durable Bunny citation contract v1 text part"
56
+ );
57
+ }
58
+ return [
59
+ {
60
+ textPartId: identity,
61
+ output_text: projectOutputText(part.text, contract.annotations),
62
+ bindings: contract.bindings.map((binding) => ({
63
+ ...binding,
64
+ tool_call_ids: [...binding.tool_call_ids]
65
+ })),
66
+ searchContract: { ...acknowledgement }
67
+ }
68
+ ];
69
+ }
70
+ return [
71
+ {
72
+ ...identity ? { textPartId: identity } : {},
73
+ output_text: projectOutputText(part.text, []),
74
+ bindings: [],
75
+ ...acknowledgement ? { searchContract: { ...acknowledgement } } : {}
76
+ }
77
+ ];
78
+ });
79
+ }
80
+
81
+ export {
82
+ isSearchContractCapabilities,
83
+ projectUIMessageOutputText,
84
+ codePointLength,
85
+ codePointOffsetToUtf16Index,
86
+ enforceSourceLimit,
87
+ isAnnotationRangeValidForText,
88
+ isCitationBinding,
89
+ isSafeHttpUrl,
90
+ isSupportedSearchContractVersion,
91
+ isUrlCitationAnnotation,
92
+ isValidCitationContractMetadata2 as isValidCitationContractMetadata,
93
+ isValidCodePointRange,
94
+ isValidWebSearchCallRecord,
95
+ isWebSearchAction,
96
+ isWebSearchError,
97
+ isWebSearchResultItem,
98
+ isWebSearchSource,
99
+ projectOutputText2 as projectOutputText,
100
+ SEARCH_CONTRACT_LIMITS,
101
+ SEARCH_CONTRACT_VERSION_1,
102
+ sliceByCodePointRange,
103
+ utf16IndexToCodePointOffset,
104
+ utf16RangeToCodePointRange
105
+ };
106
+ //# sourceMappingURL=chunk-WQI667GT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contracts.ts","../src/ui-message-projection.ts"],"sourcesContent":["/**\n * Browser-safe re-export of the web search / answer citation contract\n * types and helpers from `@bunny-agent/manager/contracts`, published as\n * `@bunny-agent/sdk/contracts` for product (Buda Cloud/OpenBuda) consumers.\n *\n * This file intentionally re-exports only the browser-safe manager barrel —\n * never `@bunny-agent/manager/contracts/source-id`, which uses `node:crypto`\n * and must stay server-only.\n */\nexport type {\n CitationBinding,\n CitationContractMetadata,\n CitationDiagnostic,\n CitationOffsetUnit,\n CitationOrigin,\n CitationProcessingState,\n CitationValidationContext,\n OutputTextContent,\n SearchContractCapabilities,\n SearchContractLimits,\n SearchContractVersion,\n SourceDetails,\n UrlCitationAnnotation,\n WebSearchAction,\n WebSearchCallRecord,\n WebSearchCallStatus,\n WebSearchError,\n WebSearchErrorCode,\n WebSearchResultItem,\n WebSearchSource,\n} from \"@bunny-agent/manager/contracts\";\nexport {\n codePointLength,\n codePointOffsetToUtf16Index,\n enforceSourceLimit,\n isAnnotationRangeValidForText,\n isCitationBinding,\n isSafeHttpUrl,\n isSupportedSearchContractVersion,\n isUrlCitationAnnotation,\n isValidCitationContractMetadata,\n isValidCodePointRange,\n isValidWebSearchCallRecord,\n isWebSearchAction,\n isWebSearchError,\n isWebSearchResultItem,\n isWebSearchSource,\n projectOutputText,\n SEARCH_CONTRACT_LIMITS,\n SEARCH_CONTRACT_VERSION_1,\n sliceByCodePointRange,\n utf16IndexToCodePointOffset,\n utf16RangeToCodePointRange,\n} from \"@bunny-agent/manager/contracts\";\nexport type { UIMessageOutputText } from \"./ui-message-projection\";\nexport {\n isSearchContractCapabilities,\n projectUIMessageOutputText,\n} from \"./ui-message-projection\";\n","import {\n type CitationBinding,\n isValidCitationContractMetadata,\n type OutputTextContent,\n projectOutputText,\n type SearchContractCapabilities,\n} from \"@bunny-agent/manager/contracts\";\n\n/** A canonical text projection with associations kept outside flat annotations. */\nexport interface UIMessageOutputText {\n textPartId?: string;\n output_text: OutputTextContent;\n bindings: CitationBinding[];\n searchContract?: SearchContractCapabilities;\n}\n\n/** Validate a request's effective acknowledgement without inferring capabilities. */\nexport function isSearchContractCapabilities(\n value: unknown,\n): value is SearchContractCapabilities {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as Record<string, unknown>;\n return (\n v.version === 1 &&\n typeof v.structured_retrieval === \"boolean\" &&\n typeof v.native_annotations === \"boolean\" &&\n typeof v.explicit_source_marker_annotations === \"boolean\"\n );\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/**\n * Project durable AI SDK UIMessage text parts in message order, without a\n * registry or Node dependencies. Identity lives in Bunny `textPartId` metadata.\n * Legacy/unknown-version parts yield plain text with empty annotations/bindings.\n * Recognized v1 citation metadata fails closed with TypeError if malformed,\n * out of bounds, missing durable identity, or lacking a valid acknowledgement.\n * Links, raw search snippets, and source cards never create annotations.\n */\nexport function projectUIMessageOutputText(message: {\n parts: readonly unknown[];\n}): UIMessageOutputText[] {\n return message.parts.flatMap((value): UIMessageOutputText[] => {\n const part = record(value);\n if (part?.type !== \"text\" || typeof part.text !== \"string\") return [];\n const bunny = record(record(part.providerMetadata)?.[\"bunny-agent\"]);\n const identity =\n typeof bunny?.textPartId === \"string\" && bunny.textPartId.length > 0\n ? bunny.textPartId\n : undefined;\n const contract = record(bunny?.citation_contract);\n const acknowledgement = isSearchContractCapabilities(bunny?.searchContract)\n ? bunny.searchContract\n : undefined;\n if (\n acknowledgement &&\n bunny?.citation_contract !== undefined &&\n contract?.schema_version === undefined\n ) {\n throw new TypeError(\n \"Invalid durable Bunny citation contract v1 text part\",\n );\n }\n if (contract?.schema_version === 1) {\n if (\n !identity ||\n !acknowledgement ||\n !isValidCitationContractMetadata(contract, { text: part.text })\n ) {\n throw new TypeError(\n \"Invalid durable Bunny citation contract v1 text part\",\n );\n }\n return [\n {\n textPartId: identity,\n output_text: projectOutputText(part.text, contract.annotations),\n bindings: contract.bindings.map((binding) => ({\n ...binding,\n tool_call_ids: [...binding.tool_call_ids],\n })),\n searchContract: { ...acknowledgement },\n },\n ];\n }\n return [\n {\n ...(identity ? { textPartId: identity } : {}),\n output_text: projectOutputText(part.text, []),\n bindings: [],\n ...(acknowledgement ? { searchContract: { ...acknowledgement } } : {}),\n },\n ];\n });\n}\n"],"mappings":";AA+BA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mCAAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACrDP;AAAA,EAEE;AAAA,EAEA;AAAA,OAEK;AAWA,SAAS,6BACd,OACqC;AACrC,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,QAAM,IAAI;AACV,SACE,EAAE,YAAY,KACd,OAAO,EAAE,yBAAyB,aAClC,OAAO,EAAE,uBAAuB,aAChC,OAAO,EAAE,uCAAuC;AAEpD;AAEA,SAAS,OAAO,OAAqD;AACnE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAUO,SAAS,2BAA2B,SAEjB;AACxB,SAAO,QAAQ,MAAM,QAAQ,CAAC,UAAiC;AAC7D,UAAM,OAAO,OAAO,KAAK;AACzB,QAAI,MAAM,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,QAAO,CAAC;AACpE,UAAM,QAAQ,OAAO,OAAO,KAAK,gBAAgB,IAAI,aAAa,CAAC;AACnE,UAAM,WACJ,OAAO,OAAO,eAAe,YAAY,MAAM,WAAW,SAAS,IAC/D,MAAM,aACN;AACN,UAAM,WAAW,OAAO,OAAO,iBAAiB;AAChD,UAAM,kBAAkB,6BAA6B,OAAO,cAAc,IACtE,MAAM,iBACN;AACJ,QACE,mBACA,OAAO,sBAAsB,UAC7B,UAAU,mBAAmB,QAC7B;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,UAAU,mBAAmB,GAAG;AAClC,UACE,CAAC,YACD,CAAC,mBACD,CAAC,gCAAgC,UAAU,EAAE,MAAM,KAAK,KAAK,CAAC,GAC9D;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,UACE,YAAY;AAAA,UACZ,aAAa,kBAAkB,KAAK,MAAM,SAAS,WAAW;AAAA,UAC9D,UAAU,SAAS,SAAS,IAAI,CAAC,aAAa;AAAA,YAC5C,GAAG;AAAA,YACH,eAAe,CAAC,GAAG,QAAQ,aAAa;AAAA,UAC1C,EAAE;AAAA,UACF,gBAAgB,EAAE,GAAG,gBAAgB;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,QACE,GAAI,WAAW,EAAE,YAAY,SAAS,IAAI,CAAC;AAAA,QAC3C,aAAa,kBAAkB,KAAK,MAAM,CAAC,CAAC;AAAA,QAC5C,UAAU,CAAC;AAAA,QACX,GAAI,kBAAkB,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,IAAI,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":["isValidCitationContractMetadata","projectOutputText"]}
@@ -0,0 +1,25 @@
1
+ import { OutputTextContent, CitationBinding, SearchContractCapabilities } from '@bunny-agent/manager/contracts';
2
+ export { CitationBinding, CitationContractMetadata, CitationDiagnostic, CitationOffsetUnit, CitationOrigin, CitationProcessingState, CitationValidationContext, OutputTextContent, SEARCH_CONTRACT_LIMITS, SEARCH_CONTRACT_VERSION_1, SearchContractCapabilities, SearchContractLimits, SearchContractVersion, SourceDetails, UrlCitationAnnotation, WebSearchAction, WebSearchCallRecord, WebSearchCallStatus, WebSearchError, WebSearchErrorCode, WebSearchResultItem, WebSearchSource, codePointLength, codePointOffsetToUtf16Index, enforceSourceLimit, isAnnotationRangeValidForText, isCitationBinding, isSafeHttpUrl, isSupportedSearchContractVersion, isUrlCitationAnnotation, isValidCitationContractMetadata, isValidCodePointRange, isValidWebSearchCallRecord, isWebSearchAction, isWebSearchError, isWebSearchResultItem, isWebSearchSource, projectOutputText, sliceByCodePointRange, utf16IndexToCodePointOffset, utf16RangeToCodePointRange } from '@bunny-agent/manager/contracts';
3
+
4
+ /** A canonical text projection with associations kept outside flat annotations. */
5
+ interface UIMessageOutputText {
6
+ textPartId?: string;
7
+ output_text: OutputTextContent;
8
+ bindings: CitationBinding[];
9
+ searchContract?: SearchContractCapabilities;
10
+ }
11
+ /** Validate a request's effective acknowledgement without inferring capabilities. */
12
+ declare function isSearchContractCapabilities(value: unknown): value is SearchContractCapabilities;
13
+ /**
14
+ * Project durable AI SDK UIMessage text parts in message order, without a
15
+ * registry or Node dependencies. Identity lives in Bunny `textPartId` metadata.
16
+ * Legacy/unknown-version parts yield plain text with empty annotations/bindings.
17
+ * Recognized v1 citation metadata fails closed with TypeError if malformed,
18
+ * out of bounds, missing durable identity, or lacking a valid acknowledgement.
19
+ * Links, raw search snippets, and source cards never create annotations.
20
+ */
21
+ declare function projectUIMessageOutputText(message: {
22
+ parts: readonly unknown[];
23
+ }): UIMessageOutputText[];
24
+
25
+ export { type UIMessageOutputText, isSearchContractCapabilities, projectUIMessageOutputText };
@@ -0,0 +1,51 @@
1
+ import {
2
+ SEARCH_CONTRACT_LIMITS,
3
+ SEARCH_CONTRACT_VERSION_1,
4
+ codePointLength,
5
+ codePointOffsetToUtf16Index,
6
+ enforceSourceLimit,
7
+ isAnnotationRangeValidForText,
8
+ isCitationBinding,
9
+ isSafeHttpUrl,
10
+ isSearchContractCapabilities,
11
+ isSupportedSearchContractVersion,
12
+ isUrlCitationAnnotation,
13
+ isValidCitationContractMetadata,
14
+ isValidCodePointRange,
15
+ isValidWebSearchCallRecord,
16
+ isWebSearchAction,
17
+ isWebSearchError,
18
+ isWebSearchResultItem,
19
+ isWebSearchSource,
20
+ projectOutputText,
21
+ projectUIMessageOutputText,
22
+ sliceByCodePointRange,
23
+ utf16IndexToCodePointOffset,
24
+ utf16RangeToCodePointRange
25
+ } from "./chunk-WQI667GT.js";
26
+ export {
27
+ SEARCH_CONTRACT_LIMITS,
28
+ SEARCH_CONTRACT_VERSION_1,
29
+ codePointLength,
30
+ codePointOffsetToUtf16Index,
31
+ enforceSourceLimit,
32
+ isAnnotationRangeValidForText,
33
+ isCitationBinding,
34
+ isSafeHttpUrl,
35
+ isSearchContractCapabilities,
36
+ isSupportedSearchContractVersion,
37
+ isUrlCitationAnnotation,
38
+ isValidCitationContractMetadata,
39
+ isValidCodePointRange,
40
+ isValidWebSearchCallRecord,
41
+ isWebSearchAction,
42
+ isWebSearchError,
43
+ isWebSearchResultItem,
44
+ isWebSearchSource,
45
+ projectOutputText,
46
+ projectUIMessageOutputText,
47
+ sliceByCodePointRange,
48
+ utf16IndexToCodePointOffset,
49
+ utf16RangeToCodePointRange
50
+ };
51
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.d.ts CHANGED
@@ -96,8 +96,8 @@ type BunnyAgentRunnerType = "claude" | "pi" | "codex" | "gemini" | "opencode";
96
96
  /**
97
97
  * AI Provider specific settings that extend BunnyAgentOptions.
98
98
  * Requires a `sandbox`. Optional `daemonUrl` streams via bunny-agent-daemon HTTP
99
- * (`POST /api/coding/run`) inside the sandbox. The SDK does **not** probe `/healthz`
100
- * on each request — call `isBunnyAgentDaemonHealthy` (re-exported from
99
+ * (`POST /api/coding/run`) inside the sandbox. Unless search contract `"auto"`
100
+ * is selected, the SDK does not probe `/healthz` — call `isBunnyAgentDaemonHealthy` (re-exported from
101
101
  * `@bunny-agent/sdk`) when you want to check readiness and omit `daemonUrl` to fall
102
102
  * back to the CLI runner, or set `daemonUrl` and handle HTTP errors yourself.
103
103
  */
@@ -107,7 +107,8 @@ interface BunnyAgentProviderSettings extends Omit<BunnyAgentOptions, "runner" |
107
107
  /**
108
108
  * bunny-agent-daemon base URL **inside** the sandbox (e.g. BunnyAgent image:
109
109
  * `http://127.0.0.1:3080`). When set, the SDK uses HTTP streaming only (no
110
- * automatic health probe or CLI fallback).
110
+ * automatic health probe or CLI fallback, except read-only capability discovery
111
+ * when `searchContractVersion: "auto"` is selected).
111
112
  */
112
113
  daemonUrl?: string;
113
114
  /**
@@ -163,6 +164,26 @@ interface BunnyAgentProviderSettings extends Omit<BunnyAgentOptions, "runner" |
163
164
  mcpConfig?: McpConfig;
164
165
  /** Reasoning effort / thinking level (e.g. "low", "medium", "high"). */
165
166
  effort?: string;
167
+ /**
168
+ * Explicit opt-in to the versioned web search / answer citation contract
169
+ * (see `@bunny-agent/manager/contracts`, re-exported browser-safe as
170
+ * `@bunny-agent/sdk/contracts`). Absent means legacy text-only search
171
+ * behavior, unchanged; `1` requests the structured retrieval envelope and
172
+ * Responses-shaped answer annotations. V1 requires a stable AI SDK 6 peer
173
+ * at >=6.0.264 <7.0.0 to preserve tool-result metadata; older peers are
174
+ * rejected before execution. Request-scoped, never a
175
+ * process-global switch. The SDK rejects the call (surfaced as a stream
176
+ * error / rejected `generateText`/`streamText` result) if the runner/daemon
177
+ * never acknowledges support for the requested version rather than
178
+ * silently falling back to legacy behavior or retrying automatically.
179
+ * `"auto"` discovers a Pi daemon's capabilities before coding execution and
180
+ * selects v1 only when advertised. An older healthy daemon without that
181
+ * advertisement uses legacy behavior. Other runners and CLI-only transports
182
+ * stay legacy in auto mode. Discovery failures reject instead of executing or
183
+ * downgrading. Successful discovery is cached per handle/sandbox ID/URL for
184
+ * 30 seconds; an in-place daemon restart can take that long to be rediscovered.
185
+ */
186
+ searchContractVersion?: 1 | "auto";
166
187
  /**
167
188
  * Optional subset of `env` whose keys are safe to expose to the pi runner's
168
189
  * bash tool. When omitted, the runner classifies `env` via a built-in
@@ -205,11 +226,40 @@ declare class BunnyAgentLanguageModel implements LanguageModelV3 {
205
226
  private legacyTextPartCounter;
206
227
  private receivedFinish;
207
228
  private activeReasoningPartId;
229
+ private acknowledgedSearchContract;
230
+ /** Defense in depth: the runner already dedupes per emitted source, but a
231
+ * duplicate `source-url` event must never reach the UI as two source parts. */
232
+ private emittedSourceIds;
233
+ private readonly completedTextIds;
234
+ private readonly completedToolIds;
235
+ private readonly textById;
236
+ private readonly textMetadata;
237
+ private readonly toolMetadata;
238
+ private readonly pendingSources;
239
+ private readonly citedSources;
240
+ private messageMetadata;
241
+ private readonly seenMessageMetadata;
242
+ private readonly seenStreamErrors;
243
+ /** Merge every provider namespace while retaining negotiated request facts. */
244
+ private partMetadata;
245
+ private toolPartMetadata;
246
+ private emitCitedSource;
208
247
  private logUnparsedStreamLine;
209
248
  constructor(modelOptions: BunnyAgentLanguageModelOptions);
210
249
  doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult>;
211
250
  doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult>;
212
- private resetStreamState;
251
+ private doStreamInvocation;
252
+ /** The `searchContractVersion` explicitly requested for this turn, if any (never a process-global default). */
253
+ private get requestedSearchContractVersion();
254
+ /**
255
+ * When an explicit v1 selection was requested but the stream reached a
256
+ * normal finish without ever delivering a `searchContract` acknowledgement
257
+ * (e.g. an older daemon/CLI peer that silently ignores the field), surface
258
+ * a clear error instead of letting the caller believe v1 behavior is
259
+ * active. Never retried automatically — the caller decides whether to
260
+ * retry without the contract.
261
+ */
262
+ private buildMissingAcknowledgementErrorParts;
213
263
  /** Close the open reasoning part, if any (returns the end part to emit). */
214
264
  private closeReasoningPart;
215
265
  /** Synthesize a terminal error+finish pair for streams that end (EOF or `[DONE]`) without ever emitting a "finish" part, so the UI shows an error instead of silently stalling. */