@bbclaw/core 0.1.0

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 (85) hide show
  1. package/dist/index.d.ts +8 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +8 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/providers/anthropic/index.d.ts +44 -0
  6. package/dist/providers/anthropic/index.d.ts.map +1 -0
  7. package/dist/providers/anthropic/index.js +75 -0
  8. package/dist/providers/anthropic/index.js.map +1 -0
  9. package/dist/providers/index.d.ts +8 -0
  10. package/dist/providers/index.d.ts.map +1 -0
  11. package/dist/providers/index.js +8 -0
  12. package/dist/providers/index.js.map +1 -0
  13. package/dist/providers/openai-compat/index.d.ts +59 -0
  14. package/dist/providers/openai-compat/index.d.ts.map +1 -0
  15. package/dist/providers/openai-compat/index.js +175 -0
  16. package/dist/providers/openai-compat/index.js.map +1 -0
  17. package/dist/providers/openai-compat/presets.d.ts +22 -0
  18. package/dist/providers/openai-compat/presets.d.ts.map +1 -0
  19. package/dist/providers/openai-compat/presets.js +73 -0
  20. package/dist/providers/openai-compat/presets.js.map +1 -0
  21. package/dist/providers/openai-compat/requestTranslate.d.ts +39 -0
  22. package/dist/providers/openai-compat/requestTranslate.d.ts.map +1 -0
  23. package/dist/providers/openai-compat/requestTranslate.js +228 -0
  24. package/dist/providers/openai-compat/requestTranslate.js.map +1 -0
  25. package/dist/providers/openai-compat/sseParser.d.ts +29 -0
  26. package/dist/providers/openai-compat/sseParser.d.ts.map +1 -0
  27. package/dist/providers/openai-compat/sseParser.js +139 -0
  28. package/dist/providers/openai-compat/sseParser.js.map +1 -0
  29. package/dist/providers/openai-compat/streamAdapter.d.ts +45 -0
  30. package/dist/providers/openai-compat/streamAdapter.d.ts.map +1 -0
  31. package/dist/providers/openai-compat/streamAdapter.js +233 -0
  32. package/dist/providers/openai-compat/streamAdapter.js.map +1 -0
  33. package/dist/providers/openai-compat/types.d.ts +126 -0
  34. package/dist/providers/openai-compat/types.d.ts.map +1 -0
  35. package/dist/providers/openai-compat/types.js +11 -0
  36. package/dist/providers/openai-compat/types.js.map +1 -0
  37. package/dist/providers/selector.d.ts +52 -0
  38. package/dist/providers/selector.d.ts.map +1 -0
  39. package/dist/providers/selector.js +101 -0
  40. package/dist/providers/selector.js.map +1 -0
  41. package/dist/providers/types.d.ts +114 -0
  42. package/dist/providers/types.d.ts.map +1 -0
  43. package/dist/providers/types.js +152 -0
  44. package/dist/providers/types.js.map +1 -0
  45. package/dist/providers/web-bridge/deepseek/spec.d.ts +22 -0
  46. package/dist/providers/web-bridge/deepseek/spec.d.ts.map +1 -0
  47. package/dist/providers/web-bridge/deepseek/spec.js +70 -0
  48. package/dist/providers/web-bridge/deepseek/spec.js.map +1 -0
  49. package/dist/providers/web-bridge/historySerializer.d.ts +49 -0
  50. package/dist/providers/web-bridge/historySerializer.d.ts.map +1 -0
  51. package/dist/providers/web-bridge/historySerializer.js +152 -0
  52. package/dist/providers/web-bridge/historySerializer.js.map +1 -0
  53. package/dist/providers/web-bridge/index.d.ts +10 -0
  54. package/dist/providers/web-bridge/index.d.ts.map +1 -0
  55. package/dist/providers/web-bridge/index.js +10 -0
  56. package/dist/providers/web-bridge/index.js.map +1 -0
  57. package/dist/providers/web-bridge/promptInjector.d.ts +63 -0
  58. package/dist/providers/web-bridge/promptInjector.d.ts.map +1 -0
  59. package/dist/providers/web-bridge/promptInjector.js +189 -0
  60. package/dist/providers/web-bridge/promptInjector.js.map +1 -0
  61. package/dist/providers/web-bridge/provider.d.ts +59 -0
  62. package/dist/providers/web-bridge/provider.d.ts.map +1 -0
  63. package/dist/providers/web-bridge/provider.js +176 -0
  64. package/dist/providers/web-bridge/provider.js.map +1 -0
  65. package/dist/providers/web-bridge/shared/BrowserSession.d.ts +51 -0
  66. package/dist/providers/web-bridge/shared/BrowserSession.d.ts.map +1 -0
  67. package/dist/providers/web-bridge/shared/BrowserSession.js +88 -0
  68. package/dist/providers/web-bridge/shared/BrowserSession.js.map +1 -0
  69. package/dist/providers/web-bridge/shared/WebBridgeAdapter.d.ts +97 -0
  70. package/dist/providers/web-bridge/shared/WebBridgeAdapter.d.ts.map +1 -0
  71. package/dist/providers/web-bridge/shared/WebBridgeAdapter.js +359 -0
  72. package/dist/providers/web-bridge/shared/WebBridgeAdapter.js.map +1 -0
  73. package/dist/providers/web-bridge/shared/observerScript.d.ts +41 -0
  74. package/dist/providers/web-bridge/shared/observerScript.d.ts.map +1 -0
  75. package/dist/providers/web-bridge/shared/observerScript.js +138 -0
  76. package/dist/providers/web-bridge/shared/observerScript.js.map +1 -0
  77. package/dist/providers/web-bridge/shared/types.d.ts +94 -0
  78. package/dist/providers/web-bridge/shared/types.d.ts.map +1 -0
  79. package/dist/providers/web-bridge/shared/types.js +25 -0
  80. package/dist/providers/web-bridge/shared/types.js.map +1 -0
  81. package/dist/providers/web-bridge/toolUseParser.d.ts +70 -0
  82. package/dist/providers/web-bridge/toolUseParser.d.ts.map +1 -0
  83. package/dist/providers/web-bridge/toolUseParser.js +360 -0
  84. package/dist/providers/web-bridge/toolUseParser.js.map +1 -0
  85. package/package.json +36 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Provider abstraction.
