@bunny-agent/manager 0.9.63-beta.2 → 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.
Files changed (62) hide show
  1. package/dist/__tests__/bunny-agent-cli-command.test.d.ts +2 -0
  2. package/dist/__tests__/bunny-agent-cli-command.test.d.ts.map +1 -0
  3. package/dist/__tests__/bunny-agent-cli-command.test.js +51 -0
  4. package/dist/__tests__/bunny-agent-cli-command.test.js.map +1 -0
  5. package/dist/__tests__/contracts-browser-bundle.test.d.ts +2 -0
  6. package/dist/__tests__/contracts-browser-bundle.test.d.ts.map +1 -0
  7. package/dist/__tests__/contracts-browser-bundle.test.js +37 -0
  8. package/dist/__tests__/contracts-browser-bundle.test.js.map +1 -0
  9. package/dist/__tests__/contracts-conformance.test.d.ts +2 -0
  10. package/dist/__tests__/contracts-conformance.test.d.ts.map +1 -0
  11. package/dist/__tests__/contracts-conformance.test.js +316 -0
  12. package/dist/__tests__/contracts-conformance.test.js.map +1 -0
  13. package/dist/__tests__/contracts-projection.test.d.ts +2 -0
  14. package/dist/__tests__/contracts-projection.test.d.ts.map +1 -0
  15. package/dist/__tests__/contracts-projection.test.js +131 -0
  16. package/dist/__tests__/contracts-projection.test.js.map +1 -0
  17. package/dist/__tests__/contracts-record.test.d.ts +2 -0
  18. package/dist/__tests__/contracts-record.test.d.ts.map +1 -0
  19. package/dist/__tests__/contracts-record.test.js +175 -0
  20. package/dist/__tests__/contracts-record.test.js.map +1 -0
  21. package/dist/__tests__/contracts-source-id.test.d.ts +2 -0
  22. package/dist/__tests__/contracts-source-id.test.d.ts.map +1 -0
  23. package/dist/__tests__/contracts-source-id.test.js +61 -0
  24. package/dist/__tests__/contracts-source-id.test.js.map +1 -0
  25. package/dist/__tests__/contracts-validators.test.d.ts +2 -0
  26. package/dist/__tests__/contracts-validators.test.d.ts.map +1 -0
  27. package/dist/__tests__/contracts-validators.test.js +203 -0
  28. package/dist/__tests__/contracts-validators.test.js.map +1 -0
  29. package/dist/bunny-agent.d.ts.map +1 -1
  30. package/dist/bunny-agent.js +3 -0
  31. package/dist/bunny-agent.js.map +1 -1
  32. package/dist/citation-guidance.d.ts +5 -0
  33. package/dist/citation-guidance.d.ts.map +1 -0
  34. package/dist/citation-guidance.js +23 -0
  35. package/dist/citation-guidance.js.map +1 -0
  36. package/dist/contracts/index.d.ts +14 -0
  37. package/dist/contracts/index.d.ts.map +1 -0
  38. package/dist/contracts/index.js +12 -0
  39. package/dist/contracts/index.js.map +1 -0
  40. package/dist/contracts/projection.d.ts +56 -0
  41. package/dist/contracts/projection.d.ts.map +1 -0
  42. package/dist/contracts/projection.js +129 -0
  43. package/dist/contracts/projection.js.map +1 -0
  44. package/dist/contracts/record.d.ts +56 -0
  45. package/dist/contracts/record.d.ts.map +1 -0
  46. package/dist/contracts/record.js +205 -0
  47. package/dist/contracts/record.js.map +1 -0
  48. package/dist/contracts/source-id.d.ts +26 -0
  49. package/dist/contracts/source-id.d.ts.map +1 -0
  50. package/dist/contracts/source-id.js +42 -0
  51. package/dist/contracts/source-id.js.map +1 -0
  52. package/dist/contracts/types.d.ts +143 -0
  53. package/dist/contracts/types.d.ts.map +1 -0
  54. package/dist/contracts/types.js +20 -0
  55. package/dist/contracts/types.js.map +1 -0
  56. package/dist/contracts/validators.d.ts +31 -0
  57. package/dist/contracts/validators.d.ts.map +1 -0
  58. package/dist/contracts/validators.js +261 -0
  59. package/dist/contracts/validators.js.map +1 -0
  60. package/dist/types.d.ts +15 -0
  61. package/dist/types.d.ts.map +1 -1
  62. package/package.json +17 -1
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Pure helpers converting between Bunny's canonical Unicode code-point
3
+ * offsets and JavaScript's native UTF-16 string indices, plus the
4
+ * Responses-shaped output-text projection.
5
+ *
6
+ * Bunny v1 annotation ranges are zero-based Unicode code-point offsets with
7
+ * an exclusive end, measured over the exact emitted Markdown text of one
8
+ * text part — before any Markdown rendering, Unicode normalization, or
9
+ * line-ending conversion. See design.md §7 for the canonical `A😀B [1]`
10
+ * example: the literal `[1]` is code-point range `[4,7)` and UTF-16 range
11
+ * `[5,8)`, because the leading `😀` is one code point but two UTF-16 units.
12
+ */
13
+ import type { OutputTextContent, UrlCitationAnnotation } from "./types.js";
14
+ /** Number of Unicode code points in `text` (NOT `text.length`, which counts UTF-16 units). */
15
+ export declare function codePointLength(text: string): number;
16
+ /**
17
+ * Convert a zero-based Unicode code-point offset into the corresponding
18
+ * UTF-16 index of `text`. `codePointOffset` may equal `codePointLength(text)`
19
+ * to address the end of the string.
20
+ *
21
+ * Throws if `codePointOffset` is negative or beyond the string's length.
22
+ */
23
+ export declare function codePointOffsetToUtf16Index(text: string, codePointOffset: number): number;
24
+ /**
25
+ * Convert a UTF-16 index of `text` into the corresponding zero-based Unicode
26
+ * code-point offset. `utf16Index` may equal `text.length` to address the end
27
+ * of the string. Throws if `utf16Index` splits a surrogate pair, is
28
+ * negative, or exceeds `text.length`.
29
+ */
30
+ export declare function utf16IndexToCodePointOffset(text: string, utf16Index: number): number;
31
+ /**
32
+ * Convert a native UTF-16 `[start, end)` range of `text` into a Bunny
33
+ * canonical Unicode code-point `[start, end)` range, preserving the exact
34
+ * covered substring. Throws if either boundary splits a surrogate pair or
35
+ * is out of range.
36
+ */
37
+ export declare function utf16RangeToCodePointRange(text: string, utf16Start: number, utf16End: number): {
38
+ start_index: number;
39
+ end_index: number;
40
+ };
41
+ /** Validate that a code-point annotation range is well-formed for `text`. */
42
+ export declare function isValidCodePointRange(text: string, startIndex: number, endIndex: number): boolean;
43
+ /**
44
+ * Slice `text` by a validated Unicode code-point `[startIndex, endIndex)`
45
+ * range. Callers should validate with {@link isValidCodePointRange} first;
46
+ * this throws on an invalid range rather than silently clamping.
47
+ */
48
+ export declare function sliceByCodePointRange(text: string, startIndex: number, endIndex: number): string;
49
+ /**
50
+ * Project a finalized text part and its validated annotations into the
51
+ * flat Responses `output_text` shape. Annotation order is preserved because
52
+ * citation bindings address the sibling annotations array by index. Callers
53
+ * must validate ranges against the exact finalized text before projection.
54
+ */
55
+ export declare function projectOutputText(text: string, annotations: UrlCitationAnnotation[]): OutputTextContent;
56
+ //# sourceMappingURL=projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../src/contracts/projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE3E,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,GACtB,MAAM,CAiBR;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,MAAM,CA2BR;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAU5C;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAUT;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CASR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,qBAAqB,EAAE,GACnC,iBAAiB,CAYnB"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Pure helpers converting between Bunny's canonical Unicode code-point
3
+ * offsets and JavaScript's native UTF-16 string indices, plus the
4
+ * Responses-shaped output-text projection.
5
+ *
6
+ * Bunny v1 annotation ranges are zero-based Unicode code-point offsets with
7
+ * an exclusive end, measured over the exact emitted Markdown text of one
8
+ * text part — before any Markdown rendering, Unicode normalization, or
9
+ * line-ending conversion. See design.md §7 for the canonical `A😀B [1]`
10
+ * example: the literal `[1]` is code-point range `[4,7)` and UTF-16 range
11
+ * `[5,8)`, because the leading `😀` is one code point but two UTF-16 units.
12
+ */
13
+ /** Number of Unicode code points in `text` (NOT `text.length`, which counts UTF-16 units). */
14
+ export function codePointLength(text) {
15
+ let count = 0;
16
+ // `for...of` over a string iterates by code point, not UTF-16 code unit.
17
+ for (const _ of text)
18
+ count++;
19
+ return count;
20
+ }
21
+ /**
22
+ * Convert a zero-based Unicode code-point offset into the corresponding
23
+ * UTF-16 index of `text`. `codePointOffset` may equal `codePointLength(text)`
24
+ * to address the end of the string.
25
+ *
26
+ * Throws if `codePointOffset` is negative or beyond the string's length.
27
+ */
28
+ export function codePointOffsetToUtf16Index(text, codePointOffset) {
29
+ if (!Number.isInteger(codePointOffset) || codePointOffset < 0) {
30
+ throw new RangeError(`codePointOffset must be a non-negative integer, got ${codePointOffset}`);
31
+ }
32
+ let remaining = codePointOffset;
33
+ let utf16Index = 0;
34
+ for (const ch of text) {
35
+ if (remaining === 0)
36
+ return utf16Index;
37
+ remaining--;
38
+ utf16Index += ch.length; // 1 for BMP code points, 2 for astral (surrogate pair)
39
+ }
40
+ if (remaining === 0)
41
+ return utf16Index;
42
+ throw new RangeError(`codePointOffset ${codePointOffset} exceeds code-point length of text`);
43
+ }
44
+ /**
45
+ * Convert a UTF-16 index of `text` into the corresponding zero-based Unicode
46
+ * code-point offset. `utf16Index` may equal `text.length` to address the end
47
+ * of the string. Throws if `utf16Index` splits a surrogate pair, is
48
+ * negative, or exceeds `text.length`.
49
+ */
50
+ export function utf16IndexToCodePointOffset(text, utf16Index) {
51
+ if (!Number.isInteger(utf16Index) || utf16Index < 0) {
52
+ throw new RangeError(`utf16Index must be a non-negative integer, got ${utf16Index}`);
53
+ }
54
+ if (utf16Index > text.length) {
55
+ throw new RangeError(`utf16Index ${utf16Index} exceeds UTF-16 length of text`);
56
+ }
57
+ let codePointOffset = 0;
58
+ let utf16Cursor = 0;
59
+ for (const ch of text) {
60
+ if (utf16Cursor === utf16Index)
61
+ return codePointOffset;
62
+ if (utf16Cursor + ch.length > utf16Index) {
63
+ throw new RangeError(`utf16Index ${utf16Index} splits a surrogate pair at code-point offset ${codePointOffset}`);
64
+ }
65
+ utf16Cursor += ch.length;
66
+ codePointOffset++;
67
+ }
68
+ if (utf16Cursor === utf16Index)
69
+ return codePointOffset;
70
+ throw new RangeError(`utf16Index ${utf16Index} exceeds UTF-16 length of text`);
71
+ }
72
+ /**
73
+ * Convert a native UTF-16 `[start, end)` range of `text` into a Bunny
74
+ * canonical Unicode code-point `[start, end)` range, preserving the exact
75
+ * covered substring. Throws if either boundary splits a surrogate pair or
76
+ * is out of range.
77
+ */
78
+ export function utf16RangeToCodePointRange(text, utf16Start, utf16End) {
79
+ if (utf16End < utf16Start) {
80
+ throw new RangeError(`utf16End ${utf16End} must be >= utf16Start ${utf16Start}`);
81
+ }
82
+ return {
83
+ start_index: utf16IndexToCodePointOffset(text, utf16Start),
84
+ end_index: utf16IndexToCodePointOffset(text, utf16End),
85
+ };
86
+ }
87
+ /** Validate that a code-point annotation range is well-formed for `text`. */
88
+ export function isValidCodePointRange(text, startIndex, endIndex) {
89
+ if (!Number.isInteger(startIndex) ||
90
+ !Number.isInteger(endIndex) ||
91
+ startIndex < 0 ||
92
+ endIndex <= startIndex) {
93
+ return false;
94
+ }
95
+ return endIndex <= codePointLength(text);
96
+ }
97
+ /**
98
+ * Slice `text` by a validated Unicode code-point `[startIndex, endIndex)`
99
+ * range. Callers should validate with {@link isValidCodePointRange} first;
100
+ * this throws on an invalid range rather than silently clamping.
101
+ */
102
+ export function sliceByCodePointRange(text, startIndex, endIndex) {
103
+ if (!isValidCodePointRange(text, startIndex, endIndex)) {
104
+ throw new RangeError(`invalid code-point range [${startIndex}, ${endIndex}) for text of length ${codePointLength(text)}`);
105
+ }
106
+ const utf16Start = codePointOffsetToUtf16Index(text, startIndex);
107
+ const utf16End = codePointOffsetToUtf16Index(text, endIndex);
108
+ return text.slice(utf16Start, utf16End);
109
+ }
110
+ /**
111
+ * Project a finalized text part and its validated annotations into the
112
+ * flat Responses `output_text` shape. Annotation order is preserved because
113
+ * citation bindings address the sibling annotations array by index. Callers
114
+ * must validate ranges against the exact finalized text before projection.
115
+ */
116
+ export function projectOutputText(text, annotations) {
117
+ return {
118
+ type: "output_text",
119
+ text,
120
+ annotations: annotations.map((annotation) => ({
121
+ type: "url_citation",
122
+ start_index: annotation.start_index,
123
+ end_index: annotation.end_index,
124
+ url: annotation.url,
125
+ title: annotation.title,
126
+ })),
127
+ };
128
+ }
129
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../../src/contracts/projection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Server-only helpers for building a v1 {@link WebSearchCallRecord} from raw
3
+ * provider search results.
4
+ *
5
+ * This module uses `node:crypto` (via `source-id.ts`) and MUST NOT be
6
+ * imported from `contracts/index.ts` (the browser-safe barrel re-exported
7
+ * through `@bunny-agent/sdk/contracts`). Import it directly from
8
+ * `@bunny-agent/manager/contracts/record` in Node-only runner/harness code
9
+ * that actually executes a search.
10
+ */
11
+ import type { WebSearchCallRecord, WebSearchError, WebSearchErrorCode } from "./types.js";
12
+ /** A single raw provider result, as already normalized by each tool's own provider adapter. */
13
+ export interface RawWebSearchResult {
14
+ title: string;
15
+ link: string;
16
+ snippet: string;
17
+ age?: string;
18
+ content?: string;
19
+ }
20
+ /**
21
+ * Project raw provider results into a validated v1 {@link WebSearchCallRecord}.
22
+ *
23
+ * Unsafe URLs are omitted and reported through `truncated`. If every returned
24
+ * URL is unusable, detail is unavailable; that is distinct from a confirmed
25
+ * zero-hit response. Result rows and distinct sources are both bounded.
26
+ */
27
+ export declare function buildWebSearchCallRecord(params: {
28
+ id: string;
29
+ query: string;
30
+ provider: string;
31
+ results: RawWebSearchResult[];
32
+ }): WebSearchCallRecord;
33
+ /**
34
+ * Build a `completed` v1 record for a provider that only exposes the search
35
+ * query, with no recoverable result list or citation metadata (e.g. Codex's
36
+ * `web_search` completion). `source_details: "unavailable"` is truthful
37
+ * here: the search genuinely completed, but no sources/results can ever be
38
+ * reconstructed from this provider's event shape — this is distinct from,
39
+ * and must not be confused with, a confirmed zero-hit search.
40
+ */
41
+ export declare function buildWebSearchUnavailableDetailRecord(params: {
42
+ id: string;
43
+ query: string;
44
+ provider: string;
45
+ }): WebSearchCallRecord;
46
+ /** Build a sanitized `failed` v1 record. Never include raw auth headers, keys, or unsanitized request dumps in `message`. */
47
+ export declare function buildWebSearchFailureRecord(params: {
48
+ id: string;
49
+ query: string;
50
+ provider: string;
51
+ error: WebSearchError;
52
+ }): WebSearchCallRecord;
53
+ /** Classify a thrown search error into a safe, provider-neutral {@link WebSearchErrorCode}. */
54
+ export declare function classifyWebSearchError(err: unknown): WebSearchError;
55
+ export type { WebSearchErrorCode };
56
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/contracts/record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAGnB,MAAM,YAAY,CAAC;AAIpB,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,GAAG,mBAAmB,CA+DtB;AAED;;;;;;;GAOG;AACH,wBAAgB,qCAAqC,CAAC,MAAM,EAAE;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,mBAAmB,CAgBtB;AAED,6HAA6H;AAC7H,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,cAAc,CAAC;CACvB,GAAG,mBAAmB,CAiBtB;AAED,+FAA+F;AAC/F,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAqCnE;AA0CD,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Server-only helpers for building a v1 {@link WebSearchCallRecord} from raw
3
+ * provider search results.
4
+ *
5
+ * This module uses `node:crypto` (via `source-id.ts`) and MUST NOT be
6
+ * imported from `contracts/index.ts` (the browser-safe barrel re-exported
7
+ * through `@bunny-agent/sdk/contracts`). Import it directly from
8
+ * `@bunny-agent/manager/contracts/record` in Node-only runner/harness code
9
+ * that actually executes a search.
10
+ */
11
+ import { deriveSourceId } from "./source-id.js";
12
+ import { SEARCH_CONTRACT_LIMITS } from "./types.js";
13
+ import { isSafeHttpUrl } from "./validators.js";
14
+ /**
15
+ * Project raw provider results into a validated v1 {@link WebSearchCallRecord}.
16
+ *
17
+ * Unsafe URLs are omitted and reported through `truncated`. If every returned
18
+ * URL is unusable, detail is unavailable; that is distinct from a confirmed
19
+ * zero-hit response. Result rows and distinct sources are both bounded.
20
+ */
21
+ export function buildWebSearchCallRecord(params) {
22
+ const sources = [];
23
+ const resultItems = [];
24
+ const seenSourceIds = new Set();
25
+ let truncated = false;
26
+ for (let i = 0; i < params.results.length; i++) {
27
+ if (resultItems.length >= SEARCH_CONTRACT_LIMITS.maxSourcesPerSearch) {
28
+ truncated = true;
29
+ break;
30
+ }
31
+ const raw = params.results[i];
32
+ if (!raw || !isSafeHttpUrl(raw.link)) {
33
+ truncated = true;
34
+ continue;
35
+ }
36
+ const sourceId = deriveSourceId(raw.link);
37
+ if (sourceId === undefined) {
38
+ truncated = true;
39
+ continue;
40
+ }
41
+ const title = typeof raw.title === "string" ? raw.title : undefined;
42
+ if (!seenSourceIds.has(sourceId)) {
43
+ seenSourceIds.add(sourceId);
44
+ sources.push({
45
+ type: "url",
46
+ source_id: sourceId,
47
+ url: raw.link,
48
+ ...(title ? { title } : {}),
49
+ });
50
+ }
51
+ resultItems.push({
52
+ source_id: sourceId,
53
+ url: raw.link,
54
+ ...(title ? { title } : {}),
55
+ ...(typeof raw.snippet === "string" && raw.snippet
56
+ ? { snippet: raw.snippet }
57
+ : {}),
58
+ ...(typeof raw.age === "string" && raw.age ? { age: raw.age } : {}),
59
+ ...(typeof raw.content === "string" && raw.content
60
+ ? { content: raw.content }
61
+ : {}),
62
+ });
63
+ }
64
+ return {
65
+ schema_version: 1,
66
+ type: "web_search_call",
67
+ id: params.id,
68
+ status: "completed",
69
+ action: {
70
+ type: "search",
71
+ query: params.query,
72
+ sources,
73
+ },
74
+ source_details: params.results.length > 0 && resultItems.length === 0
75
+ ? "unavailable"
76
+ : "available",
77
+ results: resultItems,
78
+ provider: params.provider,
79
+ truncated,
80
+ };
81
+ }
82
+ /**
83
+ * Build a `completed` v1 record for a provider that only exposes the search
84
+ * query, with no recoverable result list or citation metadata (e.g. Codex's
85
+ * `web_search` completion). `source_details: "unavailable"` is truthful
86
+ * here: the search genuinely completed, but no sources/results can ever be
87
+ * reconstructed from this provider's event shape — this is distinct from,
88
+ * and must not be confused with, a confirmed zero-hit search.
89
+ */
90
+ export function buildWebSearchUnavailableDetailRecord(params) {
91
+ return {
92
+ schema_version: 1,
93
+ type: "web_search_call",
94
+ id: params.id,
95
+ status: "completed",
96
+ action: {
97
+ type: "search",
98
+ query: params.query,
99
+ sources: [],
100
+ },
101
+ source_details: "unavailable",
102
+ results: [],
103
+ provider: params.provider,
104
+ truncated: false,
105
+ };
106
+ }
107
+ /** Build a sanitized `failed` v1 record. Never include raw auth headers, keys, or unsanitized request dumps in `message`. */
108
+ export function buildWebSearchFailureRecord(params) {
109
+ return {
110
+ schema_version: 1,
111
+ type: "web_search_call",
112
+ id: params.id,
113
+ status: "failed",
114
+ action: {
115
+ type: "search",
116
+ query: params.query,
117
+ sources: [],
118
+ },
119
+ source_details: "unavailable",
120
+ results: [],
121
+ provider: params.provider,
122
+ truncated: false,
123
+ error: params.error,
124
+ };
125
+ }
126
+ /** Classify a thrown search error into a safe, provider-neutral {@link WebSearchErrorCode}. */
127
+ export function classifyWebSearchError(err) {
128
+ // Provider response bodies can echo credentials or request dumps. Never
129
+ // project err.message (or String(err)) into public tool results/metadata.
130
+ const httpStatus = extractHttpStatus(err);
131
+ if (httpStatus === 401 || httpStatus === 403) {
132
+ return {
133
+ code: "authentication",
134
+ message: "Web search authentication failed.",
135
+ retryable: false,
136
+ };
137
+ }
138
+ if (httpStatus === 429) {
139
+ return {
140
+ code: "rate_limit",
141
+ message: "Web search rate limit exceeded.",
142
+ retryable: true,
143
+ };
144
+ }
145
+ if (httpStatus !== undefined && httpStatus >= 400 && httpStatus < 500) {
146
+ return {
147
+ code: "invalid_request",
148
+ message: "Web search request was rejected.",
149
+ retryable: false,
150
+ };
151
+ }
152
+ if (isNetworkError(err)) {
153
+ return {
154
+ code: "network",
155
+ message: "Web search network request failed.",
156
+ retryable: true,
157
+ };
158
+ }
159
+ return {
160
+ code: "provider_error",
161
+ message: "Web search provider failed.",
162
+ retryable: httpStatus !== undefined && httpStatus >= 500,
163
+ };
164
+ }
165
+ function extractHttpStatus(err) {
166
+ if (!(err instanceof Error))
167
+ return undefined;
168
+ const match = err.message.match(/\b(\d{3})\b/);
169
+ if (!match)
170
+ return undefined;
171
+ const status = Number.parseInt(match[1], 10);
172
+ return status >= 100 && status < 600 ? status : undefined;
173
+ }
174
+ const RETRYABLE_NETWORK_ERROR_CODES = new Set([
175
+ "EAI_AGAIN",
176
+ "ECONNREFUSED",
177
+ "ECONNRESET",
178
+ "EHOSTUNREACH",
179
+ "ENETUNREACH",
180
+ "ENOTFOUND",
181
+ "ETIMEDOUT",
182
+ "UND_ERR_BODY_TIMEOUT",
183
+ "UND_ERR_CONNECT_TIMEOUT",
184
+ "UND_ERR_HEADERS_TIMEOUT",
185
+ "UND_ERR_SOCKET",
186
+ ]);
187
+ function isNetworkError(err, seen = new Set()) {
188
+ if (err instanceof TypeError && err.message === "fetch failed")
189
+ return true;
190
+ if (err === null || typeof err !== "object" || seen.has(err))
191
+ return false;
192
+ seen.add(err);
193
+ const code = err.code;
194
+ if (typeof code === "string" && RETRYABLE_NETWORK_ERROR_CODES.has(code)) {
195
+ return true;
196
+ }
197
+ const nested = [];
198
+ if ("cause" in err)
199
+ nested.push(err.cause);
200
+ if ("errors" in err && Array.isArray(err.errors)) {
201
+ nested.push(...err.errors);
202
+ }
203
+ return nested.some((n) => isNetworkError(n, seen));
204
+ }
205
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/contracts/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Server-only stable URL source identity generation.
3
+ *
4
+ * This module uses `node:crypto` and MUST NOT be imported from
5
+ * `contracts/index.ts` (the browser-safe barrel re-exported through
6
+ * `@bunny-agent/sdk/contracts`). Import it directly from
7
+ * `@bunny-agent/manager/contracts/source-id` in Node-only runner/harness
8
+ * code that actually registers sources.
9
+ */
10
+ /**
11
+ * Validate and normalize a candidate source URL for identity hashing.
12
+ * Returns `undefined` for unsafe URLs (non-http(s), embedded credentials,
13
+ * unparsable). Path, query, and fragment are preserved verbatim — this
14
+ * intentionally does NOT strip tracking parameters, since doing so would
15
+ * create false equivalence between distinct URLs.
16
+ */
17
+ export declare function normalizeSourceUrl(url: string): string | undefined;
18
+ /**
19
+ * Derive a stable, opaque, collision-resistant source ID for a validated
20
+ * URL. The same normalized URL always yields the same ID within and across
21
+ * processes (pure function of the URL string, no per-process salt), so
22
+ * repeated searches and session resume/fork reuse one identity. Returns
23
+ * `undefined` if `url` is not a safe http(s) URL.
24
+ */
25
+ export declare function deriveSourceId(url: string): string | undefined;
26
+ //# sourceMappingURL=source-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-id.d.ts","sourceRoot":"","sources":["../../src/contracts/source-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMlE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK9D"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Server-only stable URL source identity generation.
3
+ *
4
+ * This module uses `node:crypto` and MUST NOT be imported from
5
+ * `contracts/index.ts` (the browser-safe barrel re-exported through
6
+ * `@bunny-agent/sdk/contracts`). Import it directly from
7
+ * `@bunny-agent/manager/contracts/source-id` in Node-only runner/harness
8
+ * code that actually registers sources.
9
+ */
10
+ import { createHash } from "node:crypto";
11
+ import { isSafeHttpUrl } from "./validators.js";
12
+ const SOURCE_ID_PREFIX = "src_";
13
+ /**
14
+ * Validate and normalize a candidate source URL for identity hashing.
15
+ * Returns `undefined` for unsafe URLs (non-http(s), embedded credentials,
16
+ * unparsable). Path, query, and fragment are preserved verbatim — this
17
+ * intentionally does NOT strip tracking parameters, since doing so would
18
+ * create false equivalence between distinct URLs.
19
+ */
20
+ export function normalizeSourceUrl(url) {
21
+ if (!isSafeHttpUrl(url))
22
+ return undefined;
23
+ // `new URL(...).toString()` is deterministic for a given safe input
24
+ // (consistent percent-encoding, lowercased host) without rewriting the
25
+ // semantic content of path/query/fragment.
26
+ return new URL(url).toString();
27
+ }
28
+ /**
29
+ * Derive a stable, opaque, collision-resistant source ID for a validated
30
+ * URL. The same normalized URL always yields the same ID within and across
31
+ * processes (pure function of the URL string, no per-process salt), so
32
+ * repeated searches and session resume/fork reuse one identity. Returns
33
+ * `undefined` if `url` is not a safe http(s) URL.
34
+ */
35
+ export function deriveSourceId(url) {
36
+ const normalized = normalizeSourceUrl(url);
37
+ if (normalized === undefined)
38
+ return undefined;
39
+ const hash = createHash("sha256").update(normalized, "utf8").digest("hex");
40
+ return `${SOURCE_ID_PREFIX}${hash}`;
41
+ }
42
+ //# sourceMappingURL=source-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-id.js","sourceRoot":"","sources":["../../src/contracts/source-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,oEAAoE;IACpE,uEAAuE;IACvE,2CAA2C;IAC3C,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3E,OAAO,GAAG,gBAAgB,GAAG,IAAI,EAAE,CAAC;AACtC,CAAC"}