@bastani/atomic 0.9.19-alpha.5 → 0.9.19-alpha.7
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/CHANGELOG.md +32 -0
- package/README.md +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/broker/client.ts +19 -1
- package/dist/builtin/intercom/index.bundle.mjs +8 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/README.md +3 -1
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +7 -7
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +3 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +2 -1
- package/dist/builtin/web-access/CHANGELOG.md +13 -0
- package/dist/builtin/web-access/README.md +31 -24
- package/dist/builtin/web-access/index.bundle.mjs +16121 -80
- package/dist/builtin/web-access/package.json +2 -1
- package/dist/builtin/workflows/CHANGELOG.md +15 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
- package/dist/builtin/workflows/builtin/{chunk-vhmnzdj9.js → chunk-1jth021m.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-p8jjwfg6.js → chunk-2377r57g.js} +4 -2
- package/dist/builtin/workflows/builtin/{chunk-hbnwv56f.js → chunk-31j2gs7h.js} +2 -2
- package/dist/builtin/workflows/builtin/{chunk-n8zysf1t.js → chunk-ngkqkzej.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-1nbq96fh.js → chunk-nkrafh9s.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-1kx4aggw.js → chunk-wf741bv3.js} +2 -2
- package/dist/builtin/workflows/builtin/{chunk-t6x2d78s.js → chunk-z29qbehr.js} +1 -1
- package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
- package/dist/builtin/workflows/builtin/goal.js +3 -3
- package/dist/builtin/workflows/builtin/index.js +7 -7
- package/dist/builtin/workflows/builtin/ralph.js +3 -3
- package/dist/builtin/workflows/builtin/tournament.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +339 -31
- package/dist/builtin/workflows/src/index.js +67 -19
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -0
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +10 -0
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +3 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +3 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +91 -12
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-coordinator.d.ts +255 -0
- package/dist/core/tools/file-mutation-coordinator.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-coordinator.js +300 -0
- package/dist/core/tools/file-mutation-coordinator.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +14 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +15 -3
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/hashline.d.ts +9 -0
- package/dist/core/tools/hashline.d.ts.map +1 -1
- package/dist/core/tools/hashline.js +12 -1
- package/dist/core/tools/hashline.js.map +1 -1
- package/dist/core/tools/index.d.ts +9 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +67 -12
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/non-minting-snapshot-store.d.ts +49 -0
- package/dist/core/tools/non-minting-snapshot-store.d.ts.map +1 -0
- package/dist/core/tools/non-minting-snapshot-store.js +64 -0
- package/dist/core/tools/non-minting-snapshot-store.js.map +1 -0
- package/dist/core/tools/powershell.d.ts +2 -2
- package/dist/core/tools/powershell.d.ts.map +1 -1
- package/dist/core/tools/powershell.js +2 -5
- package/dist/core/tools/powershell.js.map +1 -1
- package/dist/core/tools/read-document-extract.js.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts +30 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +97 -8
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/task-live-transcript.d.ts +1 -1
- package/dist/modes/interactive/components/task-live-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-live-transcript.js +19 -11
- package/dist/modes/interactive/components/task-live-transcript.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +3 -1
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +28 -4
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +3 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive-engine/activity-watchdog.d.ts +2 -2
- package/dist/modes/interactive-engine/activity-watchdog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/activity-watchdog.js.map +1 -1
- package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts +3 -1
- package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-diagnostic-view.js +11 -0
- package/dist/modes/interactive-engine/engine-diagnostic-view.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +2 -1
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/rpc/rpc-client-process.d.ts +2 -0
- package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-process.js +23 -1
- package/dist/modes/rpc/rpc-client-process.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +20 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/utils/markit.d.ts +2 -0
- package/dist/utils/markit.d.ts.map +1 -1
- package/dist/utils/markit.js +65 -5
- package/dist/utils/markit.js.map +1 -1
- package/docs/background-tasks.md +15 -11
- package/docs/build.md +51 -0
- package/docs/changelog.mdx +8 -8
- package/docs/compaction/reference.md +267 -0
- package/docs/compaction.md +36 -219
- package/docs/computer-use.md +35 -0
- package/docs/containerization.md +9 -5
- package/docs/custom-provider/api-reference.md +145 -0
- package/docs/custom-provider/oauth.md +131 -0
- package/docs/custom-provider/override.md +34 -0
- package/docs/custom-provider/registration.md +163 -0
- package/docs/custom-provider/streaming.md +214 -0
- package/docs/custom-provider.md +43 -607
- package/docs/development.md +87 -0
- package/docs/docs.json +199 -76
- package/docs/environment-variables.md +1 -1
- package/docs/extensions/api-reference.md +1001 -0
- package/docs/extensions/authoring.md +676 -0
- package/docs/extensions/events.md +793 -0
- package/docs/extensions/examples.md +94 -0
- package/docs/extensions/ui.md +470 -0
- package/docs/extensions.md +270 -2768
- package/docs/getting-started/authentication.md +55 -0
- package/docs/getting-started/first-session.md +172 -0
- package/docs/getting-started/installation.md +154 -0
- package/docs/getting-started/project-instructions.md +43 -0
- package/docs/guides/configuration.md +65 -0
- package/docs/guides/intercom.md +24 -0
- package/docs/guides/non-interactive.md +33 -0
- package/docs/guides/subagents.md +24 -0
- package/docs/guides/workflows.md +27 -0
- package/docs/guides.md +41 -0
- package/docs/index.md +13 -45
- package/docs/intercom/operations.md +178 -0
- package/docs/intercom/reference.md +146 -0
- package/docs/intercom.md +54 -259
- package/docs/json.md +17 -0
- package/docs/llama-cpp.md +5 -0
- package/docs/models/artificial-analysis-index.md +89 -0
- package/docs/models/evals.md +2 -2
- package/docs/models/model-selection.md +3 -3
- package/docs/models/pareto-efficiency.md +2 -2
- package/docs/models/reference.md +601 -0
- package/docs/models.md +45 -550
- package/docs/packages/authoring.md +88 -0
- package/docs/packages/reference.md +43 -0
- package/docs/packages.md +28 -107
- package/docs/programmatic.md +29 -0
- package/docs/prompt-templates.md +11 -0
- package/docs/providers/reference.md +23 -0
- package/docs/providers.md +10 -15
- package/docs/quickstart.md +44 -238
- package/docs/reference/cli.md +252 -0
- package/docs/reference.md +52 -0
- package/docs/rpc/examples.md +64 -0
- package/docs/rpc/extension-ui.md +198 -0
- package/docs/rpc/protocol.md +1341 -0
- package/docs/rpc.md +109 -1346
- package/docs/sdk/reference.md +1090 -0
- package/docs/sdk.md +56 -1002
- package/docs/session-format.md +2 -0
- package/docs/sessions.md +6 -10
- package/docs/settings.md +6 -45
- package/docs/skills/authoring.md +95 -0
- package/docs/skills/reference.md +60 -0
- package/docs/skills.md +20 -122
- package/docs/subagents/authoring.md +37 -0
- package/docs/subagents/reference.md +60 -0
- package/docs/subagents.md +42 -96
- package/docs/terminal-setup.md +1 -1
- package/docs/themes/reference.md +176 -0
- package/docs/themes.md +21 -135
- package/docs/tmux.md +5 -0
- package/docs/tools/edit.md +5 -0
- package/docs/tools.md +31 -0
- package/docs/tui/reference.md +343 -0
- package/docs/tui.md +23 -286
- package/docs/usage.md +26 -209
- package/docs/web-access.md +40 -0
- package/docs/windows.md +2 -1
- package/docs/workflows/api-reference.md +11 -4
- package/docs/workflows/authoring.md +4 -2
- package/docs/workflows/builtins.md +2 -2
- package/docs/workflows/operations.md +11 -7
- package/docs/workflows/reliable-design.md +21 -3
- package/docs/workflows/verification.md +8 -0
- package/examples/extensions/gondolin/index.ts +116 -148
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/ssh.ts +56 -34
- package/npm-shrinkwrap.json +33 -33
- package/package.json +4 -4
package/docs/containerization.md
CHANGED
|
@@ -6,11 +6,13 @@ There are two general options. You can either
|
|
|
6
6
|
1. run the whole `atomic` process inside an isolated environment, or
|
|
7
7
|
2. run `atomic` on the host and route tool execution into an isolated environment.
|
|
8
8
|
|
|
9
|
+
Containerization is the outer boundary, not the only one. [Security](/security) covers the project-trust prompt that gates which project-scoped extensions, skills, and settings load in the first place; read it alongside this page when you are deciding what an untrusted repository is allowed to do.
|
|
10
|
+
|
|
9
11
|
## Choose a pattern
|
|
10
12
|
|
|
11
13
|
| Pattern | What is isolated | Best for | Notes |
|
|
12
14
|
| --- | --- | --- | --- |
|
|
13
|
-
| Gondolin extension |
|
|
15
|
+
| Gondolin extension | Selected file tools, shell execution, and `!` commands | Local micro-VM tool routing while keeping auth on host | Other tools, including `search`, remain on the host. See [`examples/extensions/gondolin/`](https://github.com/bastani-inc/atomic/tree/main/packages/coding-agent/examples/extensions/gondolin). |
|
|
14
16
|
| Plain Docker | Whole `atomic` process in a local container | Simple local isolation | Provider API keys enter the container. |
|
|
15
17
|
| OpenShell | Whole `atomic` process in a policy-controlled sandbox | Local or remote managed sandbox | Requires an OpenShell gateway. |
|
|
16
18
|
|
|
@@ -19,14 +21,14 @@ Extensions run wherever the `atomic` process runs. If you run host `atomic` with
|
|
|
19
21
|
## Gondolin
|
|
20
22
|
|
|
21
23
|
[Gondolin](https://github.com/earendil-works/gondolin) is a local Linux micro-VM.
|
|
22
|
-
Use the [example extension](https://github.com/bastani-inc/atomic/tree/main/packages/coding-agent/examples/extensions/gondolin) when you want `atomic` on the host
|
|
24
|
+
Use the [example extension](https://github.com/bastani-inc/atomic/tree/main/packages/coding-agent/examples/extensions/gondolin) when you want `atomic` on the host with selected file tools and shell execution routed into the VM. This is not isolation for the entire session.
|
|
23
25
|
|
|
24
26
|
Setup:
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
29
|
cp -R packages/coding-agent/examples/extensions/gondolin ~/.atomic/agent/extensions/gondolin
|
|
28
30
|
cd ~/.atomic/agent/extensions/gondolin
|
|
29
|
-
|
|
31
|
+
npm ci --ignore-scripts
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
Run from the project you want mounted:
|
|
@@ -36,11 +38,13 @@ cd /path/to/project
|
|
|
36
38
|
atomic -e ~/.atomic/agent/extensions/gondolin
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
The extension mounts the host cwd at `/workspace` in the VM and overrides `read`, `write`, `edit`, `bash`, `find`, and `
|
|
41
|
+
The extension mounts the host cwd at `/workspace` in the VM and overrides `read`, `write`, `edit`, `bash`, `find`, and `ls`.
|
|
40
42
|
User `!` commands are routed into the VM, as well.
|
|
41
43
|
File changes under `/workspace` write through to the host.
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
`search` remains a host tool; it is not redirected into the VM. The removed `grep` tool is not registered. For guest-only content searches, use a shell command through the routed `bash` tool or `!` command. To expose only these routed tools, start with `atomic --tools read,write,edit,bash,find,ls -e ~/.atomic/agent/extensions/gondolin`; other loaded extensions can still supply host-side tools. Use whole-process isolation instead when host filesystem access must be prevented.
|
|
46
|
+
|
|
47
|
+
Requirements: npm for dependency installation, Node.js >= 23.6.0 for `@earendil-works/gondolin`, plus QEMU (requires installation through your package manager).
|
|
44
48
|
|
|
45
49
|
## Plain Docker
|
|
46
50
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Provider API reference
|
|
3
|
+
description: Provider config and model definition contracts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Provider API reference
|
|
7
|
+
|
|
8
|
+
## Config Reference
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
interface ProviderConfig {
|
|
12
|
+
/** Display name for the provider in UI such as /login. */
|
|
13
|
+
name?: string;
|
|
14
|
+
|
|
15
|
+
/** API endpoint URL. Required when defining models. */
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
|
|
18
|
+
/** API key literal or config value (for env vars use "$ENV_VAR" or "${ENV_VAR}"). Required when defining models (unless oauth). */
|
|
19
|
+
apiKey?: string;
|
|
20
|
+
|
|
21
|
+
/** API type for streaming. Required at provider or model level when defining models. */
|
|
22
|
+
api?: Api;
|
|
23
|
+
|
|
24
|
+
/** Custom streaming implementation for non-standard APIs. */
|
|
25
|
+
streamSimple?: (
|
|
26
|
+
model: Model<Api>,
|
|
27
|
+
context: Context,
|
|
28
|
+
options?: SimpleStreamOptions
|
|
29
|
+
) => AssistantMessageEventStream;
|
|
30
|
+
|
|
31
|
+
/** Custom headers to include in requests. Values use the same config-value syntax as apiKey. */
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
|
|
34
|
+
/** If true, adds Authorization: Bearer header with the resolved API key. */
|
|
35
|
+
authHeader?: boolean;
|
|
36
|
+
|
|
37
|
+
/** Models to register. If provided, replaces all existing models for this provider. */
|
|
38
|
+
models?: ProviderModelConfig[];
|
|
39
|
+
|
|
40
|
+
/** OAuth provider for /login support. */
|
|
41
|
+
oauth?: {
|
|
42
|
+
name: string;
|
|
43
|
+
login(callbacks: OAuthLoginCallbacks, signal: AbortSignal): Promise<OAuthCredentials>;
|
|
44
|
+
refreshToken(credentials: OAuthCredentials, signal: AbortSignal | undefined): Promise<OAuthCredentials>;
|
|
45
|
+
getApiKey(credentials: OAuthCredentials): string;
|
|
46
|
+
modifyModels?(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[];
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Model Definition Reference
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
interface ProviderModelConfig {
|
|
55
|
+
/** Model ID (e.g., "claude-sonnet-4-5"). */
|
|
56
|
+
id: string;
|
|
57
|
+
|
|
58
|
+
/** Display name (e.g., "Claude Sonnet 4.5"). */
|
|
59
|
+
name: string;
|
|
60
|
+
|
|
61
|
+
/** API type override for this specific model. */
|
|
62
|
+
api?: Api;
|
|
63
|
+
|
|
64
|
+
/** API endpoint URL override for this specific model. */
|
|
65
|
+
baseUrl?: string;
|
|
66
|
+
|
|
67
|
+
/** Whether the model supports extended thinking. */
|
|
68
|
+
reasoning: boolean;
|
|
69
|
+
|
|
70
|
+
/** Maps Atomic thinking levels to provider/model-specific values; null marks a level unsupported. */
|
|
71
|
+
thinkingLevelMap?: Partial<Record<"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", string | null>>;
|
|
72
|
+
|
|
73
|
+
/** Supported input types. */
|
|
74
|
+
input: ("text" | "image")[];
|
|
75
|
+
|
|
76
|
+
/** Base cost per million tokens plus optional request-wide long-context tiers. */
|
|
77
|
+
cost: {
|
|
78
|
+
input: number;
|
|
79
|
+
output: number;
|
|
80
|
+
cacheRead: number;
|
|
81
|
+
cacheWrite: number;
|
|
82
|
+
tiers?: Array<{
|
|
83
|
+
/** Tier applies only when input + cacheRead + cacheWrite strictly exceeds this value. */
|
|
84
|
+
inputTokensAbove: number;
|
|
85
|
+
input: number;
|
|
86
|
+
output: number;
|
|
87
|
+
cacheRead: number;
|
|
88
|
+
cacheWrite: number;
|
|
89
|
+
}>;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/** Default/effective context window size in tokens. */
|
|
93
|
+
contextWindow: number;
|
|
94
|
+
|
|
95
|
+
/** Maximum output tokens. */
|
|
96
|
+
maxTokens: number;
|
|
97
|
+
/** Default sampling parameters merged into OpenAI-compatible request bodies. */
|
|
98
|
+
samplingParams?: Record<string, unknown>;
|
|
99
|
+
|
|
100
|
+
/** Custom headers for this specific model. */
|
|
101
|
+
headers?: Record<string, string>;
|
|
102
|
+
|
|
103
|
+
/** API-specific provider compatibility settings. */
|
|
104
|
+
compat?: {
|
|
105
|
+
supportsStore?: boolean;
|
|
106
|
+
supportsDeveloperRole?: boolean;
|
|
107
|
+
supportsReasoningEffort?: boolean;
|
|
108
|
+
supportsUsageInStreaming?: boolean;
|
|
109
|
+
supportsFinishReason?: boolean;
|
|
110
|
+
supportsThinkingTokenBudget?: boolean;
|
|
111
|
+
supportsStrictMode?: boolean;
|
|
112
|
+
supportsOpenAIGrammarTools?: boolean;
|
|
113
|
+
/** Atomic alias for supportsOpenAIGrammarTools. */
|
|
114
|
+
supportsGrammarTools?: boolean;
|
|
115
|
+
maxTokensField?: "max_completion_tokens" | "max_tokens";
|
|
116
|
+
requiresToolResultName?: boolean;
|
|
117
|
+
requiresAssistantAfterToolResult?: boolean;
|
|
118
|
+
requiresThinkingAsText?: boolean;
|
|
119
|
+
requiresReasoningContentOnAssistantMessages?: boolean;
|
|
120
|
+
thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "baseten" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
|
|
121
|
+
supportsStrictTools?: boolean;
|
|
122
|
+
chatTemplateKwargs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort" | "thinking.budget"; omitWhenOff?: boolean }>;
|
|
123
|
+
chatTemplateArgs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort" | "thinking.budget"; omitWhenOff?: boolean }>;
|
|
124
|
+
thinkingTokenBudgetField?: "thinking_token_budget" | "thinking_budget" | "thinking_budget_tokens";
|
|
125
|
+
cacheControlFormat?: "anthropic";
|
|
126
|
+
sendSessionAffinityHeaders?: boolean;
|
|
127
|
+
sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter";
|
|
128
|
+
supportsLongCacheRetention?: boolean;
|
|
129
|
+
vllmPriority?: number;
|
|
130
|
+
supportsToolSearch?: boolean;
|
|
131
|
+
supportsMaxOutputTokens?: boolean;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The `cost` shape is equivalent to `Model<Api>["cost"]`. Base rates and every tier are complete rate sets. When multiple thresholds match, `calculateCost()` uses the highest threshold and applies that tier to all four cost buckets for the request.
|
|
137
|
+
|
|
138
|
+
`openrouter` sends `reasoning: { effort }`. `deepseek` sends `thinking: { type: "enabled" | "disabled" }` and `reasoning_effort` when enabled. `together` sends `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`. Use `chat-template` for configurable `chat_template_kwargs`, for example DeepSeek V3.x behind vLLM with `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }`. Use `thinkingFormat: "baseten"` with `chatTemplateArgs` when the provider expects toggle values under `chat_template_args` and optionally supports top-level `reasoning_effort`.
|
|
139
|
+
`thinkingTokenBudgetField` sends a clamped per-level thinking budget as a top-level request field (`thinking_token_budget` on vLLM, `thinking_budget` on Qwen/SGLang, `thinking_budget_tokens` on llama.cpp). `supportsThinkingTokenBudget: true` is an alias for the vLLM field name. Do not combine it with `reasoning_effort` on DashScope Qwen models.
|
|
140
|
+
`vllmPriority` sends a top-level `priority` request field for `openai-completions` providers. Lower values are scheduled earlier and the vLLM server default is `0`, so it only takes effect when vLLM runs with `--scheduling-policy priority`. Set it on a background or batch model so its long prefills queue behind interactive sessions. Unset by default and never set on the generated catalog.
|
|
141
|
+
`cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user, assistant, or tool-result text content.
|
|
142
|
+
|
|
143
|
+
Capability flags are enforcement claims, not preferences. `supportsStrictMode` controls strict JSON-schema tools for OpenAI-compatible APIs; Anthropic/Bedrock use `supportsStrictTools`; `supportsOpenAIGrammarTools` controls OpenAI Lark/regex custom tools. Atomic also accepts `supportsGrammarTools` as a compatibility alias and synchronizes it to the canonical OpenAI name; when both disagree, the canonical field wins. Leave these fields unset/false unless the endpoint and selected model actually preserve and enforce the corresponding request shape. See [Extensions](/extensions/authoring#constrained-sampling) for exact `constrainedSampling` modes.
|
|
144
|
+
|
|
145
|
+
For `openai-responses` providers, set `compat.sessionAffinityFormat` to `"openai"` for `session_id` plus `x-client-request-id`, `"openai-nosession"` to omit `session_id` while retaining `x-client-request-id`, or `"openrouter"` for `x-session-id`. Responses-compatible providers may also set `supportsToolSearch` when they support deferred tool loading. `supportsMaxOutputTokens` defaults to `true`; set it to `false` for OpenAI Responses-compatible gateways such as Codex-protocol proxies that reject `max_output_tokens` with a 400, and Atomic omits the parameter from those requests.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Provider OAuth
|
|
3
|
+
description: OAuth login callbacks, credential storage, and dynamic catalog refresh.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Provider OAuth
|
|
7
|
+
|
|
8
|
+
## OAuth Support
|
|
9
|
+
|
|
10
|
+
Add OAuth/SSO authentication that integrates with `/login`:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import type { OAuthCredentials, OAuthLoginCallbacks } from "@bastani/pi-ai";
|
|
14
|
+
|
|
15
|
+
pi.registerProvider("corporate-ai", {
|
|
16
|
+
baseUrl: "https://ai.corp.com/v1",
|
|
17
|
+
api: "openai-responses",
|
|
18
|
+
models: [...],
|
|
19
|
+
oauth: {
|
|
20
|
+
name: "Corporate AI (SSO)",
|
|
21
|
+
|
|
22
|
+
async login(callbacks: OAuthLoginCallbacks, signal: AbortSignal): Promise<OAuthCredentials> {
|
|
23
|
+
// Option 1: Browser-based OAuth
|
|
24
|
+
callbacks.onAuth({ url: "https://sso.corp.com/authorize?..." });
|
|
25
|
+
|
|
26
|
+
// Option 2: Device code flow
|
|
27
|
+
callbacks.onDeviceCode({
|
|
28
|
+
userCode: "ABCD-1234",
|
|
29
|
+
verificationUri: "https://sso.corp.com/device"
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Option 3: Prompt for token/code
|
|
33
|
+
const code = await callbacks.onPrompt({ message: "Enter SSO code:" });
|
|
34
|
+
|
|
35
|
+
// Exchange for tokens (your implementation). Forward `signal` so
|
|
36
|
+
// cancelling /login aborts the in-flight network request.
|
|
37
|
+
const tokens = await exchangeCodeForTokens(code, { signal });
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
refresh: tokens.refreshToken,
|
|
41
|
+
access: tokens.accessToken,
|
|
42
|
+
expires: Date.now() + tokens.expiresIn * 1000
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async refreshToken(
|
|
47
|
+
credentials: OAuthCredentials,
|
|
48
|
+
signal: AbortSignal | undefined
|
|
49
|
+
): Promise<OAuthCredentials> {
|
|
50
|
+
const tokens = await refreshAccessToken(credentials.refresh, { signal });
|
|
51
|
+
return {
|
|
52
|
+
refresh: tokens.refreshToken ?? credentials.refresh,
|
|
53
|
+
access: tokens.accessToken,
|
|
54
|
+
expires: Date.now() + tokens.expiresIn * 1000
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
getApiKey(credentials: OAuthCredentials): string {
|
|
59
|
+
return credentials.access;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Optional: modify models based on user's subscription
|
|
63
|
+
modifyModels(models, credentials) {
|
|
64
|
+
const region = decodeRegionFromToken(credentials.access);
|
|
65
|
+
return models.map(m => ({
|
|
66
|
+
...m,
|
|
67
|
+
baseUrl: `https://${region}.ai.corp.com/v1`
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
After registration, users can authenticate via `/login corporate-ai`.
|
|
75
|
+
|
|
76
|
+
Existing extension OAuth definitions keep their `login`, `refreshToken`, `getApiKey`, and optional `modifyModels` methods. OAuth refresh is serialized so concurrent requests do not overwrite each other's credentials.
|
|
77
|
+
|
|
78
|
+
In isolated interactive mode, extension code and executable OAuth methods remain in the engine process. Atomic transports only the JSON-safe provider description (`id`, `name`, `loginLabel`, and `usesCallbackServer`) to the terminal process; it never serializes provider functions or acquired credentials and does not load the extension a second time in the frontend. `loginLabel` replaces the login dialog title, while `usesCallbackServer: true` exposes a redirect-URL paste field that races the browser callback. The engine executes the provider's login closure and correlates browser URLs, device codes, progress/info messages, prompts, selections, and manual-code callbacks with the originating login.
|
|
79
|
+
|
|
80
|
+
After acquisition, the engine owns serialized credential persistence and logout. It publishes the authenticated provider against its already-loaded snapshot as soon as persistence succeeds; dynamic catalog and ambient-availability refreshes run separately under the model selector's deadline and never extend the login transaction. Logout similarly publishes stored-credential removal without invoking `refreshModels`; Atomic gives the provider's local remaining-auth probe a short deadline so extension code cannot keep the dialog open. The frontend applies the returned snapshot only after the engine transaction succeeds. Escape or Ctrl+C cancels only the matching login and leaves the prior credential/catalog intact. Built-in OAuth and direct, non-isolated extension OAuth use the same persistence and cancellation semantics; later provider registrations continue to override earlier registrations by ID.
|
|
81
|
+
|
|
82
|
+
Intentional cancellation is quiet, including native `AbortError`, an aborted signal or its exact reason, nested abort causes, and the legacy exact `Login cancelled` error. Provider denial, timeout, network/protocol errors, malformed responses, token exchange failures, and storage failures remain visible. Catalog-refresh failures are reported by `/model` while cached models remain selectable; they do not turn a persisted login into a failed transaction.
|
|
83
|
+
|
|
84
|
+
## Dynamic model catalog refresh
|
|
85
|
+
|
|
86
|
+
Providers whose catalogs change at runtime can add `refreshModels`. Atomic calls it during the model picker's bounded asynchronous refresh, independently of authentication completion:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
pi.registerProvider("corporate-ai", {
|
|
90
|
+
baseUrl: "https://ai.corp.com/v1",
|
|
91
|
+
api: "openai-responses",
|
|
92
|
+
apiKey: "$CORPORATE_AI_KEY",
|
|
93
|
+
models: cachedModels,
|
|
94
|
+
async refreshModels({ signal, force, credential, store }) {
|
|
95
|
+
const models = await fetchCorporateModels({ signal, force, credential });
|
|
96
|
+
await store.write({ models, checkedAt: Date.now() });
|
|
97
|
+
return models;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The current catalog stays readable while refresh is pending. Successful provider results are applied independently; a provider that fails, times out, or observes an aborted `signal` retains its previous list. Use the provider-scoped `store` only when the catalog should persist across sessions.
|
|
103
|
+
|
|
104
|
+
### OAuthLoginCallbacks
|
|
105
|
+
|
|
106
|
+
The `callbacks` object provides three ways to authenticate:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
interface OAuthLoginCallbacks {
|
|
110
|
+
// Open URL in browser (for OAuth redirects)
|
|
111
|
+
onAuth(params: { url: string }): void;
|
|
112
|
+
|
|
113
|
+
// Show device code (for device authorization flow)
|
|
114
|
+
onDeviceCode(params: { userCode: string; verificationUri: string }): void;
|
|
115
|
+
|
|
116
|
+
// Prompt user for input (for manual token entry)
|
|
117
|
+
onPrompt(params: { message: string }): Promise<string>;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### OAuthCredentials
|
|
122
|
+
|
|
123
|
+
Credentials are persisted in `~/.atomic/agent/auth.json` (legacy `~/.pi/agent/auth.json` may be read for compatibility):
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
interface OAuthCredentials {
|
|
127
|
+
refresh: string; // Refresh token (for refreshToken())
|
|
128
|
+
access: string; // Access token (returned by getApiKey())
|
|
129
|
+
expires: number; // Expiration timestamp in milliseconds
|
|
130
|
+
}
|
|
131
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Override an existing provider
|
|
3
|
+
description: Change the behavior of a provider Atomic already ships.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Override an existing provider
|
|
7
|
+
|
|
8
|
+
## Override Existing Provider
|
|
9
|
+
|
|
10
|
+
The simplest use case: redirect an existing provider through a proxy.
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// All Anthropic requests now go through your proxy
|
|
14
|
+
pi.registerProvider("anthropic", {
|
|
15
|
+
baseUrl: "https://proxy.example.com"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Add custom headers to OpenAI requests
|
|
19
|
+
pi.registerProvider("openai", {
|
|
20
|
+
headers: {
|
|
21
|
+
"X-Custom-Header": "value"
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Both baseUrl and headers
|
|
26
|
+
pi.registerProvider("google", {
|
|
27
|
+
baseUrl: "https://ai-gateway.corp.com/google",
|
|
28
|
+
headers: {
|
|
29
|
+
"X-Corp-Auth": "$CORP_AUTH_TOKEN" // resolves from env; omit $ for a literal
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
When only `baseUrl` and/or `headers` are provided (no `models`), all existing models for that provider are preserved with the new endpoint.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Register a provider
|
|
3
|
+
description: Register and unregister a provider, and the API types a provider implements.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Register a provider
|
|
7
|
+
|
|
8
|
+
## Register New Provider
|
|
9
|
+
|
|
10
|
+
To add a completely new provider, specify `models` along with the required configuration.
|
|
11
|
+
|
|
12
|
+
If the model list comes from a remote endpoint, use an async extension factory:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import type { ExtensionAPI } from "@bastani/atomic";
|
|
16
|
+
|
|
17
|
+
export default async function (pi: ExtensionAPI) {
|
|
18
|
+
const response = await fetch("http://localhost:1234/v1/models");
|
|
19
|
+
const payload = (await response.json()) as {
|
|
20
|
+
data: Array<{
|
|
21
|
+
id: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
context_window?: number;
|
|
24
|
+
max_tokens?: number;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
pi.registerProvider("local-openai", {
|
|
29
|
+
baseUrl: "http://localhost:1234/v1",
|
|
30
|
+
apiKey: "$LOCAL_OPENAI_API_KEY",
|
|
31
|
+
api: "openai-completions",
|
|
32
|
+
models: payload.data.map((model) => ({
|
|
33
|
+
id: model.id,
|
|
34
|
+
name: model.name ?? model.id,
|
|
35
|
+
reasoning: false,
|
|
36
|
+
input: ["text"],
|
|
37
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
38
|
+
contextWindow: model.context_window ?? 128000,
|
|
39
|
+
maxTokens: model.max_tokens ?? 4096,
|
|
40
|
+
})),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This registers the fetched models before startup finishes.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
pi.registerProvider("my-llm", {
|
|
49
|
+
baseUrl: "https://api.my-llm.com/v1",
|
|
50
|
+
apiKey: "$MY_LLM_API_KEY", // env var reference; omit $ for a literal value
|
|
51
|
+
api: "openai-completions", // which streaming API to use
|
|
52
|
+
models: [
|
|
53
|
+
{
|
|
54
|
+
id: "my-llm-large",
|
|
55
|
+
name: "My LLM Large",
|
|
56
|
+
reasoning: true, // supports extended thinking
|
|
57
|
+
input: ["text", "image"],
|
|
58
|
+
cost: {
|
|
59
|
+
input: 3.0, // $/million tokens
|
|
60
|
+
output: 15.0,
|
|
61
|
+
cacheRead: 0.3,
|
|
62
|
+
cacheWrite: 3.75
|
|
63
|
+
},
|
|
64
|
+
contextWindow: 200000,
|
|
65
|
+
maxTokens: 16384
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
When `models` is provided, it **replaces** all existing models for that provider.
|
|
72
|
+
|
|
73
|
+
## Unregister Provider
|
|
74
|
+
|
|
75
|
+
Use `pi.unregisterProvider(name)` to remove a provider that was previously registered via `pi.registerProvider(name, ...)`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Register
|
|
79
|
+
pi.registerProvider("my-llm", {
|
|
80
|
+
baseUrl: "https://api.my-llm.com/v1",
|
|
81
|
+
apiKey: "$MY_LLM_API_KEY",
|
|
82
|
+
api: "openai-completions",
|
|
83
|
+
models: [
|
|
84
|
+
{
|
|
85
|
+
id: "my-llm-large",
|
|
86
|
+
name: "My LLM Large",
|
|
87
|
+
reasoning: true,
|
|
88
|
+
input: ["text", "image"],
|
|
89
|
+
cost: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
90
|
+
contextWindow: 200000,
|
|
91
|
+
maxTokens: 16384
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Later, remove it
|
|
97
|
+
pi.unregisterProvider("my-llm");
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Unregistering removes that provider's dynamic models, API key fallback, OAuth provider registration, and custom stream handler registrations. Any built-in models or provider behavior that were overridden are restored.
|
|
101
|
+
|
|
102
|
+
Calls made after the initial extension load phase are applied immediately, so no `/reload` is required.
|
|
103
|
+
|
|
104
|
+
### API Types
|
|
105
|
+
|
|
106
|
+
The `api` field determines which streaming implementation is used:
|
|
107
|
+
|
|
108
|
+
| API | Use for |
|
|
109
|
+
|-----|---------|
|
|
110
|
+
| `anthropic-messages` | Anthropic Claude API and compatibles |
|
|
111
|
+
| `openai-completions` | OpenAI Chat Completions API and compatibles |
|
|
112
|
+
| `openai-responses` | OpenAI Responses API |
|
|
113
|
+
| `azure-openai-responses` | Azure OpenAI Responses API |
|
|
114
|
+
| `openai-codex-responses` | OpenAI Codex Responses API |
|
|
115
|
+
| `mistral-conversations` | Native Mistral Chat Completions streaming |
|
|
116
|
+
| `google-generative-ai` | Google Generative AI API |
|
|
117
|
+
| `google-vertex` | Google Vertex AI API |
|
|
118
|
+
| `bedrock-converse-stream` | Amazon Bedrock Converse API |
|
|
119
|
+
|
|
120
|
+
Most OpenAI-compatible providers work with `openai-completions`. Use model-level `thinkingLevelMap` for model-specific thinking levels, and `compat` for provider quirks:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
models: [{
|
|
124
|
+
id: "custom-model",
|
|
125
|
+
// ...
|
|
126
|
+
reasoning: true,
|
|
127
|
+
thinkingLevelMap: { // map Atomic thinking levels to provider values; null hides unsupported levels
|
|
128
|
+
minimal: null,
|
|
129
|
+
low: null,
|
|
130
|
+
medium: null,
|
|
131
|
+
high: "default",
|
|
132
|
+
xhigh: null,
|
|
133
|
+
max: "max"
|
|
134
|
+
},
|
|
135
|
+
compat: {
|
|
136
|
+
supportsDeveloperRole: false, // use "system" instead of "developer"
|
|
137
|
+
supportsReasoningEffort: true,
|
|
138
|
+
maxTokensField: "max_tokens", // instead of "max_completion_tokens"
|
|
139
|
+
requiresToolResultName: true, // tool results need name field
|
|
140
|
+
thinkingFormat: "qwen", // top-level enable_thinking: true
|
|
141
|
+
cacheControlFormat: "anthropic" // Anthropic-style cache_control markers
|
|
142
|
+
}
|
|
143
|
+
}]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use `openrouter` for OpenRouter-style `reasoning: { effort }` controls. Use `together` for Together-style `reasoning: { enabled }` controls; with `supportsReasoningEffort`, it also sends `reasoning_effort`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`.
|
|
147
|
+
Use `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` on the system prompt, last tool definition, and last user/assistant text content.
|
|
148
|
+
|
|
149
|
+
Use `mistral-conversations` for native Mistral models. If you intentionally route a Mistral-compatible or custom endpoint through `openai-completions`, set the required `compat` flags explicitly.
|
|
150
|
+
|
|
151
|
+
### Auth Header
|
|
152
|
+
|
|
153
|
+
If your provider expects `Authorization: Bearer <key>` but doesn't use a standard API, set `authHeader: true`:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
pi.registerProvider("custom-api", {
|
|
157
|
+
baseUrl: "https://api.example.com",
|
|
158
|
+
apiKey: "$MY_API_KEY",
|
|
159
|
+
authHeader: true, // adds Authorization: Bearer header
|
|
160
|
+
api: "openai-completions",
|
|
161
|
+
models: [...]
|
|
162
|
+
});
|
|
163
|
+
```
|