orbitx 0.2.9 → 0.3.2
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 +478 -456
- package/dist/agents/backend.d.ts +4 -0
- package/dist/agents/backend.d.ts.map +1 -0
- package/dist/agents/backend.js +54 -0
- package/dist/agents/backend.js.map +1 -0
- package/dist/agents/frontend.d.ts +4 -0
- package/dist/agents/frontend.d.ts.map +1 -0
- package/dist/agents/frontend.js +50 -0
- package/dist/agents/frontend.js.map +1 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +22 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/planner.d.ts +4 -0
- package/dist/agents/planner.d.ts.map +1 -0
- package/dist/agents/planner.js +86 -0
- package/dist/agents/planner.js.map +1 -0
- package/dist/agents/protocol.d.ts +5 -0
- package/dist/agents/protocol.d.ts.map +1 -0
- package/dist/agents/protocol.js +70 -0
- package/dist/agents/protocol.js.map +1 -0
- package/dist/agents/reasoner.d.ts +5 -0
- package/dist/agents/reasoner.d.ts.map +1 -0
- package/dist/agents/reasoner.js +96 -0
- package/dist/agents/reasoner.js.map +1 -0
- package/dist/agents/research.d.ts +4 -0
- package/dist/agents/research.d.ts.map +1 -0
- package/dist/agents/research.js +28 -0
- package/dist/agents/research.js.map +1 -0
- package/dist/agents/tester.d.ts +4 -0
- package/dist/agents/tester.d.ts.map +1 -0
- package/dist/agents/tester.js +70 -0
- package/dist/agents/tester.js.map +1 -0
- package/dist/core/agent-definition.d.ts +61 -0
- package/dist/core/agent-definition.d.ts.map +1 -0
- package/dist/core/agent-definition.js +82 -0
- package/dist/core/agent-definition.js.map +1 -0
- package/dist/core/ai-ask.d.ts +28 -0
- package/dist/core/ai-ask.d.ts.map +1 -0
- package/dist/core/ai-ask.js +80 -0
- package/dist/core/ai-ask.js.map +1 -0
- package/dist/core/ai-provider.d.ts +3 -0
- package/dist/core/ai-provider.d.ts.map +1 -1
- package/dist/core/ai-provider.js.map +1 -1
- package/dist/core/anthropic-provider.d.ts +1 -0
- package/dist/core/anthropic-provider.d.ts.map +1 -1
- package/dist/core/anthropic-provider.js +3 -0
- package/dist/core/anthropic-provider.js.map +1 -1
- package/dist/core/base-agent.d.ts +51 -28
- package/dist/core/base-agent.d.ts.map +1 -1
- package/dist/core/base-agent.js +118 -26
- package/dist/core/base-agent.js.map +1 -1
- package/dist/core/deepseek-provider.d.ts +1 -0
- package/dist/core/deepseek-provider.d.ts.map +1 -1
- package/dist/core/deepseek-provider.js +3 -0
- package/dist/core/deepseek-provider.js.map +1 -1
- package/dist/core/mcp-auto-execution-policy.d.ts +22 -0
- package/dist/core/mcp-auto-execution-policy.d.ts.map +1 -0
- package/dist/core/mcp-auto-execution-policy.js +85 -0
- package/dist/core/mcp-auto-execution-policy.js.map +1 -0
- package/dist/core/mcp-computer.d.ts +14 -1
- package/dist/core/mcp-computer.d.ts.map +1 -1
- package/dist/core/mcp-computer.js +352 -71
- package/dist/core/mcp-computer.js.map +1 -1
- package/dist/core/ollama-provider.d.ts +1 -0
- package/dist/core/ollama-provider.d.ts.map +1 -1
- package/dist/core/ollama-provider.js +3 -0
- package/dist/core/ollama-provider.js.map +1 -1
- package/dist/core/openai-provider.d.ts +1 -0
- package/dist/core/openai-provider.d.ts.map +1 -1
- package/dist/core/openai-provider.js +3 -0
- package/dist/core/openai-provider.js.map +1 -1
- package/dist/core/stateless-agent.d.ts +30 -0
- package/dist/core/stateless-agent.d.ts.map +1 -0
- package/dist/core/stateless-agent.js +79 -0
- package/dist/core/stateless-agent.js.map +1 -0
- package/dist/core/swarm-base.d.ts +88 -0
- package/dist/core/swarm-base.d.ts.map +1 -0
- package/dist/core/swarm-base.js +427 -0
- package/dist/core/swarm-base.js.map +1 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/tests/agents/default-agents.test.d.ts +2 -0
- package/dist/tests/agents/default-agents.test.d.ts.map +1 -0
- package/dist/tests/agents/default-agents.test.js +106 -0
- package/dist/tests/agents/default-agents.test.js.map +1 -0
- package/dist/tests/core/agent-definition.test.d.ts +2 -0
- package/dist/tests/core/agent-definition.test.d.ts.map +1 -0
- package/dist/tests/core/agent-definition.test.js +99 -0
- package/dist/tests/core/agent-definition.test.js.map +1 -0
- package/dist/tests/core/ai-ask.test.d.ts +2 -0
- package/dist/tests/core/ai-ask.test.d.ts.map +1 -0
- package/dist/tests/core/ai-ask.test.js +101 -0
- package/dist/tests/core/ai-ask.test.js.map +1 -0
- package/dist/tests/core/mcp-auto-execution-policy.test.d.ts +2 -0
- package/dist/tests/core/mcp-auto-execution-policy.test.d.ts.map +1 -0
- package/dist/tests/core/mcp-auto-execution-policy.test.js +107 -0
- package/dist/tests/core/mcp-auto-execution-policy.test.js.map +1 -0
- package/dist/tests/core/scripted-provider.d.ts +38 -0
- package/dist/tests/core/scripted-provider.d.ts.map +1 -0
- package/dist/tests/core/scripted-provider.js +85 -0
- package/dist/tests/core/scripted-provider.js.map +1 -0
- package/dist/tests/core/stateless-agent.test.d.ts +2 -0
- package/dist/tests/core/stateless-agent.test.d.ts.map +1 -0
- package/dist/tests/core/stateless-agent.test.js +73 -0
- package/dist/tests/core/stateless-agent.test.js.map +1 -0
- package/dist/tests/core/swarm-base.test.d.ts +2 -0
- package/dist/tests/core/swarm-base.test.d.ts.map +1 -0
- package/dist/tests/core/swarm-base.test.js +387 -0
- package/dist/tests/core/swarm-base.test.js.map +1 -0
- package/dist/tests/index.d.ts +6 -0
- package/dist/tests/index.d.ts.map +1 -1
- package/dist/tests/index.js +6 -0
- package/dist/tests/index.js.map +1 -1
- package/dist/tests/tools/agent/agent-tools.test.js +225 -153
- package/dist/tests/tools/agent/agent-tools.test.js.map +1 -1
- package/dist/tools/agent/active.d.ts +5 -0
- package/dist/tools/agent/active.d.ts.map +1 -0
- package/dist/tools/agent/active.js +40 -0
- package/dist/tools/agent/active.js.map +1 -0
- package/dist/tools/agent/fire.d.ts +5 -0
- package/dist/tools/agent/fire.d.ts.map +1 -0
- package/dist/tools/agent/fire.js +38 -0
- package/dist/tools/agent/fire.js.map +1 -0
- package/dist/tools/agent/hire.d.ts +2 -2
- package/dist/tools/agent/hire.d.ts.map +1 -1
- package/dist/tools/agent/hire.js +43 -10
- package/dist/tools/agent/hire.js.map +1 -1
- package/dist/tools/agent/index.d.ts +22 -19
- package/dist/tools/agent/index.d.ts.map +1 -1
- package/dist/tools/agent/index.js +29 -21
- package/dist/tools/agent/index.js.map +1 -1
- package/dist/tools/agent/prompt.d.ts +2 -2
- package/dist/tools/agent/prompt.d.ts.map +1 -1
- package/dist/tools/agent/prompt.js +19 -53
- package/dist/tools/agent/prompt.js.map +1 -1
- package/dist/tools/agent/registry.d.ts +74 -24
- package/dist/tools/agent/registry.d.ts.map +1 -1
- package/dist/tools/agent/registry.js +93 -48
- package/dist/tools/agent/registry.js.map +1 -1
- package/dist/tools/agent/report-group.d.ts +5 -0
- package/dist/tools/agent/report-group.d.ts.map +1 -0
- package/dist/tools/agent/report-group.js +50 -0
- package/dist/tools/agent/report-group.js.map +1 -0
- package/dist/tools/agent/report-parent.d.ts +5 -0
- package/dist/tools/agent/report-parent.d.ts.map +1 -0
- package/dist/tools/agent/report-parent.js +36 -0
- package/dist/tools/agent/report-parent.js.map +1 -0
- package/dist/tools/agent/types.d.ts +5 -0
- package/dist/tools/agent/types.d.ts.map +1 -0
- package/dist/tools/agent/types.js +17 -0
- package/dist/tools/agent/types.js.map +1 -0
- package/docs/agents.md +249 -228
- package/docs/api-reference.md +9 -4
- package/docs/getting-started.md +78 -78
- package/docs/index.md +24 -23
- package/docs/mcp-architecture.md +71 -6
- package/docs/providers.md +258 -254
- package/docs/skills.md +0 -1
- package/docs/streaming.md +133 -133
- package/docs/swarm.md +503 -0
- package/docs/tools.md +214 -212
- package/package.json +1 -1
- package/dist/core/image-describer.d.ts +0 -19
- package/dist/core/image-describer.d.ts.map +0 -1
- package/dist/core/image-describer.js +0 -37
- package/dist/core/image-describer.js.map +0 -1
- package/dist/core/worker-agent.d.ts +0 -43
- package/dist/core/worker-agent.d.ts.map +0 -1
- package/dist/core/worker-agent.js +0 -40
- package/dist/core/worker-agent.js.map +0 -1
- package/dist/skills/shopping.d.ts +0 -4
- package/dist/skills/shopping.d.ts.map +0 -1
- package/dist/skills/shopping.js +0 -71
- package/dist/skills/shopping.js.map +0 -1
- package/dist/tools/agent/interaction.d.ts +0 -25
- package/dist/tools/agent/interaction.d.ts.map +0 -1
- package/dist/tools/agent/interaction.js +0 -14
- package/dist/tools/agent/interaction.js.map +0 -1
- package/dist/tools/agent/list.d.ts +0 -5
- package/dist/tools/agent/list.d.ts.map +0 -1
- package/dist/tools/agent/list.js +0 -16
- package/dist/tools/agent/list.js.map +0 -1
- package/dist/tools/agent/report.d.ts +0 -12
- package/dist/tools/agent/report.d.ts.map +0 -1
- package/dist/tools/agent/report.js +0 -31
- package/dist/tools/agent/report.js.map +0 -1
- package/dist/tools/bash/interaction.d.ts +0 -29
- package/dist/tools/bash/interaction.d.ts.map +0 -1
- package/dist/tools/bash/interaction.js +0 -13
- package/dist/tools/bash/interaction.js.map +0 -1
- package/dist/tools/browser/interaction.d.ts +0 -49
- package/dist/tools/browser/interaction.d.ts.map +0 -1
- package/dist/tools/browser/interaction.js +0 -14
- package/dist/tools/browser/interaction.js.map +0 -1
- package/dist/tools/fs/interaction.d.ts +0 -37
- package/dist/tools/fs/interaction.d.ts.map +0 -1
- package/dist/tools/fs/interaction.js +0 -13
- package/dist/tools/fs/interaction.js.map +0 -1
- package/dist/tools/present/interaction.d.ts +0 -17
- package/dist/tools/present/interaction.d.ts.map +0 -1
- package/dist/tools/present/interaction.js +0 -15
- package/dist/tools/present/interaction.js.map +0 -1
- package/dist/tools/question-answer/interaction.d.ts +0 -14
- package/dist/tools/question-answer/interaction.d.ts.map +0 -1
- package/dist/tools/question-answer/interaction.js +0 -13
- package/dist/tools/question-answer/interaction.js.map +0 -1
- package/dist/tools/todo/interaction.d.ts +0 -30
- package/dist/tools/todo/interaction.d.ts.map +0 -1
- package/dist/tools/todo/interaction.js +0 -13
- package/dist/tools/todo/interaction.js.map +0 -1
package/docs/providers.md
CHANGED
|
@@ -1,254 +1,258 @@
|
|
|
1
|
-
# Providers
|
|
2
|
-
|
|
3
|
-
An `AIProvider` is OrbitX's abstraction over a model API. All four built-in providers implement the same interface (`src/core/ai-provider.ts`):
|
|
4
|
-
|
|
5
|
-
```ts
|
|
6
|
-
abstract class AIProvider {
|
|
7
|
-
abstract chat(
|
|
8
|
-
messages: Message[],
|
|
9
|
-
streamCallback?: StreamCallback,
|
|
10
|
-
tools?: ToolSchema[],
|
|
11
|
-
signal?: AbortSignal
|
|
12
|
-
): Promise<ChatResponse>;
|
|
13
|
-
|
|
14
|
-
abstract
|
|
15
|
-
|
|
16
|
-
abstract
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- `
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
| "
|
|
193
|
-
| "
|
|
194
|
-
| "
|
|
195
|
-
| "
|
|
196
|
-
| "
|
|
197
|
-
| "
|
|
198
|
-
| "
|
|
199
|
-
| "
|
|
200
|
-
| "
|
|
201
|
-
| "
|
|
202
|
-
| "
|
|
203
|
-
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
`
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
| `
|
|
235
|
-
|
|
236
|
-
| `"
|
|
237
|
-
| `"
|
|
238
|
-
| `"
|
|
239
|
-
| `"
|
|
240
|
-
| `"
|
|
241
|
-
| `"
|
|
242
|
-
| `"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
`
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
1
|
+
# Providers
|
|
2
|
+
|
|
3
|
+
An `AIProvider` is OrbitX's abstraction over a model API. All four built-in providers implement the same interface (`src/core/ai-provider.ts`):
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
abstract class AIProvider {
|
|
7
|
+
abstract chat(
|
|
8
|
+
messages: Message[],
|
|
9
|
+
streamCallback?: StreamCallback,
|
|
10
|
+
tools?: ToolSchema[],
|
|
11
|
+
signal?: AbortSignal
|
|
12
|
+
): Promise<ChatResponse>;
|
|
13
|
+
|
|
14
|
+
abstract getModel(): string;
|
|
15
|
+
|
|
16
|
+
abstract getCapabilities(): ProviderCapabilities;
|
|
17
|
+
|
|
18
|
+
abstract setOption(key: string, value: unknown): void;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- `chat` sends the full message history (plus a provider-agnostic `tools` schema, when supplied) and returns a `ChatResponse` (`{ content, inputMissTokens, inputCacheTokens, outputTokens, toolCalls? }`) — `inputMissTokens`/`inputCacheTokens` are real, provider-reported non-cached/cache-hit input token counts (see "User tracking & prompt caching" below), not a client-side estimate. If `streamCallback` is passed, text is streamed to it incrementally as it arrives — see [Streaming](./streaming.md).
|
|
23
|
+
- `signal`, when passed and later aborted, cancels the in-flight request to the provider's API immediately rather than just abandoning it client-side — `BaseAgent.immediateStop()` uses this so the call stops generating (and billing for) further output right away. It doesn't retroactively waive input tokens or output already generated before the abort. Anthropic, OpenAI, and DeepSeek honor it for both streaming and non-streaming calls (their SDKs forward it straight to `fetch`). Ollama only honors it for streaming calls — its client has no way to abort a non-streaming request — so a `chat()` call made without a `streamCallback` against `OllamaProvider` can't be cancelled mid-flight; see the retry-loop guard in `ollama-provider.ts`.
|
|
24
|
+
- **Streaming calls resolve normally on abort**, returning a `ChatResponse` built from whatever content/thinking/tool-calls were accumulated before the cutoff, instead of throwing — so text already delivered via `streamCallback` still ends up recorded rather than discarded. A tool call still being generated when the abort lands is dropped (its arguments may be truncated JSON); only tool calls that had already fully finished streaming are included in `toolCalls`.
|
|
25
|
+
- **Non-streaming calls still throw on abort** — there's no partial response to salvage since the API only returns once, in full — and `BaseAgent` discards that turn.
|
|
26
|
+
- `getModel()` returns the model id this instance talks to (e.g. `"deepseek-v4-flash"`). It is stamped onto every usage entry the agent records for this provider, so a total can be broken down per model and not just per role — see [Token accounting](./agents.md#token-accounting). A custom provider must implement it; return whatever string identifies the model you're billing against.
|
|
27
|
+
|
|
28
|
+
- `getCapabilities()` returns a static description used by `BaseAgent` to decide things like when to trigger memory compaction — it is **not** re-queried per call:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
interface ProviderCapabilities {
|
|
32
|
+
supportsTools: boolean; // must be true — BaseAgent requires native tool-calling
|
|
33
|
+
supportsImages: boolean; // whether this provider/model accepts image content
|
|
34
|
+
contextWindow: number; // total context window, in tokens
|
|
35
|
+
safeUsageRatio?: number; // fraction of contextWindow safe to fill before compacting (default 0.5)
|
|
36
|
+
supportsThinking?: boolean; // whether reasoning/thinking text is surfaced — see "Think effort" below
|
|
37
|
+
maxOutputTokens?: number; // the model's max output tokens per response, where confirmed — not yet enforced anywhere, reserved for a future release
|
|
38
|
+
supportsVideo?: boolean; // whether this provider/model accepts video content
|
|
39
|
+
supportsAudio?: boolean; // whether this provider/model accepts audio content
|
|
40
|
+
supportsImageGeneration?: boolean; // whether this provider/model can generate new images
|
|
41
|
+
supportsImageEditing?: boolean; // whether this provider/model can edit an existing image
|
|
42
|
+
supportsVideoGeneration?: boolean; // whether this provider/model can generate new video
|
|
43
|
+
supportsVideoEditing?: boolean; // whether this provider/model can edit existing video
|
|
44
|
+
supportsAudioGeneration?: boolean; // whether this provider/model can generate new audio (speech, music, sound effects)
|
|
45
|
+
supportsAudioDesign?: boolean; // whether this provider/model can design/produce sound effects or soundscapes
|
|
46
|
+
supportsAudioClone?: boolean; // whether this provider/model can clone a voice from a reference sample
|
|
47
|
+
supports3DModelGeneration?: boolean; // whether this provider/model can generate a new 3D model
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
These flags describe what the model itself can natively do — they're unrelated to `ProviderType` (see "Provider roles" below), which is just a role name used to look a provider up in a `ProviderRegistry`. A provider registered under the `"image-generation"` role doesn't need `supportsImageGeneration: true` (or vice versa) — the role name and the capability flag are independent. All four built-in providers are chat/vision LLMs, not generation APIs, so every one of the new flags above is always `false` on them.
|
|
52
|
+
|
|
53
|
+
`BaseAgent.run()` throws immediately if `getCapabilities().supportsTools` is `false` — the legacy JSON-in-text tool-call convention has been removed from the main loop. All providers default `supportsTools` to `true` except Ollama, which defaults it to `false` (many locally-hosted models don't support native function calling; pass `{ supportsTools: true }` explicitly if your model does).
|
|
54
|
+
|
|
55
|
+
## Changing an option without reconstructing
|
|
56
|
+
|
|
57
|
+
`setOption(key, value)` mutates an already-constructed provider in place — e.g. `provider.setOption("thinkEffort", 0.8)` — instead of building a new one. Every built-in provider only allows keys it already reads fresh on every `chat()` call (never something baked into derived state at construction, like `contextWindow`); an unrecognized key throws.
|
|
58
|
+
|
|
59
|
+
| Provider | Settable keys |
|
|
60
|
+
|---|---|
|
|
61
|
+
| Anthropic | `thinkEffort`, `userId`, `disablePromptCaching`, `maxTokens`, `supportsTools` |
|
|
62
|
+
| OpenAI | `thinkEffort`, `userId`, `supportsTools` |
|
|
63
|
+
| DeepSeek | `thinkEffort`, `userId`, `supportsTools` |
|
|
64
|
+
| Ollama | `thinkEffort`, `supportsTools` |
|
|
65
|
+
|
|
66
|
+
## Usage on the streaming path
|
|
67
|
+
|
|
68
|
+
OpenAI and DeepSeek's Chat-Completions-style streaming protocol doesn't compute token usage until a turn is fully complete — it arrives once, in a special final chunk (`stream_options: { include_usage: true }`), alongside `done: true`. Every chunk before that has `usage: { inputMissTokens: 0, inputCacheTokens: 0, outputTokens: 0 }` — not because those numbers are unknown-but-estimated, but because the API genuinely hasn't computed them yet. `OpenAIProvider` and `DeepSeekProvider` both trust this real, provider-reported final chunk directly — no local estimation involved on either. DeepSeek's final chunk includes its own `prompt_cache_hit_tokens`/`prompt_cache_miss_tokens` fields (falling back to `prompt_tokens_details.cached_tokens` if those aren't present), parsed the same way real cache data is read from any other provider.
|
|
69
|
+
|
|
70
|
+
If a call is cut off before that final chunk arrives (e.g. `BaseAgent.immediateStop()` aborting the connection to stop paying for further output), the real usage number simply never gets computed or sent for either provider — not partially, not late, just never — so `inputMissTokens`/`inputCacheTokens`/`outputTokens` stay `0` on `ChatResponse` too in that case. Anthropic doesn't have this problem (it streams real usage incrementally, see [Streaming](./streaming.md#usage-chunks)); Ollama has the same final-chunk-only limitation but isn't covered by what's described here.
|
|
71
|
+
|
|
72
|
+
This is the only usage source DeepSeek's streaming path has, so it's treated as the real number, not flagged as a lesser "estimate" — whatever lands on the final `done: true` chunk (and the returned `ChatResponse`) is what a consumer should use for billing/accounting, the same as it would for any other provider's real, API-reported usage. The two sides of that number are revealed at different points, though: `outputTokens` grows on every streamed chunk as content arrives, since it's cheap to recompute from what's been generated so far; `inputMissTokens`/`inputCacheTokens` are only computed once, on that final chunk, since the input side never changes mid-call and there's no reason to resend the same number on every content delta.
|
|
73
|
+
|
|
74
|
+
## Think effort
|
|
75
|
+
|
|
76
|
+
Reasoning/thinking models each expose a different native "how hard should the model think" knob — OpenAI and Ollama use `low`/`medium`/`high`, DeepSeek uses `none`/`low`/`high`/`max`, Anthropic uses a numeric thinking-token budget. OrbitX replaces all of that with one universal option on every provider's constructor:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
thinkEffort?: number; // 0-1, e.g. 0.5 or 0.8 — never a provider-specific string
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Each provider maps that 0-1 value onto whatever scale it actually accepts (`src/core/think-effort.ts`'s `resolveThinkEffortLevel()` buckets it evenly across a provider's supported levels; Anthropic instead scales it into a `budget_tokens` range). A provider or model that doesn't support thinking at all — or doesn't recognize the resulting native value — simply ignores it:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { AnthropicProvider, DeepSeekProvider } from "orbitx";
|
|
86
|
+
|
|
87
|
+
const anthropicProvider = new AnthropicProvider("api-key", "claude-sonnet-5", { thinkEffort: 0.7 });
|
|
88
|
+
const deepseekProvider = new DeepSeekProvider("api-key", "deepseek-v4-flash", { thinkEffort: 0.3 });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Per-provider specifics:
|
|
92
|
+
|
|
93
|
+
| Provider | Native scale | Notes |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| Anthropic | `thinking.budget_tokens` (numeric) | Scales with `maxTokens`; `max_tokens` sent to the API is automatically bumped so it always exceeds the thinking budget. Signed thinking blocks are carried forward automatically via `Message.providerThinking` when a thinking turn also made a tool call — required by Anthropic's API, handled for you. |
|
|
96
|
+
| DeepSeek | `none` / `low` / `high` / `max` | Sent as `reasoning_effort` on reasoner models. |
|
|
97
|
+
| OpenAI | `low` / `medium` / `high` | Sent as `reasoning_effort`, and **only** for reasoning-capable model families (`o1`/`o3`/`o4`/`gpt-5*` prefixes) — silently ignored for every other model, since the Chat Completions API rejects the param on non-reasoning models. |
|
|
98
|
+
| Ollama | `false` / `low` / `medium` / `high` | `thinkEffort <= 0` maps to `false` (works as an on/off switch for models that only understand a boolean, e.g. deepseek-r1/qwen3); higher values map to a level string for models that support graded effort (e.g. gpt-oss). |
|
|
99
|
+
|
|
100
|
+
See [Streaming](./streaming.md#thinking-chunks) for how (and whether) a given provider's reasoning text is observable while it streams.
|
|
101
|
+
|
|
102
|
+
## User tracking & prompt caching
|
|
103
|
+
|
|
104
|
+
Every provider's constructor accepts `userId?: string` — an opaque end-user identifier forwarded to the underlying API for that provider's own abuse-monitoring/analytics, same universal-option pattern as `thinkEffort`: pass it uniformly and providers that have no equivalent field just ignore it.
|
|
105
|
+
|
|
106
|
+
| Provider | Where `userId` goes | Notes |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| Anthropic | `metadata.user_id` on every request | Anthropic's documented field for this purpose. |
|
|
109
|
+
| OpenAI | `user_id` on every request | Sent as an extra body field alongside the standard params. |
|
|
110
|
+
| DeepSeek | `user_id` on every request | Same mechanism as OpenAI (DeepSeek's API is OpenAI-compatible). |
|
|
111
|
+
| Ollama | Ignored | Accepted in the options type for interface parity only — Ollama's API has no equivalent field. |
|
|
112
|
+
|
|
113
|
+
**Anthropic prompt caching is on by default.** Every request marks the last content block with an ephemeral `cache_control` breakpoint, so a growing conversation gets served from cache instead of being fully reprocessed each turn — pass `disablePromptCaching: true` to send every request uncached. DeepSeek's cache is automatic server-side (no `cache_control` equivalent to toggle) and reports its own explicit `prompt_cache_hit_tokens`/`prompt_cache_miss_tokens`. OpenAI's cache is also automatic, reporting hits via `prompt_tokens_details.cached_tokens`. Ollama has no cache concept at all — `inputCacheTokens` is always `0`.
|
|
114
|
+
|
|
115
|
+
## Built-in providers
|
|
116
|
+
|
|
117
|
+
### Ollama
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { OllamaProvider } from "orbitx";
|
|
121
|
+
|
|
122
|
+
new OllamaProvider(model: string, host = "http://localhost:11434", options?: {
|
|
123
|
+
supportsTools?: boolean; // default false
|
|
124
|
+
contextWindow?: number; // default 32_000
|
|
125
|
+
thinkEffort?: number; // 0-1, see "Think effort" above
|
|
126
|
+
userId?: string; // accepted for interface parity only — ignored, Ollama has no equivalent field
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Wraps the `ollama` npm package, talking to a local (or remote) Ollama server. Always reports `supportsImages: true`.
|
|
131
|
+
|
|
132
|
+
### DeepSeek
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { DeepSeekProvider } from "orbitx";
|
|
136
|
+
|
|
137
|
+
new DeepSeekProvider(apiKey: string, model = "deepseek-v4-flash", options?: {
|
|
138
|
+
supportsTools?: boolean; // default true
|
|
139
|
+
supportsImages?: boolean; // default: true only for deepseek-v4-flash-vision-exp
|
|
140
|
+
contextWindow?: number; // default 1_000_000 (deepseek-v4-flash / deepseek-v4-flash-vision-exp / deepseek-v4-pro)
|
|
141
|
+
thinkEffort?: number; // 0-1, see "Think effort" above
|
|
142
|
+
userId?: string; // see "User tracking & prompt caching" above
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Uses the `openai` SDK pointed at `https://api.deepseek.com` (DeepSeek's API is OpenAI-compatible). `getCapabilities().supportsImages` is `false` for every model except `deepseek-v4-flash-vision-exp`, which is `deepseek-v4-flash` plus native image input (same context window, same reasoning support) — pass `supportsImages: true` explicitly if a future model adds vision support before this SDK's allowlist is updated.
|
|
147
|
+
|
|
148
|
+
`stream_options: { include_usage: true }` is set on every streaming call, so `inputMissTokens`/`inputCacheTokens`/`outputTokens` are accurate on both the streaming and non-streaming paths — DeepSeek sends a usage-bearing final chunk (including its own `prompt_cache_hit_tokens`/`prompt_cache_miss_tokens` split) the same way OpenAI does.
|
|
149
|
+
|
|
150
|
+
### Anthropic
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { AnthropicProvider } from "orbitx";
|
|
154
|
+
|
|
155
|
+
new AnthropicProvider(apiKey: string, model = "claude-sonnet-5", options?: {
|
|
156
|
+
supportsTools?: boolean; // default true
|
|
157
|
+
supportsImages?: boolean; // default true
|
|
158
|
+
contextWindow?: number; // default from a per-model table, else 200_000; an explicit value here always wins over extendedContext below
|
|
159
|
+
maxTokens?: number; // output token cap, default 4096
|
|
160
|
+
thinkEffort?: number; // 0-1, see "Think effort" above
|
|
161
|
+
userId?: string; // see "User tracking & prompt caching" above
|
|
162
|
+
disablePromptCaching?: boolean; // default false — prompt caching is on by default, see above
|
|
163
|
+
extendedContext?: boolean | string; // default false/undefined — see "Context window" below
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Wraps `@anthropic-ai/sdk`. `maxTokens` is the only provider with an explicit output-length cap in its options today.
|
|
168
|
+
|
|
169
|
+
### OpenAI
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { OpenAIProvider } from "orbitx";
|
|
173
|
+
|
|
174
|
+
new OpenAIProvider(apiKey: string, model = "gpt-5", options?: {
|
|
175
|
+
supportsTools?: boolean; // default true
|
|
176
|
+
supportsImages?: boolean; // default true unless model is in a small denylist (e.g. o3-mini, o1-mini)
|
|
177
|
+
contextWindow?: number; // default from a per-model table, else 128_000
|
|
178
|
+
baseURL?: string; // point at an OpenAI-compatible endpoint
|
|
179
|
+
thinkEffort?: number; // 0-1, see "Think effort" above — ignored on non-reasoning models
|
|
180
|
+
userId?: string; // see "User tracking & prompt caching" above
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Uses the Chat Completions API (not the Responses API). `baseURL` makes this usable against any OpenAI-compatible provider, not just OpenAI itself.
|
|
185
|
+
|
|
186
|
+
## Provider roles
|
|
187
|
+
|
|
188
|
+
An agent's providers are registered under open-ended role names, not a fixed pair. `"main"` — the provider driving the agent loop itself — is the only one that's required; every other role is optional and looked up on demand by whatever tool needs it.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
export type ProviderType =
|
|
192
|
+
| "main"
|
|
193
|
+
| "image-describer"
|
|
194
|
+
| "image-generation"
|
|
195
|
+
| "image-editing"
|
|
196
|
+
| "video-describer"
|
|
197
|
+
| "video-generation"
|
|
198
|
+
| "video-editing"
|
|
199
|
+
| "audio-describer"
|
|
200
|
+
| "audio-generation"
|
|
201
|
+
| "audio-design"
|
|
202
|
+
| "audio-clone"
|
|
203
|
+
| "3d-model-generator"
|
|
204
|
+
| "llm-low"
|
|
205
|
+
| "llm-medium"
|
|
206
|
+
| "llm-high"
|
|
207
|
+
| (string & {});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`ProviderType` (exported from `orbitx`) is the documented starting vocabulary for every role name — not a closed union, and purely for naming/registry lookup. It has no relationship to `ProviderCapabilities` (see above) — a role name doesn't imply anything about what the provider registered under it can actually do. The trailing `(string & {})` (not bare `string`) is what makes the open-endedness work: a plain `| string` union collapses to just `string` and an editor stops suggesting the literals at all, while intersecting with `{}` keeps TS from widening it away, so a custom name still type-checks the moment nothing here fits. `llm-low`/`medium`/`high` are a self-declared overall quality/durability tier (cheap-and-good-enough vs. worth relying on for longer/harder tasks) — not a specific claim about reasoning power.
|
|
211
|
+
|
|
212
|
+
Pass a single provider to use it as `main` (same as today); pass an array of `{ type, provider, default? }` entries to register more:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { BaseAgent, DeepSeekProvider, OllamaProvider } from "orbitx";
|
|
216
|
+
|
|
217
|
+
const agent = new BaseAgent({
|
|
218
|
+
aiProvider: [
|
|
219
|
+
{ type: "main", provider: new DeepSeekProvider("api-key", "deepseek-v4-flash") },
|
|
220
|
+
{ type: "image-describer", provider: new OllamaProvider("llava:latest") },
|
|
221
|
+
],
|
|
222
|
+
instruction: "You are a helpful assistant.",
|
|
223
|
+
mcpClient,
|
|
224
|
+
allowedTools: [/* ... */],
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`provider` can also be an array — a pool of candidates for that role — with `default` (0-based, defaults to `0`) picking which one is used when code needs exactly one. The rest of the pool is there to be enumerated later; nothing built-in does smarter selection than "the default" yet.
|
|
229
|
+
|
|
230
|
+
### Resolving a provider by role
|
|
231
|
+
|
|
232
|
+
`ProviderRegistry.getProvider(type)` tries an exact role-name match first (that role's pool default). If nothing is registered under `type`, it falls back to scanning every provider registered under *any* role for the first one whose `getCapabilities()` reports the matching capability flag — not a generic capability bag keyed by `ProviderType` (that's gone; `ProviderType` is purely a registry role name and has no relationship to what a model can do), but a fixed, small mapping from a handful of well-known role names to their one corresponding `ProviderCapabilities` flag:
|
|
233
|
+
|
|
234
|
+
| Role (`ProviderType`) | Capability flag checked in the fallback |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `"image-describer"` | `supportsImages` |
|
|
237
|
+
| `"image-generation"` | `supportsImageGeneration` |
|
|
238
|
+
| `"image-editing"` | `supportsImageEditing` |
|
|
239
|
+
| `"video-describer"` | `supportsVideo` |
|
|
240
|
+
| `"video-generation"` | `supportsVideoGeneration` |
|
|
241
|
+
| `"video-editing"` | `supportsVideoEditing` |
|
|
242
|
+
| `"audio-describer"` | `supportsAudio` |
|
|
243
|
+
| `"audio-generation"` | `supportsAudioGeneration` |
|
|
244
|
+
| `"audio-design"` | `supportsAudioDesign` |
|
|
245
|
+
| `"audio-clone"` | `supportsAudioClone` |
|
|
246
|
+
| `"3d-model-generator"` | `supports3DModelGeneration` |
|
|
247
|
+
|
|
248
|
+
This is why a single vision-capable `main` provider "just works" as an image describer with zero extra config: nothing needs to be registered under `"image-describer"` explicitly — the fallback finds `main` because its `getCapabilities().supportsImages` is `true`. Role names outside this table (`"main"`, `"llm-low"`/`"medium"`/`"high"`, or any custom string) have no capability equivalent, so the fallback never applies to them — an unregistered role like that returns `undefined` from `getProvider` regardless of what any registered provider can do.
|
|
249
|
+
|
|
250
|
+
`BaseAgent` calls `getProvider` directly, and so does `SwarmBase` when an agent is part of a swarm (see [Swarm](./swarm.md#providers-a-tool-reaches-for)) — a tool never touches a `ProviderRegistry`, or a raw provider, itself. It reaches this indirectly via `mcp.executeProvider(toolCallId, type, input)` (see [Tools](./tools.md#mcpexecuteprovider--calling-a-provider-from-inside-a-tool)), which works the same way whether the tool is registered on the local `MCPClient` or a sandboxed/remote `MCPServer` — the request is always resolved on `BaseAgent`'s side, never handed across as a live object.
|
|
251
|
+
|
|
252
|
+
`resolveAgentProviders()` (`AgentProvidersInput = AIProvider | AgentProviderEntry[]`) builds the `ProviderRegistry` this all runs on — both are exported from `orbitx` if you need them directly (`getMain()`, `getProvider(type)`, `getProviders(type)` for the full pool under an exact role). There is no separate "utils" role — every tool/skill is included in the system prompt from the start, so there's nothing left for a separate pass to decide.
|
|
253
|
+
|
|
254
|
+
## Writing a custom provider
|
|
255
|
+
|
|
256
|
+
Extend `AIProvider` and implement `chat`/`getCapabilities`. Follow an existing provider (e.g. `src/core/ollama-provider.ts`) as a template: translate `Message[]` to your API's wire format, stream text chunks to `streamCallback` as they arrive, accumulate tool-call deltas and emit them on the final chunk, and translate `ToolSchema[]` into your API's function-calling format (see `src/core/tool-schema-translator.ts` for existing OpenAI/Anthropic translators you can reuse or reference).
|
|
257
|
+
|
|
258
|
+
`inputMissTokens`/`inputCacheTokens`/`outputTokens` on the returned `ChatResponse` are optional — every built-in provider is token-billed and always sets them, but a provider registered under a per-request-billed role (e.g. `image-generation`, `audio-clone`) should omit them and set `cost` instead. `BaseAgent#executeProvider` (see [Tools](./tools.md#mcpexecuteprovider--calling-a-provider-from-inside-a-tool)) checks `cost` first and records a `unit: "cost"` usage entry (stamped with the provider's `getModel()`, like every other entry) when it's present, falling back to the token fields (defaulting any missing one to `0`) otherwise — see [Token accounting](./agents.md#token-accounting).
|
package/docs/skills.md
CHANGED
|
@@ -30,7 +30,6 @@ class Skill {
|
|
|
30
30
|
| `CodeVerificationSkill` | Running the right type checker/build/lint/test suite before handing code back, and telling real bugs apart from sandbox artifacts. |
|
|
31
31
|
| `WebEndToEndTestSkill` | Testing a live web app like a real user would (fill, click, navigate) and verifying via the rendered page, console, and network. |
|
|
32
32
|
| `ResearchSkill` | Answering questions that need real, current information from the live web — cross-checks multiple sources. |
|
|
33
|
-
| `ShoppingSkill` | Purchase-decision help — real current listings, prices, links, and review sentiment instead of recommending from memory. |
|
|
34
33
|
| `PresentSkill` | Deciding how to hand back a job's output as files (individually or zipped) and cleaning build artifacts out first — pairs with `PresentTools`. |
|
|
35
34
|
| `QuestionAnswerSkill` | Pausing to ask the user for missing information, a decision, or confirmation before continuing, instead of guessing — batches every pending question into one call and stops the turn immediately after asking (`QuestionAnswerTool` sets `stopIterationAfterUsingThisTool`). |
|
|
36
35
|
| `QuestionAnswerSkill` | Wraps `QuestionAnswerTool` with instructions for when it's appropriate to stop and ask the human operator a clarifying question. |
|