opencode-agy-bridge 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,133 +23,81 @@ opencode TUI
23
23
 
24
24
  ## Installation
25
25
 
26
- ### 1. Automatic Installation (Recommended)
26
+ ### Automatic (hands-free)
27
27
 
28
- If you are using OpenCode's built-in plugin manager, **you do not need to run any commands** in your terminal. OpenCode can automatically fetch and install the package from npm if you specify the package name with its version in your `opencode.json` configuration file.
28
+ Add the package with its version to `~/.config/opencode/opencode.json`. OpenCode will download and resolve it from npm automatically no terminal commands needed.
29
29
 
30
- See the [Configuration](#configuration) section below for details.
31
-
32
- ### 2. Manual Global Installation
33
-
34
- If you prefer to install it globally yourself using a package manager:
30
+ ### Manual global install
35
31
 
36
32
  ```bash
37
- # Using npm
38
33
  npm install -g opencode-agy-bridge
39
-
40
- # Using Bun
41
- bun install -g opencode-agy-bridge
42
-
43
- # Using pnpm
44
- pnpm add -g opencode-agy-bridge
34
+ # or: bun install -g opencode-agy-bridge
35
+ # or: pnpm add -g opencode-agy-bridge
45
36
  ```
46
37
 
47
- ### 3. Local Development Installation
48
-
49
- If you prefer to build from source:
38
+ ### Build from source
50
39
 
51
40
  ```bash
52
41
  git clone https://github.com/raultov/opencode-agy-bridge.git
53
42
  cd opencode-agy-bridge
54
-
55
- # Using Bun (recommended)
56
- bun install
57
- bun run build
58
- bun test # verify all tests pass
59
-
60
- # Or using pnpm/npm
61
- pnpm install && pnpm run build && pnpm test
43
+ bun install && bun run build && bun test
62
44
  ```
63
45
 
64
- ## Features
65
-
66
- - **Robust Delta Extraction:** Automatically normalizes `\r\n` (CRLF) and `\n` (LF) line endings, tolerates trailing whitespace/newline differences, and implements suffix-based alignment to support seamless recovery during context window truncation.
67
-
68
46
  ## Configuration
69
47
 
70
- Add the plugin and provider to your `~/.config/opencode/opencode.json` configuration file:
48
+ Add the plugin and provider to `~/.config/opencode/opencode.json`.
71
49
 
72
- ### For Automatic Installation (Hands-free)
50
+ > The **node.js** path represents a **package** (directory or npm package name), not a `.js` file. Pointing `"npm"` at a `.js` file will cause a `ProviderInitError` because opencode internally appends `/provider` to resolve the exports map.
73
51
 
74
- Just add the package directly with the desired version tag. OpenCode will download and resolve it automatically from npm:
52
+ ### Recommended: automatic from npm
75
53
 
76
54
  ```jsonc
77
55
  {
78
56
  "plugin": [
79
- // ...your existing plugins...
80
- "opencode-agy-bridge@0.2.1"
57
+ "opencode-agy-bridge@0.2.3"
81
58
  ],
82
59
  "provider": {
83
- // ...your existing providers...
84
60
  "agy": {
85
- "npm": "opencode-agy-bridge/provider",
61
+ "npm": "opencode-agy-bridge",
86
62
  "name": "Google Antigravity (via agy CLI)",
87
- "options": {
88
- "binary": "agy",
89
- "timeoutMs": 300000
90
- },
91
- "models": {
92
- "antigravity": {
93
- "name": "Antigravity (server-selected Gemini)"
94
- }
95
- }
63
+ "options": { "binary": "agy", "timeoutMs": 300000 },
64
+ "models": { "antigravity": { "name": "Antigravity (server-selected Gemini)" } }
96
65
  }
97
66
  }
98
67
  }
99
68
  ```
100
69
 
101
- ### For Manual Global Installation
70
+ ### Using npm global install path
102
71
 
103
72
  ```jsonc
104
73
  {
105
74
  "plugin": [
106
- // ...your existing plugins...
107
75
  "opencode-agy-bridge"
108
76
  ],
109
77
  "provider": {
110
- // ...your existing providers...
111
78
  "agy": {
112
- "npm": "opencode-agy-bridge/provider",
79
+ "npm": "opencode-agy-bridge",
113
80
  "name": "Google Antigravity (via agy CLI)",
114
- "options": {
115
- "binary": "agy",
116
- "timeoutMs": 300000
117
- },
118
- "models": {
119
- "antigravity": {
120
- "name": "Antigravity (server-selected Gemini)"
121
- }
122
- }
81
+ "options": { "binary": "agy", "timeoutMs": 300000 },
82
+ "models": { "antigravity": { "name": "Antigravity (server-selected Gemini)" } }
123
83
  }
124
84
  }
125
85
  }
126
86
  ```
127
87
 
128
- > [!NOTE]
129
- > If OpenCode has trouble resolving the global module name directly, you can replace the module names with their absolute paths pointing to your global `node_modules` folder (e.g., `"/usr/local/lib/node_modules/opencode-agy-bridge/dist/plugin.js"` for the plugin and `"/usr/local/lib/node_modules/opencode-agy-bridge/dist/provider.js"` for the provider).
130
-
131
- ### For Local Development / Manual Installation
88
+ ### Local development (absolute paths)
132
89
 
133
90
  ```jsonc
134
91
  {
135
92
  "plugin": [
136
- // ...your existing plugins...
137
93
  "/home/USER/workspace/opencode-agy-bridge/dist/plugin.js"
138
94
  ],
139
95
  "provider": {
140
- // ...your existing providers...
141
96
  "agy": {
142
97
  "npm": "/home/USER/workspace/opencode-agy-bridge",
143
98
  "name": "Google Antigravity (via agy CLI)",
144
- "options": {
145
- "binary": "agy",
146
- "timeoutMs": 300000
147
- },
148
- "models": {
149
- "antigravity": {
150
- "name": "Antigravity (server-selected Gemini)"
151
- }
152
- }
99
+ "options": { "binary": "agy", "timeoutMs": 300000 },
100
+ "models": { "antigravity": { "name": "Antigravity (server-selected Gemini)" } }
153
101
  }
154
102
  }
155
103
  }
@@ -157,6 +105,10 @@ Just add the package directly with the desired version tag. OpenCode will downlo
157
105
 
158
106
  Then restart OpenCode and run `/model` → select `agy/antigravity`.
159
107
 
108
+ ## Features
109
+
110
+ - **Robust Delta Extraction:** Automatically normalizes `\r\n` (CRLF) and `\n` (LF) line endings, tolerates trailing whitespace/newline differences, and implements suffix-based alignment to support seamless recovery during context window truncation.
111
+
160
112
  ## Known limitations
161
113
 
162
114
  | Limitation | Detail |
@@ -183,35 +135,17 @@ src/
183
135
 
184
136
  ## Development
185
137
 
186
- Using **Bun**:
187
- ```bash
188
- bun run build
189
- bun test
190
- ```
191
-
192
- Using **pnpm**:
193
- ```bash
194
- pnpm run build
195
- pnpm test
196
- ```
197
-
198
- Using **npm**:
199
138
  ```bash
200
- npm run build
201
- npm test
139
+ bun run build # compile TypeScript
140
+ bun test # run test suite
202
141
  ```
203
142
 
204
143
  ## CI/CD (GitHub Actions)
205
144
 
206
- The project includes two GitHub Actions workflows:
207
-
208
- - **CI (`ci.yml`):** Runs on push and pull requests to `main` or `master` to compile the project and execute all unit tests using Bun.
209
- - **Release (`release.yml`):** Runs automatically when a new version tag matching `v*` (e.g., `v0.2.1`) is pushed to the repository. It automatically installs dependencies, builds, tests, and publishes the package to the public npm registry.
210
-
211
- Note that both `npm` and `pnpm` share the same public registry (`registry.npmjs.org`), so a single publish step makes the package installable by both package managers.
145
+ - **CI (`ci.yml`):** Runs on push and pull requests to `main` — compiles and runs all tests.
146
+ - **Release (`release.yml`):** Runs on `v*` tags — builds, tests, and publishes to npm.
212
147
 
213
148
  ### Setup
214
149
 
215
- To enable automated releases:
216
- 1. Generate an Access Token with publish permissions on [npmjs.com](https://www.npmjs.com/).
217
- 2. Add the token as a repository secret named `NPM_TOKEN` in your GitHub repository settings under **Settings** → **Secrets and variables** → **Actions**.
150
+ 1. Generate a Granular Access Token on [npmjs.com](https://www.npmjs.com/) with **Bypass 2FA** enabled.
151
+ 2. Add it as repository secret `NPM_TOKEN` in GitHub **Settings → Secrets and variables → Actions**.
package/dist/plugin.js CHANGED
@@ -1,9 +1,9 @@
1
1
  const plugin = async () => ({
2
2
  "chat.headers": async (incoming, output) => {
3
- // Only inject for our own provider
4
- if (incoming.model.providerID !== "agy")
3
+ if (incoming?.model?.providerID !== "agy")
4
+ return;
5
+ if (!output?.headers)
5
6
  return;
6
- // Pass the stable OpenCode session ID so agy can reuse conversations
7
7
  output.headers["x-agy-session-id"] = incoming.sessionID;
8
8
  },
9
9
  });
package/dist/provider.js CHANGED
@@ -1,7 +1,7 @@
1
- import { runAgy } from "./agy-runner";
2
- import { snapshot, findNewConversation, defaultConversationsDir } from "./conversation-tracker";
3
- import { SessionStore } from "./session-store";
4
- import { flattenPrompt } from "./prompt-mapper";
1
+ import { runAgy } from "./agy-runner.js";
2
+ import { snapshot, findNewConversation, defaultConversationsDir } from "./conversation-tracker.js";
3
+ import { SessionStore } from "./session-store.js";
4
+ import { flattenPrompt } from "./prompt-mapper.js";
5
5
  import { randomUUID } from "node:crypto";
6
6
  const prevOutputs = new Map();
7
7
  export function extractDelta(prevOutput, fullText, conversationBound) {
@@ -49,8 +49,6 @@ function buildLanguageModel(modelId, opts) {
49
49
  const store = new SessionStore(opts.stateFile);
50
50
  const conversationsDir = opts.conversationsDir ?? defaultConversationsDir();
51
51
  const doGenerate = async (callOpts) => {
52
- // Use the stable OpenCode session ID injected via plugin chat.headers hook.
53
- // Falls back to providerMetadata or a random UUID for standalone/testing.
54
52
  const sessionId = callOpts.headers?.["x-agy-session-id"] ??
55
53
  callOpts.providerOptions?.agy
56
54
  ?.sessionId ??
@@ -68,13 +66,11 @@ function buildLanguageModel(modelId, opts) {
68
66
  let before = null;
69
67
  try {
70
68
  before = conversationId ? null : await snapshot(conversationsDir);
71
- // Only send new messages when conversation is already bound.
72
- // agy preserves context internally via --conversation, so sending
73
- // the full history each turn confuses it and causes hallucination.
74
69
  const newMessages = conversationId
75
70
  ? callOpts.prompt.slice(processedMessages)
76
71
  : callOpts.prompt;
77
72
  const prompt = flattenPrompt(newMessages);
73
+ console.error("[agy-bridge] doGenerate session=%s conv=%s msgs=%d/%d", sessionId.slice(0, 8), conversationId?.slice(0, 8) ?? "-", newMessages.length, callOpts.prompt.length);
78
74
  const result = await runAgy({
79
75
  prompt,
80
76
  cwd: process.cwd(),
@@ -195,19 +191,47 @@ function buildLanguageModel(modelId, opts) {
195
191
  doStream,
196
192
  };
197
193
  }
198
- export function createAgyProvider(opts) {
199
- const resolvedOpts = opts ?? {};
200
- const languageModel = (modelId) => buildLanguageModel(modelId, resolvedOpts);
194
+ let factoryInitWarned = false;
195
+ function unsupportedEmbeddingModel(modelId) {
201
196
  return {
202
- languageModel,
203
- textEmbeddingModel() {
197
+ specificationVersion: "v2",
198
+ provider: "agy",
199
+ modelId,
200
+ maxEmbeddingsPerCall: 0,
201
+ supportsParallelCalls: false,
202
+ doEmbed: async () => {
204
203
  throw new Error("agy bridge does not support text embeddings");
205
204
  },
206
- imageModel() {
205
+ };
206
+ }
207
+ function unsupportedImageModel(modelId) {
208
+ return {
209
+ specificationVersion: "v2",
210
+ provider: "agy",
211
+ modelId,
212
+ maxImagesPerCall: 0,
213
+ doGenerate: async () => {
207
214
  throw new Error("agy bridge does not support image generation");
208
215
  },
209
216
  };
210
217
  }
218
+ export function createAgyProvider(opts) {
219
+ const resolvedOpts = opts ?? {};
220
+ if (!factoryInitWarned) {
221
+ factoryInitWarned = true;
222
+ console.error("[agy-bridge] createAgyProvider called");
223
+ }
224
+ const languageModel = (modelId) => {
225
+ console.error("[agy-bridge] languageModel called for modelId=%s", modelId);
226
+ return buildLanguageModel(modelId, resolvedOpts);
227
+ };
228
+ return {
229
+ languageModel,
230
+ textEmbeddingModel: (modelId) => unsupportedEmbeddingModel(modelId),
231
+ imageModel: (modelId) => unsupportedImageModel(modelId),
232
+ };
233
+ }
211
234
  export default function defaultFactory(opts) {
235
+ console.error("[agy-bridge] defaultFactory called");
212
236
  return createAgyProvider(opts);
213
237
  }
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "opencode-agy-bridge",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
- "main": "./plugin.js",
5
+ "main": "./dist/provider.js",
6
6
  "license": "MIT",
7
7
  "description": "OpenCode plugin + provider that routes LLM prompts to agy (Google Antigravity CLI)",
8
8
  "exports": {
9
- ".": "./dist/plugin.js",
10
- "./provider": "./dist/provider.js"
9
+ ".": "./dist/provider.js",
10
+ "./provider": "./dist/provider.js",
11
+ "./plugin": "./dist/plugin.js"
11
12
  },
12
13
  "files": [
13
14
  "dist",
@@ -18,10 +19,16 @@
18
19
  "build": "tsc",
19
20
  "test": "bun test"
20
21
  },
21
- "dependencies": {
22
+ "peerDependencies": {
22
23
  "@ai-sdk/provider": "^3.0.0"
23
24
  },
25
+ "peerDependenciesMeta": {
26
+ "@ai-sdk/provider": {
27
+ "optional": true
28
+ }
29
+ },
24
30
  "devDependencies": {
31
+ "@ai-sdk/provider": "^3.0.0",
25
32
  "@opencode-ai/plugin": "^1.15.12",
26
33
  "@types/bun": "latest",
27
34
  "typescript": "^5.8.0"