3
+ *
4
+ * BBClaw's agent runtime speaks one internal protocol: the type vocabulary of
5
+ * the official Anthropic SDK (Message, MessageParam, RawMessageStreamEvent,
6
+ * etc). We use those types because they're a published npm package and they
7
+ * happen to be the most expressive of the major LLM schemas (content blocks,
8
+ * tool_use, thinking, cache_control all model the world we care about).
9
+ *
10
+ * Non-Anthropic providers translate to/from these types at their own
11
+ * boundaries — see openai-compat/requestTranslate.ts and streamAdapter.ts.
12
+ */
13
+ import type { ContentBlockParam, Message, MessageCreateParamsStreaming, MessageParam, MessageStreamParams, RawMessageStreamEvent, TextBlockParam, Tool, ToolResultBlockParam, ToolUseBlockParam, ToolUnion } from '@anthropic-ai/sdk/resources/messages/messages.js';
14
+ import type { Logger, ProviderKind } from '@bbclaw/shared';
15
+ /**
16
+ * Re-export the Anthropic SDK message vocabulary as the canonical internal
17
+ * protocol. Downstream packages (cli, apps) consume these from @bbclaw/core
18
+ * rather than depending on the SDK directly.
19
+ */
20
+ export type { ContentBlockParam, Message, MessageCreateParamsStreaming, MessageParam, MessageStreamParams, RawMessageStreamEvent, TextBlockParam, Tool, ToolResultBlockParam, ToolUseBlockParam, ToolUnion, };
21
+ /**
22
+ * What a provider claims it can do. Every field defaults to the least-powerful
23
+ * answer (see FAIL_CLOSED_CAPABILITIES below). A provider that forgets a flag
24
+ * gracefully degrades, never accidentally unlocks something it can't deliver.
25
+ */
26
+ export interface ProviderCapabilities {
27
+ /** Native function-calling on the wire. False → caller must use prompt-injected emulation. */
28
+ nativeTools: boolean;
29
+ /** Honors cache_control breakpoints. False → caller should strip them before sending. */
30
+ promptCaching: boolean;
31
+ /** Returns thinking/reasoning blocks as structured content. */
32
+ extendedThinking: boolean;
33
+ /** Accepts image inputs. */
34
+ vision: boolean;
35
+ /** Conservative estimate of how much we can fit in one request. */
36
+ contextWindow: number;
37
+ /** Cap on output tokens. */
38
+ maxOutputTokens: number;
39
+ /** Streaming supported. (We require this, but flag it explicitly so synthetic providers can declare it.) */
40
+ streaming: boolean;
41
+ }
42
+ export declare const FAIL_CLOSED_CAPABILITIES: ProviderCapabilities;
43
+ export interface HealthCheckResult {
44
+ ok: boolean;
45
+ /** Round-trip time for a probe request, when measurable. */
46
+ latencyMs?: number;
47
+ /** Free-form reason on failure. Surface to user. */
48
+ reason?: string;
49
+ }
50
+ /**
51
+ * Every backend BBClaw can talk to implements this.
52
+ *
53
+ * The contract is intentionally minimal: stream a turn, do a one-shot, probe
54
+ * health, clean up. Anything else (cost tracking, retries, fallback) lives
55
+ * one layer up so it can be applied uniformly across providers.
56
+ */
57
+ export interface Provider {
58
+ /** Family — used for capability-based dispatch (e.g. "is this an API provider?"). */
59
+ readonly kind: ProviderKind;
60
+ /** Stable instance id, e.g. 'anthropic-default', 'openai-personal', 'deepseek-web'. */
61
+ readonly id: string;
62
+ /** Human-readable label for UI. */
63
+ readonly displayName: string;
64
+ /** Static capability declaration. */
65
+ readonly capabilities: ProviderCapabilities;
66
+ /**
67
+ * Suggested model id to use when the caller doesn't specify one. Optional
68
+ * because some providers (custom OpenAI-compat without a preset) genuinely
69
+ * don't know — in those cases the caller must supply --model.
70
+ */
71
+ readonly defaultModel?: string;
72
+ /**
73
+ * Streaming turn. The returned async iterable yields the Anthropic SDK
74
+ * stream-event vocabulary regardless of backend.
75
+ *
76
+ * Implementations should NOT throw for predictable errors (rate limits,
77
+ * auth, network) inside the iterable — instead yield a synthetic event
78
+ * sequence ending in message_stop and let the caller decide. They MAY
79
+ * throw before yielding the first event (e.g. param validation failures).
80
+ */
81
+ streamMessage(params: MessageStreamParams | MessageCreateParamsStreaming): Promise<AsyncIterable<RawMessageStreamEvent>>;
82
+ /** Non-streaming variant. Default implementation in BaseProvider collects the stream. */
83
+ createMessage(params: MessageStreamParams): Promise<Message>;
84
+ /** Lightweight reachability probe. Called by Selector on startup and by /provider health. */
85
+ healthCheck(): Promise<HealthCheckResult>;
86
+ /** Release any held resources (sockets, browser contexts, etc.). */
87
+ close(): Promise<void>;
88
+ }
89
+ /**
90
+ * Shared base for providers. Gives them a default createMessage that just
91
+ * collects the stream — saves boilerplate while letting any provider override
92
+ * if their backend has a more efficient one-shot path.
93
+ */
94
+ export declare abstract class BaseProvider implements Provider {
95
+ abstract readonly kind: ProviderKind;
96
+ abstract readonly id: string;
97
+ abstract readonly displayName: string;
98
+ abstract readonly capabilities: ProviderCapabilities;
99
+ readonly defaultModel?: string;
100
+ protected logger: Logger;
101
+ constructor(logger: Logger);
102
+ abstract streamMessage(params: MessageStreamParams | MessageCreateParamsStreaming): Promise<AsyncIterable<RawMessageStreamEvent>>;
103
+ createMessage(params: MessageStreamParams): Promise<Message>;
104
+ abstract healthCheck(): Promise<HealthCheckResult>;
105
+ close(): Promise<void>;
106
+ }
107
+ /**
108
+ * Fold a RawMessageStreamEvent iterable into a complete Message. Used by the
109
+ * default non-streaming path and by tests.
110
+ *
111
+ * Spec: https://docs.anthropic.com/en/api/messages-streaming
112
+ */
113
+ export declare function collectStreamToMessage(events: AsyncIterable<RawMessageStreamEvent>): Promise<Message>;
114
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Provider abstraction.
3
+ *
4
+ * BBClaw's agent runtime speaks one internal protocol: the type vocabulary of
5
+ * the official Anthropic SDK (Message, MessageParam, RawMessageStreamEvent,
6
+ * etc). We use those types because they're a published npm package and they
7
+ * happen to be the most expressive of the major LLM schemas (content blocks,
8
+ * tool_use, thinking, cache_control all model the world we care about).
9
+ *
10
+ * Non-Anthropic providers translate to/from these types at their own
11
+ * boundaries — see openai-compat/requestTranslate.ts and streamAdapter.ts.
12
+ */
13
+ export const FAIL_CLOSED_CAPABILITIES = {
14
+ nativeTools: false,
15
+ promptCaching: false,
16
+ extendedThinking: false,
17
+ vision: false,
18
+ contextWindow: 8_000,
19
+ maxOutputTokens: 4_096,
20
+ streaming: false,
21
+ };
22
+ /**
23
+ * Shared base for providers. Gives them a default createMessage that just
24
+ * collects the stream — saves boilerplate while letting any provider override
25
+ * if their backend has a more efficient one-shot path.
26
+ */
27
+ export class BaseProvider {
28
+ defaultModel;
29
+ logger;
30
+ constructor(logger) {
31
+ this.logger = logger;
32
+ }
33
+ async createMessage(params) {
34
+ const stream = await this.streamMessage(params);
35
+ return collectStreamToMessage(stream);
36
+ }
37
+ async close() {
38
+ // default: nothing held
39
+ }
40
+ }
41
+ /**
42
+ * Fold a RawMessageStreamEvent iterable into a complete Message. Used by the
43
+ * default non-streaming path and by tests.
44
+ *
45
+ * Spec: https://docs.anthropic.com/en/api/messages-streaming
46
+ */
47
+ export async function collectStreamToMessage(events) {
48
+ let message = null;
49
+ // Per-index buffers. Each content block has its own delta stream.
50
+ const textBuffers = new Map();
51
+ const toolUseBuffers = new Map();
52
+ const thinkingBuffers = new Map();
53
+ for await (const event of events) {
54
+ switch (event.type) {
55
+ case 'message_start':
56
+ message = event.message;
57
+ break;
58
+ case 'content_block_start': {
59
+ const block = event.content_block;
60
+ if (block.type === 'text') {
61
+ textBuffers.set(event.index, block.text ?? '');
62
+ }
63
+ else if (block.type === 'tool_use') {
64
+ toolUseBuffers.set(event.index, {
65
+ id: block.id,
66
+ name: block.name,
67
+ input: '',
68
+ });
69
+ }
70
+ else if (block.type === 'thinking') {
71
+ thinkingBuffers.set(event.index, {
72
+ text: block.thinking ?? '',
73
+ signature: block.signature ?? '',
74
+ });
75
+ }
76
+ break;
77
+ }
78
+ case 'content_block_delta': {
79
+ const delta = event.delta;
80
+ if (delta.type === 'text_delta') {
81
+ textBuffers.set(event.index, (textBuffers.get(event.index) ?? '') + delta.text);
82
+ }
83
+ else if (delta.type === 'input_json_delta') {
84
+ const buf = toolUseBuffers.get(event.index);
85
+ if (buf)
86
+ buf.input += delta.partial_json;
87
+ }
88
+ else if (delta.type === 'thinking_delta') {
89
+ const buf = thinkingBuffers.get(event.index);
90
+ if (buf)
91
+ buf.text += delta.thinking;
92
+ }
93
+ else if (delta.type === 'signature_delta') {
94
+ const buf = thinkingBuffers.get(event.index);
95
+ if (buf)
96
+ buf.signature += delta.signature;
97
+ }
98
+ break;
99
+ }
100
+ case 'content_block_stop':
101
+ // nothing — accumulators are kept until message_stop
102
+ break;
103
+ case 'message_delta':
104
+ if (message) {
105
+ message.stop_reason = event.delta.stop_reason ?? message.stop_reason;
106
+ message.stop_sequence = event.delta.stop_sequence ?? message.stop_sequence;
107
+ // usage in message_delta is OUTPUT-only per spec; merge carefully
108
+ if (event.usage) {
109
+ message.usage = {
110
+ ...message.usage,
111
+ output_tokens: event.usage.output_tokens,
112
+ };
113
+ }
114
+ }
115
+ break;
116
+ case 'message_stop':
117
+ break;
118
+ }
119
+ }
120
+ if (!message)
121
+ throw new Error('collectStreamToMessage: stream ended without message_start');
122
+ // Assemble content blocks in order. We sort by the indices we saw.
123
+ const allIndices = new Set([
124
+ ...textBuffers.keys(),
125
+ ...toolUseBuffers.keys(),
126
+ ...thinkingBuffers.keys(),
127
+ ]);
128
+ const sorted = [...allIndices].sort((a, b) => a - b);
129
+ const content = sorted.map((idx) => {
130
+ if (textBuffers.has(idx)) {
131
+ return { type: 'text', text: textBuffers.get(idx) ?? '', citations: null };
132
+ }
133
+ if (toolUseBuffers.has(idx)) {
134
+ const tu = toolUseBuffers.get(idx);
135
+ let input = {};
136
+ try {
137
+ input = tu.input ? JSON.parse(tu.input) : {};
138
+ }
139
+ catch {
140
+ // Malformed JSON from a misbehaving backend; surface the raw text so the
141
+ // caller can decide what to do (rather than silently dropping it).
142
+ input = { _malformed: tu.input };
143
+ }
144
+ return { type: 'tool_use', id: tu.id, name: tu.name, input };
145
+ }
146
+ const th = thinkingBuffers.get(idx);
147
+ return { type: 'thinking', thinking: th.text, signature: th.signature };
148
+ });
149
+ message.content = content;
150
+ return message;
151
+ }
152
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * DeepSeek web-bridge spec.
3
+ *
4
+ * Targets chat.deepseek.com. Selectors use attribute-CONTAINS matching
5
+ * instead of exact class names — DeepSeek's frontend ships hashed class
6
+ * names that change on every deploy, but a few semantic prefixes are
7
+ * stable: `ds-message`, `ds-markdown`, `ds-think-content`.
8
+ *
9
+ * DOM shape as of 2026-05 (verified via scripts/probe-deepseek-dom.ts):
10
+ * - Each turn is wrapped in `.ds-message` (used for BOTH user + assistant).
11
+ * - Assistant's answer content lives in `.ds-markdown` inside that wrapper.
12
+ * - Assistant's chain-of-thought lives in `.ds-think-content` (filtered out
13
+ * by the observer's reasoning heuristic).
14
+ * - User messages have no `.ds-markdown` child — distinguished via :not(:has()).
15
+ *
16
+ * If DeepSeek ever rebrands these completely, this file is the recovery
17
+ * point. Re-run scripts/probe-deepseek-dom.ts to discover the new classes
18
+ * and update below.
19
+ */
20
+ import type { WebBridgeSpec } from '../shared/types.js';
21
+ export declare const DEEPSEEK_WEB_SPEC: WebBridgeSpec;
22
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../../src/providers/web-bridge/deepseek/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,eAAO,MAAM,iBAAiB,EAAE,aAyD/B,CAAA"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * DeepSeek web-bridge spec.
3
+ *
4
+ * Targets chat.deepseek.com. Selectors use attribute-CONTAINS matching
5
+ * instead of exact class names — DeepSeek's frontend ships hashed class
6
+ * names that change on every deploy, but a few semantic prefixes are
7
+ * stable: `ds-message`, `ds-markdown`, `ds-think-content`.
8
+ *
9
+ * DOM shape as of 2026-05 (verified via scripts/probe-deepseek-dom.ts):
10
+ * - Each turn is wrapped in `.ds-message` (used for BOTH user + assistant).
11
+ * - Assistant's answer content lives in `.ds-markdown` inside that wrapper.
12
+ * - Assistant's chain-of-thought lives in `.ds-think-content` (filtered out
13
+ * by the observer's reasoning heuristic).
14
+ * - User messages have no `.ds-markdown` child — distinguished via :not(:has()).
15
+ *
16
+ * If DeepSeek ever rebrands these completely, this file is the recovery
17
+ * point. Re-run scripts/probe-deepseek-dom.ts to discover the new classes
18
+ * and update below.
19
+ */
20
+ export const DEEPSEEK_WEB_SPEC = {
21
+ serviceId: 'deepseek-web',
22
+ displayName: 'DeepSeek (Web Bridge)',
23
+ homeUrl: 'https://chat.deepseek.com',
24
+ selectors: {
25
+ composer: 'textarea',
26
+ // Assistant's rendered answer. ds-markdown is the stable semantic class
27
+ // even though it ships alongside hashed siblings.
28
+ responseRoot: '.ds-markdown',
29
+ // .ds-message wraps BOTH user and assistant turns — we use it as a
30
+ // turn-boundary signal, not as a strict user-only selector. We tried
31
+ // `.ds-message:not(:has(.ds-markdown))` to isolate user messages, but
32
+ // there's a brief window where the assistant's outer `.ds-message`
33
+ // exists before its inner `.ds-markdown` is mounted — the selector
34
+ // briefly matches it, then stops matching, which made the observer's
35
+ // count-down branch move turnStartIdx past the assistant's content and
36
+ // silently lose all streaming text. Counting any `.ds-message` change
37
+ // as a turn boundary is racier-but-correct: prevResponseText resets,
38
+ // and turnStartIdx only advances at signal points we trust.
39
+ userMessage: '.ds-message',
40
+ },
41
+ // Try a ladder of locator strategies — each one returns a Playwright Locator.
42
+ // The adapter calls .click() on each in order and falls back to URL
43
+ // navigation if all of them throw. Order matters: most specific first,
44
+ // most generic last.
45
+ newChatStrategies: [
46
+ (page) => page
47
+ .getByRole('button', {
48
+ name: /新对话|新建对话|new chat|new conversation|开启新对话/i,
49
+ })
50
+ .first(),
51
+ (page) => page.getByRole('link', { name: /新对话|新建对话|new chat/i }).first(),
52
+ (page) => page.locator('button:has-text("新对话")').first(),
53
+ (page) => page.locator('button:has-text("新建对话")').first(),
54
+ (page) => page.locator('button:has-text("New chat")').first(),
55
+ (page) => page.locator('[role="button"]:has-text("新对话")').first(),
56
+ (page) => page.locator('a:has-text("新对话")').first(),
57
+ (page) => page
58
+ .locator('button[aria-label*="新对话"], button[aria-label*="新建"], button[aria-label*="new" i]')
59
+ .first(),
60
+ ],
61
+ // DeepSeek doesn't deploy aggressive bot detection (it's a logged-in flow
62
+ // for free users, and they treat the website as a product surface for
63
+ // their own users). Standard Chromium is sufficient.
64
+ stealthLevel: 'off',
65
+ // DeepSeek-V3 / R1 advertise 128k but the WEB UI's accepted input is
66
+ // smaller — somewhere around 32-64k in practice before the message gets
67
+ // truncated. Be conservative.
68
+ contextWindowEstimate: 32_000,
69
+ };
70
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../../src/providers/web-bridge/deepseek/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,CAAC,MAAM,iBAAiB,GAAkB;IAC9C,SAAS,EAAE,cAAc;IACzB,WAAW,EAAE,uBAAuB;IACpC,OAAO,EAAE,2BAA2B;IAEpC,SAAS,EAAE;QACT,QAAQ,EAAE,UAAU;QACpB,wEAAwE;QACxE,kDAAkD;QAClD,YAAY,EAAE,cAAc;QAC5B,mEAAmE;QACnE,qEAAqE;QACrE,sEAAsE;QACtE,mEAAmE;QACnE,mEAAmE;QACnE,qEAAqE;QACrE,uEAAuE;QACvE,sEAAsE;QACtE,qEAAqE;QACrE,4DAA4D;QAC5D,WAAW,EAAE,aAAa;KAC3B;IAED,8EAA8E;IAC9E,oEAAoE;IACpE,uEAAuE;IACvE,qBAAqB;IACrB,iBAAiB,EAAE;QACjB,CAAC,IAAI,EAAE,EAAE,CACP,IAAI;aACD,SAAS,CAAC,QAAQ,EAAE;YACnB,IAAI,EAAE,2CAA2C;SAClD,CAAC;aACD,KAAK,EAAE;QACZ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC,KAAK,EAAE;QACxE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,KAAK,EAAE;QACxD,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC,KAAK,EAAE;QACzD,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC,KAAK,EAAE;QAC7D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,KAAK,EAAE;QACjE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,KAAK,EAAE;QACnD,CAAC,IAAI,EAAE,EAAE,CACP,IAAI;aACD,OAAO,CACN,kFAAkF,CACnF;aACA,KAAK,EAAE;KACb;IAED,0EAA0E;IAC1E,sEAAsE;IACtE,qDAAqD;IACrD,YAAY,EAAE,KAAK;IAEnB,qEAAqE;IACrE,wEAAwE;IACxE,8BAA8B;IAC9B,qBAAqB,EAAE,MAAM;CAC9B,CAAA"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Serialize an Anthropic-shaped message history into a single text prompt
3
+ * that web-bridge chat services can ingest.
4
+ *
5
+ * Decision: send the COMPLETE history every turn (after clicking "new chat")
6
+ * rather than reuse the service's own chat session. Reasons:
7
+ * - Upper layers (microcompact, fork-agent, history rewrites) treat
8
+ * messages[] as the source of truth and routinely mutate it. The
9
+ * service's internal chat state would diverge.
10
+ * - Reusing chat state would also conflict with the agent's "tool_result"
11
+ * pattern: we'd have to stuff tool results into user turns anyway.
12
+ *
13
+ * The serialized prompt is structured so it parses well visually AND models
14
+ * see clear turn boundaries. Format:
15
+ *
16
+ * [Optional system block]
17
+ * [Tool protocol instructions]
18
+ *
19
+ * ──── Conversation history ────
20
+ *
21
+ * User:
22
+ * ...
23
+ *
24
+ * Assistant:
25
+ * ...
26
+ *
27
+ * [Tool result for Bash]:
28
+ * ...
29
+ *
30
+ * ──── Current turn ────
31
+ *
32
+ * User:
33
+ * <latest user message>
34
+ *
35
+ * Assistant:
36
+ *
37
+ * Empty "Assistant:" trailing prompt nudges the model to continue rather
38
+ * than echo the structure back.
39
+ */
40
+ import type { MessageCreateParamsBase } from '@anthropic-ai/sdk/resources/messages/messages.js';
41
+ export interface SerializeOptions {
42
+ /**
43
+ * When the most recent message is the user's current turn, render it under
44
+ * the "Current turn" header. Default: true.
45
+ */
46
+ separateCurrentTurn?: boolean;
47
+ }
48
+ export declare function serializeHistory(params: MessageCreateParamsBase, opts?: SerializeOptions): string;
49
+ //# sourceMappingURL=historySerializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historySerializer.d.ts","sourceRoot":"","sources":["../../../src/providers/web-bridge/historySerializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAEV,uBAAuB,EAKxB,MAAM,kDAAkD,CAAA;AAiBzD,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,uBAAuB,EAC/B,IAAI,GAAE,gBAAqB,GAC1B,MAAM,CAsCR"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Serialize an Anthropic-shaped message history into a single text prompt
3
+ * that web-bridge chat services can ingest.
4
+ *
5
+ * Decision: send the COMPLETE history every turn (after clicking "new chat")
6
+ * rather than reuse the service's own chat session. Reasons:
7
+ * - Upper layers (microcompact, fork-agent, history rewrites) treat
8
+ * messages[] as the source of truth and routinely mutate it. The
9
+ * service's internal chat state would diverge.
10
+ * - Reusing chat state would also conflict with the agent's "tool_result"
11
+ * pattern: we'd have to stuff tool results into user turns anyway.
12
+ *
13
+ * The serialized prompt is structured so it parses well visually AND models
14
+ * see clear turn boundaries. Format:
15
+ *
16
+ * [Optional system block]
17
+ * [Tool protocol instructions]
18
+ *
19
+ * ──── Conversation history ────
20
+ *
21
+ * User:
22
+ * ...
23
+ *
24
+ * Assistant:
25
+ * ...
26
+ *
27
+ * [Tool result for Bash]:
28
+ * ...
29
+ *
30
+ * ──── Current turn ────
31
+ *
32
+ * User:
33
+ * <latest user message>
34
+ *
35
+ * Assistant:
36
+ *
37
+ * Empty "Assistant:" trailing prompt nudges the model to continue rather
38
+ * than echo the structure back.
39
+ */
40
+ import { ARG_CLOSE, ARG_OPEN_PREFIX, ARG_OPEN_SUFFIX, TOOL_NAME_CLOSE, TOOL_NAME_OPEN, TOOL_USE_CLOSE, TOOL_USE_OPEN, buildToolProtocolPrompt, escapeArgContent, } from './promptInjector.js';
41
+ const SECTION_DIVIDER = '\n\n───────────────────────────────────────\n\n';
42
+ const HISTORY_HEADER = `${SECTION_DIVIDER}Conversation history${SECTION_DIVIDER}`;
43
+ const CURRENT_HEADER = `${SECTION_DIVIDER}Current turn${SECTION_DIVIDER}`;
44
+ export function serializeHistory(params, opts = {}) {
45
+ const sep = opts.separateCurrentTurn !== false;
46
+ const parts = [];
47
+ // 1. System content (if any)
48
+ const sysText = renderSystem(params.system);
49
+ if (sysText)
50
+ parts.push(sysText);
51
+ // 2. Tool protocol explanation (also includes tool descriptions)
52
+ const protocolText = buildToolProtocolPrompt(params.tools);
53
+ if (protocolText)
54
+ parts.push(protocolText);
55
+ // 3. History / current turn
56
+ const messages = params.messages;
57
+ if (messages.length === 0) {
58
+ // Edge case: no messages. Just return the system + protocol.
59
+ return parts.join('\n\n').trim();
60
+ }
61
+ // Split off the current (last) message if it's the user's, so we can show
62
+ // it under a clear "current" header.
63
+ const last = messages[messages.length - 1];
64
+ const isLastUser = !!last && last.role === 'user';
65
+ const history = sep && isLastUser ? messages.slice(0, -1) : messages;
66
+ if (history.length > 0) {
67
+ parts.push(HISTORY_HEADER + history.map(renderMessage).join('\n\n'));
68
+ }
69
+ if (sep && isLastUser && last) {
70
+ parts.push(`${CURRENT_HEADER}${renderMessage(last)}\n\nAssistant:`);
71
+ }
72
+ else if (!sep) {
73
+ // Without the split, append the trailing "Assistant:" cue if the last
74
+ // message is from the user.
75
+ if (isLastUser)
76
+ parts.push('Assistant:');
77
+ }
78
+ return parts.join('\n\n').trim();
79
+ }
80
+ // ----- helpers -----
81
+ function renderSystem(system) {
82
+ if (!system)
83
+ return '';
84
+ if (typeof system === 'string')
85
+ return system.trim();
86
+ return system.map((b) => b.text).join('\n\n').trim();
87
+ }
88
+ function renderMessage(m) {
89
+ const label = m.role === 'user' ? 'User' : 'Assistant';
90
+ const body = typeof m.content === 'string' ? m.content : renderBlocks(m.content, m.role);
91
+ return `${label}:\n${body.trim()}`;
92
+ }
93
+ function renderBlocks(blocks, role) {
94
+ const pieces = [];
95
+ for (const b of blocks) {
96
+ if (b.type === 'text') {
97
+ pieces.push(b.text);
98
+ }
99
+ else if (b.type === 'tool_use' && role === 'assistant') {
100
+ pieces.push(renderToolUseAsXml(b));
101
+ }
102
+ else if (b.type === 'tool_result' && role === 'user') {
103
+ pieces.push(renderToolResult(b));
104
+ }
105
+ else if (b.type === 'image') {
106
+ // Web bridges don't accept image uploads via this path; flag clearly.
107
+ pieces.push('[image omitted — web bridge does not support image uploads in this mode]');
108
+ }
109
+ else if (b.type === 'thinking') {
110
+ // Don't echo reasoning into the prompt — it's the model's own scratch
111
+ // space; sending it back would be circular and bloat the context.
112
+ continue;
113
+ }
114
+ }
115
+ return pieces.join('\n\n').trim();
116
+ }
117
+ function renderToolUseAsXml(b) {
118
+ // Re-encode the assistant's prior tool call into the same arg-tag format
119
+ // the model is asked to emit. This keeps the history visually consistent
120
+ // with the on-protocol output and reinforces the protocol by example.
121
+ const lines = [TOOL_USE_OPEN, `${TOOL_NAME_OPEN}${b.name}${TOOL_NAME_CLOSE}`];
122
+ const input = (b.input ?? {});
123
+ for (const [k, v] of Object.entries(input)) {
124
+ const raw = typeof v === 'string' ? v : JSON.stringify(v);
125
+ const escaped = escapeArgContent(raw);
126
+ // Match the multi-line style the prompt teaches: tag on its own line if
127
+ // the value contains a newline, otherwise compact on one line.
128
+ if (escaped.includes('\n')) {
129
+ lines.push(`${ARG_OPEN_PREFIX}${k}${ARG_OPEN_SUFFIX}`, escaped, ARG_CLOSE);
130
+ }
131
+ else {
132
+ lines.push(`${ARG_OPEN_PREFIX}${k}${ARG_OPEN_SUFFIX}${escaped}${ARG_CLOSE}`);
133
+ }
134
+ }
135
+ lines.push(TOOL_USE_CLOSE);
136
+ return lines.join('\n');
137
+ }
138
+ function renderToolResult(b) {
139
+ const body = typeof b.content === 'string'
140
+ ? b.content
141
+ : Array.isArray(b.content)
142
+ ? b.content
143
+ .map((p) => (p.type === 'text' ? p.text : p.type === 'image' ? '[image]' : ''))
144
+ .join('\n')
145
+ : '';
146
+ const prefix = b.is_error ? '[Tool ERROR' : '[Tool result';
147
+ // We don't know the tool name from the result block alone (only tool_use_id);
148
+ // upper layer could pass a name map if it cared, but the id pairing is
149
+ // usually obvious from the immediately preceding tool_use in the history.
150
+ return `${prefix} for ${b.tool_use_id}]:\n${body.trim()}`;
151
+ }
152
+ //# sourceMappingURL=historySerializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historySerializer.js","sourceRoot":"","sources":["../../../src/providers/web-bridge/historySerializer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** Public surface of the web-bridge subsystem. */
2
+ export { WebBridgeProvider, type WebBridgeProviderOptions } from './provider.js';
3
+ export { WebBridgeAdapter, type WebBridgeAdapterOptions } from './shared/WebBridgeAdapter.js';
4
+ export { BrowserSession, type BrowserSessionOptions } from './shared/BrowserSession.js';
5
+ export { type WebBridgeSpec, type WebBridgeState, type WebBridgeSelectors, type ResponseTextListener, type ExternalUserMessageListener, type StateChangeListener, BridgeLoginRequiredError, BridgeDOMUnrecognizedError, } from './shared/types.js';
6
+ export { DEEPSEEK_WEB_SPEC } from './deepseek/spec.js';
7
+ export { buildToolProtocolPrompt, TOOL_USE_OPEN, TOOL_USE_CLOSE, TOOL_NAME_OPEN, TOOL_NAME_CLOSE, ARG_OPEN_PREFIX, ARG_OPEN_SUFFIX, ARG_CLOSE, ARG_CLOSE_ESCAPE_TOKEN, escapeArgContent, unescapeArgContent, } from './promptInjector.js';
8
+ export { ToolUseStreamParser } from './toolUseParser.js';
9
+ export { serializeHistory, type SerializeOptions } from './historySerializer.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/web-bridge/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAElD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AACvF,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,SAAS,EACT,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,10 @@
1
+ /** Public surface of the web-bridge subsystem. */
2
+ export { WebBridgeProvider } from './provider.js';
3
+ export { WebBridgeAdapter } from './shared/WebBridgeAdapter.js';
4
+ export { BrowserSession } from './shared/BrowserSession.js';
5
+ export { BridgeLoginRequiredError, BridgeDOMUnrecognizedError, } from './shared/types.js';
6
+ export { DEEPSEEK_WEB_SPEC } from './deepseek/spec.js';
7
+ export { buildToolProtocolPrompt, TOOL_USE_OPEN, TOOL_USE_CLOSE, TOOL_NAME_OPEN, TOOL_NAME_CLOSE, ARG_OPEN_PREFIX, ARG_OPEN_SUFFIX, ARG_CLOSE, ARG_CLOSE_ESCAPE_TOKEN, escapeArgContent, unescapeArgContent, } from './promptInjector.js';
8
+ export { ToolUseStreamParser } from './toolUseParser.js';
9
+ export { serializeHistory } from './historySerializer.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/web-bridge/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAElD,OAAO,EAAE,iBAAiB,EAAiC,MAAM,eAAe,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAgC,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EAAE,cAAc,EAA8B,MAAM,4BAA4B,CAAA;AACvF,OAAO,EAOL,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,SAAS,EACT,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,wBAAwB,CAAA"}