opencode-kiro 0.5.0-beta.1 → 0.5.0-beta.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
@@ -1,10 +1,10 @@
1
1
  # opencode-kiro
2
2
 
3
- > ⚠️ **Experimental prerelease** — `0.5.0-beta.1` targets the **unreleased OpenCode v2**
3
+ > ⚠️ **Experimental prerelease** — `0.5.0-beta.3` targets the **unreleased OpenCode v2**
4
4
  > plugin contract at a pinned snapshot. It does **not** work with OpenCode v1.
5
5
  > v1 users: stay on **`opencode-kiro@0.4.0`** (the `main` branch / npm `latest` line,
6
6
  > which remains the supported stable release). See
7
- > [RELEASE_NOTES_0.5.0-beta.1.md](./RELEASE_NOTES_0.5.0-beta.1.md) and
7
+ > [RELEASE_NOTES_0.5.0-beta.3.md](./RELEASE_NOTES_0.5.0-beta.3.md) and
8
8
  > [PINNED_VERSIONS.md](./PINNED_VERSIONS.md) for exact pins and the tested OpenCode SHA.
9
9
  > No OpenCode v2 release date is known or claimed here.
10
10
 
@@ -20,7 +20,10 @@ The plugin supplies:
20
20
  - **Provider ownership**: an AISDK hook constructs the provider from
21
21
  [`kiro-acp-ai-provider`](https://www.npmjs.com/package/kiro-acp-ai-provider) with the
22
22
  right options (`cwd`, `agent`, `trustAllTools`, `mcpTimeout`, `contextWindows`)
23
- - **TUI credits display**: a live Kiro credits box in the sidebar
23
+ - **TUI credits display**: a live Kiro credits box in the sidebar and a compact
24
+ credits chip in the prompt footer row beside the host cost/context display, both
25
+ styled with the host's active theme tokens — auto-loaded from the same single
26
+ config entry (`tui: true`)
24
27
 
25
28
  `kiro-acp-ai-provider` talks to your locally installed `kiro-cli` over Kiro's
26
29
  [Agent Client Protocol](https://agentclientprotocol.com) (ACP). This is the supported
@@ -33,9 +36,9 @@ This prerelease is built and tested against **one pinned OpenCode v2 snapshot**:
33
36
 
34
37
  | Item | Value |
35
38
  |---|---|
36
- | Tested OpenCode commit (`upstream/v2`) | `b47cfbee7c4fd24e5d73e5753b4755db62a92a63` |
37
- | `@opencode-ai/plugin` | `0.0.0-next-16420` (exact) |
38
- | Package version | `0.5.0-beta.1` |
39
+ | Tested OpenCode commit (`upstream/v2`) | `8ba434b5973856b2f32b8cd3543e154b25c413e6` |
40
+ | `@opencode-ai/plugin` | `0.0.0-dev-18686` (exact) |
41
+ | Package version | `0.5.0-beta.3` |
39
42
 
40
43
  Full pin table and verification evidence: [PINNED_VERSIONS.md](./PINNED_VERSIONS.md).
41
44
  There is no `engines.opencode` constraint — the v2 host has no stable semver yet; the
@@ -51,79 +54,86 @@ tested SHA above is the compatibility target. Other v2 snapshots may or may not
51
54
 
52
55
  ## Install and configure
53
56
 
54
- OpenCode v2 splits plugin configuration into **two separate files**, and both entries
55
- are configured manually:
57
+ **One config entry that's the whole setup.** Add the package to the **`plugins`**
58
+ array (plural) of your OpenCode config (`opencode.json`, project or global):
56
59
 
57
- ### 1. Server plugin — `plugins` in your OpenCode config
60
+ ```json
61
+ {
62
+ "plugins": ["opencode-kiro@0.5.0-beta.3"]
63
+ }
64
+ ```
65
+
66
+ > ⚠️ **Always pin the exact version, as above.** The host now background-auto-refreshes
67
+ > UNPINNED npm plugin packages to whatever the registry serves — a bare `"opencode-kiro"`
68
+ > spec can silently move you off the tested build. Use the exact
69
+ > `opencode-kiro@0.5.0-beta.3` spec.
58
70
 
59
- Add the package to the **`plugins`** array (plural) of your OpenCode config
60
- (`opencode.json`, project or global). The object form pins the version and leaves room
61
- for future options:
71
+ The object form pins the same version and leaves room for future options:
62
72
 
63
73
  ```json
64
74
  {
65
75
  "plugins": [
66
76
  {
67
- "package": "opencode-kiro@0.5.0-beta.1",
77
+ "package": "opencode-kiro@0.5.0-beta.3",
68
78
  "options": {}
69
79
  }
70
80
  ]
71
81
  }
72
82
  ```
73
83
 
74
- A plain package string is also accepted:
75
-
76
- ```json
77
- {
78
- "plugins": ["opencode-kiro@0.5.0-beta.1"]
79
- }
80
- ```
81
-
82
84
  This loads the server entry (`./server` export): auth, model discovery, and provider
83
- ownership.
85
+ ownership. The server plugin declares **`tui: true`**, so the host TUI **auto-loads the
86
+ package's `./tui` entrypoint by itself** — the sidebar credits box and the prompt footer
87
+ credits chip appear with no further configuration. **No `cli.json` TUI entry is
88
+ needed.** (The beta.1 two-file setup is obsolete: if you added `"opencode-kiro"` to
89
+ your global `cli.json` `plugins` array for beta.1, remove it.)
84
90
 
85
- ### 2. TUI plugin — `plugins` in your global `cli.json`
91
+ ### Disabling
86
92
 
87
- To enable the Kiro credits sidebar, add `"opencode-kiro"` to the **`plugins`** array in
88
- your **global `cli.json`** (typically `~/.config/opencode/cli.json`) and restart
89
- opencode:
93
+ One `"-kiro"` directive in the same `plugins` array disables **everything**: it removes
94
+ the server plugin, and with it the `tui: true` auto-load, so the TUI half never
95
+ activates either:
90
96
 
91
97
  ```json
92
98
  {
93
- "plugins": ["opencode-kiro"]
99
+ "plugins": ["opencode-kiro@0.5.0-beta.3", "-kiro"]
94
100
  }
95
101
  ```
96
102
 
97
- This loads the TUI entry (`./tui` export): the sidebar credits box. The connect flow
98
- shows these same steps when you answer **Yes** to the
99
- "Enable the Kiro credits sidebar?" prompt during `opencode auth login`.
100
-
101
103
  ### Legacy `tui.json` (v1)
102
104
 
103
105
  `tui.json` is **legacy v1 configuration** and, under v2, is **migration input only**:
104
- the host may read it when migrating old setups, and this plugin **never modifies it**
105
- (the v1 consent-driven update of that file was deleted in v2). Do not add new entries
106
- to `tui.json`; use the global `cli.json` `plugins` array described above.
106
+ the host may read it when migrating old setups, and this plugin **never modifies it**.
107
+ Do not add new entries to `tui.json` (or to `cli.json` neither is used by this
108
+ plugin anymore); the single `plugins` entry above is the only configuration.
107
109
 
108
110
  ### Local development (path source)
109
111
 
110
- Run a local checkout without npm — build first, then reference the repo directory by
111
- absolute path in both `plugins` arrays:
112
+ Run a local checkout without npm — build and pack first, then reference the tarball
113
+ with the `name@file:` form in the `plugins` array:
112
114
 
113
115
  ```bash
114
116
  git clone https://github.com/NachoFLizaur/opencode-kiro && cd opencode-kiro
115
- npm install && npm run build
117
+ npm install && npm run build && npm pack
116
118
  ```
117
119
 
118
120
  ```json
119
- { "plugins": ["/absolute/path/to/opencode-kiro"] }
121
+ { "plugins": ["opencode-kiro@file:/absolute/path/to/opencode-kiro-0.5.0-beta.3.tgz"] }
120
122
  ```
121
123
 
122
- OpenCode resolves the right entrypoint per file from the package `exports`
123
- (`./server` for the server config, `./tui` for `cli.json`). Each entry module exports
124
- its own `id`, which file-source TUI installs require: the server plugin's id is
125
- `kiro` and the TUI plugin's id is `opencode-kiro`. Enable/disable directives and
126
- host logs use `kiro` for the server plugin and `opencode-kiro` for the TUI plugin.
124
+ A bare path or bare `file:` spec is rejected at the tested SHA — the `name@file:` form
125
+ is required. **Caveat (local `file:` installs only)**: the colon in the resulting
126
+ install dirname defeats the host's OpenTUI loader shim, so the TUI surfaces render a
127
+ contained per-slot error notice instead of the credits views (the rest of the TUI keeps
128
+ working). Registry installs (`opencode-kiro@0.5.0-beta.3`) use colon-free paths and are
129
+ fully green — this affects local tarball validation only.
130
+
131
+ The host resolves entrypoints from the package `exports` (`./server` for the server
132
+ half, `./tui` for the auto-loaded TUI half). Each entry module exports its own `id`:
133
+ the server plugin's id is `kiro` and the TUI plugin's id is `opencode-kiro`. Under
134
+ `tui: true` there is no separate TUI directive to manage — `-kiro` (the server id) is
135
+ the single kill-switch, and host logs use `kiro` for the server half and
136
+ `opencode-kiro` for the TUI half.
127
137
 
128
138
  ## Auth
129
139
 
@@ -138,6 +148,11 @@ Select the **Kiro** integration, then the **Kiro CLI Login** method:
138
148
  Complete the login there; the plugin polls for up to 120 seconds and stores a minimal
139
149
  credential record when kiro-cli reports success.
140
150
 
151
+ There is no configuration prompt during login anymore: the beta.1
152
+ "Enable the Kiro credits sidebar?" consent select was removed (upstream deleted the
153
+ prompts API in favor of forms — and the sidebar no longer needs consent-driven config,
154
+ since `tui: true` auto-loads it).
155
+
141
156
  If the flow times out, authenticate directly with kiro-cli (`kiro-cli login`) and run
142
157
  `opencode auth login` again; the fast path then completes immediately.
143
158
 
@@ -176,42 +191,50 @@ so even the lowest level still produces a reasoning trail.
176
191
 
177
192
  Kiro is subscription-metered: requests consume **credits**, and the dollar cost
178
193
  OpenCode normally displays for Kiro turns is always $0.00. To surface credits the TUI
179
- plugin renders one surface:
194
+ plugin renders two surfaces:
180
195
 
181
- - a Kiro credits box in the sidebar (`sidebar.content` slot), showing the session's
196
+ - a Kiro credits box in the sidebar (`sidebar.content` claim), showing the session's
182
197
  live credits total and unit
183
-
184
- It renders only for sessions that carry Kiro credit data; other sessions are
185
- unchanged. The credits value and unit come from the provider state the SDK attaches to
186
- each message part (`part.state.credits` / `part.state.creditsUnit`); nothing is
187
- hardcoded client-side. The only configuration needed is the `cli.json` `plugins` entry
188
- from [Install](#2-tui-plugin--plugins-in-your-global-clijson).
189
-
190
- While a turn is still streaming, credits for just-ended text are picked up live through
191
- a transient store that works around a host reducer bug at the pinned snapshot (the
192
- text-ended event's provider state is dropped by the host); once durable message state
193
- arrives, it is authoritative and nothing is double-counted. See the release notes for
194
- details.
198
+ - a compact credits chip in the prompt footer row beside the host cost/context
199
+ display (`prompt.footer.status` claim, v1 placement restored) with the same total
200
+
201
+ Both are additive `append` claims they compose with the host's built-in content and
202
+ never replace it and both pick up the active theme's text tokens (feature-detected;
203
+ with no theme they fall back to default terminal styling). They render only for
204
+ sessions that carry Kiro credit data; other sessions are unchanged. The credits value
205
+ and unit come from the provider state the host persists on each message part
206
+ (`part.state.credits` / `part.state.creditsUnit`); nothing is hardcoded client-side.
207
+ The only configuration needed is the single `plugins` entry from
208
+ [Install](#install-and-configure) the TUI half auto-loads via `tui: true`.
209
+
210
+ Durable credits are read straight from host message state (the host persists provider
211
+ state on text end — fixed upstream since beta.1). While a turn is still streaming,
212
+ credits for just-ended text are picked up live through a transient overlay that works
213
+ around a host reducer bug at the pinned snapshot (the live event path still drops
214
+ provider state); once durable state arrives it is authoritative and nothing is
215
+ double-counted. See the release notes for details.
195
216
 
196
217
  ## Known limitations (prerelease)
197
218
 
198
- - **No slot ordering.** OpenCode v2 slots have no order parameter, so the sidebar
199
- credits box renders where the host places `sidebar.content` contributions (after the
200
- built-in sidebar sections), not at a plugin-chosen position.
201
- - **Default styling.** The pinned snapshot has no supported theme-token API for plugin
202
- views, so the credits box uses default/inherited terminal styling instead of
203
- matching the active theme.
204
- - **Reduced toast feedback.** Auth-flow feedback is delivered as connect-flow text
205
- (method instructions and the sidebar setup steps) rather than toasts. A toast API
206
- exists at the pinned snapshot, but this plugin's core deliberately does not depend on
207
- it — the TUI context surface is churning and toast availability is not guaranteed
208
- across snapshots.
209
- - **Live text credits use a workaround.** See [Credits in the TUI](#credits-in-the-tui)
210
- and the release notes.
219
+ - **Live text credits use a transient overlay.** The durable credits path is fixed
220
+ upstream, but the live `session.text.ended` reducer still drops provider state at
221
+ the tested SHA, so in-turn updates come from the plugin's transient overlay
222
+ (durable state always wins on reconcile). See
223
+ [Credits in the TUI](#credits-in-the-tui) and the release notes.
211
224
  - **Credits render in the TUI only.** Every other cost surface (ACP clients, web,
212
225
  desktop, share pages, CLI cost output) shows $0.00 for Kiro sessions because the
213
226
  catalog declares Kiro's per-token `cost` as 0 (subscription-metered, no per-token
214
227
  pricing). That is expected, not a defect.
228
+ - **Local `file:` installs show a per-slot TUI error.** The colon in a `name@file:`
229
+ install dirname defeats the host's OpenTUI loader shim; the failure is contained to
230
+ the plugin's slots (dismissible error notice, host TUI unaffected). Registry
231
+ installs are colon-free and fully working — this affects local tarball validation
232
+ only.
233
+ - **Reduced toast feedback.** Auth-flow feedback is delivered as connect-flow text
234
+ rather than toasts; this plugin's core deliberately does not depend on the churning
235
+ TUI toast API.
236
+ - **One tested snapshot.** All pins are exact and the compatibility target is a single
237
+ OpenCode v2 SHA (see [Compatibility](#compatibility)); other snapshots may not work.
215
238
 
216
239
  ## How it works
217
240
 
@@ -241,10 +264,10 @@ details.
241
264
  |---|---|
242
265
  | `kiro-cli is not installed` during auth | Install kiro-cli from <https://kiro.dev/docs/cli/> and ensure it is on `PATH` for the opencode process. |
243
266
  | Auth times out after ~120s | Complete the browser login faster, or run `kiro-cli login` yourself, then re-run `opencode auth login` (fast path). |
244
- | No credits line / credits stay 0 | Credits appear after the first **completed** kiro turn; cancelled turns and turns without usage state contribute nothing. Check `"opencode-kiro"` is listed in your global `cli.json` `plugins` array. |
245
- | Credits box never appears | The TUI entry loads from the global `cli.json` `plugins` array only a server-side `plugins` entry alone does not enable it. Add the `cli.json` entry and restart opencode. |
267
+ | No credits line / credits stay 0 | Credits appear after the first **completed** kiro turn; cancelled turns and turns without usage state contribute nothing. Check the plugin is active (no stray `"-kiro"` directive note that directive residue can persist on a reused data dir). |
268
+ | Credits surfaces never appear | The TUI half auto-loads from the server `plugins` entry via `tui: true` no separate TUI config exists. If the box/chip are missing, the server plugin itself is not loading (check your `plugins` entry and restart opencode). For local `name@file:` tarball installs, a contained per-slot error notice instead of the credits views is the known colon-path caveat; use a registry install. |
246
269
  | `kiro` provider not showing in `opencode models` | Run `opencode auth login` first: models are discovered after auth. If the loaded catalog lacks a `kiro` entry, the plugin self-registers a minimal fallback during discovery. |
247
- | Path install rejected (`must export id`) | Run `npm run build` in your checkout first and reference the repo root (both entry modules export ids). |
270
+ | Path install rejected (`must export id`) | Use the `name@file:<absolute tarball path>` form after `npm run build && npm pack` in your checkout (both entry modules export ids). |
248
271
  | Provider visible but runs fail | The provider can be selectable before any credential exists. Run `opencode auth login` first. |
249
272
  | Worked yesterday, broken today | This prerelease targets one pinned OpenCode snapshot (see [Compatibility](#compatibility)). If your OpenCode build moved past the tested SHA, the v2 plugin surface may have changed underneath it. |
250
273
 
@@ -255,7 +278,7 @@ v1 (`opencode >= 1.16.0`). It uses the v1 contract throughout: singular `plugin`
255
278
  arrays in `opencode.json` and `tui.json`, the `opencode plugin opencode-kiro`
256
279
  installer, and `part.metadata.kiro` credits. Its full documentation is the README at
257
280
  the [`v0.4.0` tag](https://github.com/NachoFLizaur/opencode-kiro/tree/v0.4.0)
258
- (equivalently, `main`). Do not install `0.5.0-beta.1` into an OpenCode v1 setup.
281
+ (equivalently, `main`). Do not install `0.5.0-beta.3` into an OpenCode v1 setup.
259
282
 
260
283
  ## Development
261
284
 
@@ -3,30 +3,32 @@ import {
3
3
  } from "./chunk-M7OUVABX.js";
4
4
 
5
5
  // src/tui/credits-box-view.ts
6
- import { createElement, insert, insertNode } from "@opentui/solid";
6
+ import { createElement, insert, insertNode, setProp } from "@opentui/solid";
7
7
  import { createMemo } from "solid-js";
8
- function createCreditsBoxView(credits) {
8
+ function createCreditsBoxView(credits, tokens) {
9
9
  const current = createMemo(credits);
10
10
  const root = createElement("box");
11
11
  insertNode(
12
12
  root,
13
- headerLine(() => current().present ? "Kiro" : "")
13
+ headerLine(() => current().present ? "Kiro" : "", tokens?.default)
14
14
  );
15
15
  insertNode(
16
16
  root,
17
- plainLine(() => current().present ? formatCredits(current().total, current().unit) : "")
17
+ plainLine(() => current().present ? formatCredits(current().total, current().unit) : "", tokens?.subdued)
18
18
  );
19
19
  return root;
20
20
  }
21
- function headerLine(content) {
21
+ function headerLine(content, fg) {
22
22
  const line = createElement("text");
23
+ if (fg !== void 0) setProp(line, "fg", fg);
23
24
  const bold = createElement("b");
24
25
  insert(bold, content);
25
26
  insertNode(line, bold);
26
27
  return line;
27
28
  }
28
- function plainLine(content) {
29
+ function plainLine(content, fg) {
29
30
  const line = createElement("text");
31
+ if (fg !== void 0) setProp(line, "fg", fg);
30
32
  insert(line, content);
31
33
  return line;
32
34
  }
@@ -0,0 +1,20 @@
1
+ import {
2
+ formatCredits
3
+ } from "./chunk-M7OUVABX.js";
4
+
5
+ // src/tui/credits-chip-view.ts
6
+ import { createElement, insert, setProp } from "@opentui/solid";
7
+ import { createMemo } from "solid-js";
8
+ function createCreditsChipView(credits, tokens) {
9
+ const current = createMemo(credits);
10
+ const chip = createElement("text");
11
+ setProp(chip, "height", 1);
12
+ setProp(chip, "wrapMode", "none");
13
+ setProp(chip, "flexShrink", 0);
14
+ if (tokens?.subdued !== void 0) setProp(chip, "fg", tokens.subdued);
15
+ insert(chip, () => current().present ? formatCredits(current().total, current().unit) : "");
16
+ return chip;
17
+ }
18
+ export {
19
+ createCreditsChipView
20
+ };
package/dist/server.js CHANGED
@@ -1,74 +1,3 @@
1
- // src/server/aisdk.ts
2
- var KIRO_SDK_PACKAGE = "kiro-acp-ai-provider";
3
- function createAisdkResources() {
4
- return {
5
- ownedSdks: /* @__PURE__ */ new Set(),
6
- cache: /* @__PURE__ */ new Map(),
7
- disposeHook: void 0
8
- };
9
- }
10
- function isKiroPackage(pkg) {
11
- return pkg === KIRO_SDK_PACKAGE || pkg === `aisdk:${KIRO_SDK_PACKAGE}`;
12
- }
13
- function isJsonSafe(value) {
14
- if (value === null) return true;
15
- const kind = typeof value;
16
- if (kind === "string" || kind === "number" || kind === "boolean") return true;
17
- if (Array.isArray(value)) return value.every(isJsonSafe);
18
- if (kind === "object") {
19
- const proto = Object.getPrototypeOf(value);
20
- if (proto !== Object.prototype && proto !== null) return false;
21
- return Object.values(value).every(isJsonSafe);
22
- }
23
- return false;
24
- }
25
- function stableStringify(value) {
26
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
27
- if (typeof value === "object" && value !== null) {
28
- const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
29
- return `{${entries.join(",")}}`;
30
- }
31
- return JSON.stringify(value);
32
- }
33
- function stableOptionsKey(options) {
34
- return isJsonSafe(options) ? stableStringify(options) : void 0;
35
- }
36
- async function registerAisdkHook(context, resources) {
37
- const registration = await context.aisdk.hook("sdk", async (event) => {
38
- if (!isKiroPackage(event.package)) return;
39
- const { createKiroAcp } = await import("kiro-acp-ai-provider");
40
- const key = stableOptionsKey(event.options);
41
- let sdk = key === void 0 ? void 0 : resources.cache.get(key);
42
- if (sdk === void 0) {
43
- sdk = createKiroAcp(event.options);
44
- resources.ownedSdks.add(sdk);
45
- if (key !== void 0) resources.cache.set(key, sdk);
46
- }
47
- event.sdk = sdk;
48
- });
49
- resources.disposeHook = registration.dispose;
50
- return async () => {
51
- const errors = [];
52
- resources.disposeHook = void 0;
53
- try {
54
- await registration.dispose();
55
- } catch (error) {
56
- errors.push(error);
57
- }
58
- const owned = Array.from(resources.ownedSdks);
59
- resources.ownedSdks.clear();
60
- resources.cache.clear();
61
- for (const sdk of owned) {
62
- try {
63
- await sdk.shutdown();
64
- } catch (error) {
65
- errors.push(error);
66
- }
67
- }
68
- if (errors.length > 0) throw new AggregateError(errors, "kiro aisdk cleanup failures");
69
- };
70
- }
71
-
72
1
  // src/server/auth.ts
73
2
  var KIRO_INTEGRATION_ID = "kiro";
74
3
  var KIRO_INTEGRATION_NAME = "Kiro";
@@ -79,9 +8,8 @@ var POLL_INTERVAL_MS = 2e3;
79
8
  var MAX_WAIT_MS = 12e4;
80
9
  var NOT_INSTALLED_MESSAGE = "kiro-cli is not installed. Install it from https://kiro.dev/docs/cli/";
81
10
  var TIMEOUT_MESSAGE = "Kiro authentication timed out. Run `kiro-cli login` manually, then re-run `opencode auth login`.";
82
- var LOGIN_INSTRUCTIONS = "Complete Kiro authentication in the browser window that just opened. Waiting for login...";
11
+ var LOGIN_INSTRUCTIONS = "Complete Kiro authentication in the browser window that just opened (if no browser opened, run `kiro-cli login` in another terminal). Waiting for login...";
83
12
  var ALREADY_AUTHENTICATED_INSTRUCTIONS = "Already authenticated with Kiro CLI.";
84
- var SIDEBAR_INSTRUCTIONS = 'To enable the Kiro credits sidebar, add "opencode-kiro" to the "plugins" array in your global cli.json and restart opencode.';
85
13
  function createAuthResources() {
86
14
  return {
87
15
  child: void 0,
@@ -114,11 +42,6 @@ function kiroCredential() {
114
42
  expires: 0
115
43
  };
116
44
  }
117
- function withSidebarGuidance(instructions, inputs) {
118
- return inputs["sidebar"] === "yes" ? `${instructions}
119
-
120
- ${SIDEBAR_INSTRUCTIONS}` : instructions;
121
- }
122
45
  function pollForLogin(verifyAuth, resources) {
123
46
  return new Promise((resolve, reject) => {
124
47
  const start = Date.now();
@@ -144,14 +67,14 @@ function pollForLogin(verifyAuth, resources) {
144
67
  resources.pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
145
68
  });
146
69
  }
147
- async function authorize(inputs, resources) {
70
+ async function authorize(resources) {
148
71
  const { verifyAuth } = await import("kiro-acp-ai-provider");
149
72
  const status = verifyAuth();
150
73
  if (!status.installed) throw new Error(NOT_INSTALLED_MESSAGE);
151
74
  if (status.authenticated) {
152
75
  return {
153
76
  url: KIRO_DOCS_URL,
154
- instructions: withSidebarGuidance(ALREADY_AUTHENTICATED_INSTRUCTIONS, inputs),
77
+ instructions: ALREADY_AUTHENTICATED_INSTRUCTIONS,
155
78
  mode: "auto",
156
79
  callback: Promise.resolve(kiroCredential())
157
80
  };
@@ -162,7 +85,7 @@ async function authorize(inputs, resources) {
162
85
  });
163
86
  return {
164
87
  url: KIRO_DOCS_URL,
165
- instructions: withSidebarGuidance(LOGIN_INSTRUCTIONS, inputs),
88
+ instructions: LOGIN_INSTRUCTIONS,
166
89
  mode: "auto",
167
90
  callback: pollForLogin(verifyAuth, resources)
168
91
  };
@@ -177,20 +100,9 @@ async function registerAuth(context, resources) {
177
100
  method: {
178
101
  id: KIRO_OAUTH_METHOD_ID,
179
102
  type: "oauth",
180
- label: KIRO_OAUTH_METHOD_LABEL,
181
- prompts: [
182
- {
183
- type: "select",
184
- key: "sidebar",
185
- message: "Enable the Kiro credits sidebar?",
186
- options: [
187
- { label: "Yes", value: "yes", hint: "shows manual cli.json setup steps" },
188
- { label: "No", value: "no" }
189
- ]
190
- }
191
- ]
103
+ label: KIRO_OAUTH_METHOD_LABEL
192
104
  },
193
- authorize: (inputs) => authorize(inputs, resources)
105
+ authorize: async (_answer) => authorize(resources)
194
106
  // no refresh callback: kiro-cli owns credential storage and refresh
195
107
  });
196
108
  });
@@ -309,15 +221,26 @@ function createDiscover(context, state) {
309
221
  return run;
310
222
  };
311
223
  }
224
+ function isKiroCredentialEvent(event) {
225
+ const type = event.type;
226
+ if (type === "integration.connection.updated") {
227
+ const data = event.data;
228
+ return data?.integrationID === KIRO_INTEGRATION_ID;
229
+ }
230
+ if (event.type === "credential.switched") {
231
+ return asString(event.data.integrationID) === KIRO_INTEGRATION_ID;
232
+ }
233
+ return type === "credential.updated";
234
+ }
312
235
  async function consumeEvents(iterator, discover) {
313
236
  try {
314
237
  while (true) {
315
238
  const result = await iterator.next();
316
239
  if (result.done) return;
317
240
  const event = result.value;
318
- if (event.type === "integration.connection.updated" && event.data.integrationID === KIRO_INTEGRATION_ID) {
241
+ if (isKiroCredentialEvent(event)) {
319
242
  try {
320
- await discover("connection-updated");
243
+ await discover(event.type);
321
244
  } catch {
322
245
  }
323
246
  }
@@ -374,6 +297,81 @@ async function registerDiscovery(context, resources) {
374
297
  };
375
298
  }
376
299
 
300
+ // src/server/aisdk.ts
301
+ var KIRO_SDK_PACKAGE = "kiro-acp-ai-provider";
302
+ function createAisdkResources() {
303
+ return {
304
+ ownedSdks: /* @__PURE__ */ new Set(),
305
+ cache: /* @__PURE__ */ new Map(),
306
+ disposeHook: void 0
307
+ };
308
+ }
309
+ function isKiroPackage(pkg) {
310
+ return pkg === KIRO_SDK_PACKAGE || pkg === `aisdk:${KIRO_SDK_PACKAGE}`;
311
+ }
312
+ function isJsonSafe(value) {
313
+ if (value === null) return true;
314
+ const kind = typeof value;
315
+ if (kind === "string" || kind === "number" || kind === "boolean") return true;
316
+ if (Array.isArray(value)) return value.every(isJsonSafe);
317
+ if (kind === "object") {
318
+ const proto = Object.getPrototypeOf(value);
319
+ if (proto !== Object.prototype && proto !== null) return false;
320
+ return Object.values(value).every(isJsonSafe);
321
+ }
322
+ return false;
323
+ }
324
+ function stableStringify(value) {
325
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
326
+ if (typeof value === "object" && value !== null) {
327
+ const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
328
+ return `{${entries.join(",")}}`;
329
+ }
330
+ return JSON.stringify(value);
331
+ }
332
+ function stableOptionsKey(options) {
333
+ return isJsonSafe(options) ? stableStringify(options) : void 0;
334
+ }
335
+ async function registerAisdkHook(context, resources) {
336
+ const registration = await context.aisdk.hook(
337
+ "sdk",
338
+ async (event) => {
339
+ if (!isKiroPackage(event.package)) return;
340
+ const { createKiroAcp } = await import("kiro-acp-ai-provider");
341
+ const key = stableOptionsKey(event.options);
342
+ let sdk = key === void 0 ? void 0 : resources.cache.get(key);
343
+ if (sdk === void 0) {
344
+ sdk = createKiroAcp(event.options);
345
+ resources.ownedSdks.add(sdk);
346
+ if (key !== void 0) resources.cache.set(key, sdk);
347
+ }
348
+ event.sdk = sdk;
349
+ },
350
+ { providerID: KIRO_PROVIDER_ID }
351
+ );
352
+ resources.disposeHook = registration.dispose;
353
+ return async () => {
354
+ const errors = [];
355
+ resources.disposeHook = void 0;
356
+ try {
357
+ await registration.dispose();
358
+ } catch (error) {
359
+ errors.push(error);
360
+ }
361
+ const owned = Array.from(resources.ownedSdks);
362
+ resources.ownedSdks.clear();
363
+ resources.cache.clear();
364
+ for (const sdk of owned) {
365
+ try {
366
+ await sdk.shutdown();
367
+ } catch (error) {
368
+ errors.push(error);
369
+ }
370
+ }
371
+ if (errors.length > 0) throw new AggregateError(errors, "kiro aisdk cleanup failures");
372
+ };
373
+ }
374
+
377
375
  // src/server/lifecycle.ts
378
376
  function createServerState() {
379
377
  return {
@@ -409,6 +407,9 @@ function buildCleanup(state) {
409
407
  // src/server.ts
410
408
  var plugin = {
411
409
  id: "kiro",
410
+ // tui: true (dist/promise/plugin.d.ts:40) — the host auto-loads this
411
+ // package's `./tui` entrypoint for npm-channel installs (single config entry)
412
+ tui: true,
412
413
  async setup(context) {
413
414
  const state = createServerState();
414
415
  const cleanup = buildCleanup(state);
package/dist/tui.js CHANGED
@@ -81,6 +81,21 @@ function readSessionID(props) {
81
81
  const sessionID = props.sessionID;
82
82
  return typeof sessionID === "string" && sessionID.length > 0 ? sessionID : void 0;
83
83
  }
84
+ function readColorToken(value) {
85
+ if (typeof value === "string" && value.length > 0) return value;
86
+ if (typeof value === "object" && value !== null) return value;
87
+ return void 0;
88
+ }
89
+ function readThemeTokens(theme) {
90
+ if (typeof theme !== "object" || theme === null) return void 0;
91
+ const text = theme.text;
92
+ if (typeof text !== "object" || text === null) return void 0;
93
+ const tokens = text;
94
+ const defaultFg = readColorToken(tokens.default);
95
+ const subduedFg = readColorToken(tokens.subdued);
96
+ if (defaultFg === void 0 && subduedFg === void 0) return void 0;
97
+ return { default: defaultFg, subdued: subduedFg };
98
+ }
84
99
  var plugin = {
85
100
  // `id` is required for file-source installs (opencode rejects them without one);
86
101
  // matching the package name keeps it identical across path and npm installs
@@ -88,10 +103,14 @@ var plugin = {
88
103
  async setup(context) {
89
104
  const disposers = [];
90
105
  let disposed = false;
91
- const store = createTransientStore();
92
- disposers.push(() => clear(store));
93
- const [{ createCreditsBoxView }, { createSignal }] = await Promise.all([
94
- import("./credits-box-view-FIHO6TR6.js"),
106
+ const memoryStore = context.storage?.memory?.("transient-credits", {
107
+ initial: createTransientStore()
108
+ });
109
+ const store = memoryStore ? memoryStore[0] : createTransientStore();
110
+ if (!memoryStore) disposers.push(() => clear(store));
111
+ const [{ createCreditsBoxView }, { createCreditsChipView }, { createSignal }] = await Promise.all([
112
+ import("./credits-box-view-GLFJSV6M.js"),
113
+ import("./credits-chip-view-YP4RPX26.js"),
95
114
  import("solid-js")
96
115
  ]);
97
116
  const [transientVersion, setTransientVersion] = createSignal(0);
@@ -110,12 +129,24 @@ var plugin = {
110
129
  reconcile(store, sessionID, messages);
111
130
  return mergedMessageCredits(store, sessionID, messages);
112
131
  };
113
- const unregisterSidebar = context.ui.slot("sidebar.content", (props) => {
114
- const credits = creditsFor(readSessionID(props));
115
- const view = createCreditsBoxView(credits);
116
- return (() => credits().present ? view : null);
132
+ const unregisterSidebar = context.ui.slot({
133
+ append: "sidebar.content",
134
+ render: (props) => {
135
+ const credits = creditsFor(readSessionID(props));
136
+ const view = createCreditsBoxView(credits, readThemeTokens(context.theme));
137
+ return (() => credits().present ? view : null);
138
+ }
117
139
  });
118
140
  disposers.push(unregisterSidebar);
141
+ const unregisterChip = context.ui.slot({
142
+ append: "prompt.footer.status",
143
+ render: (props) => {
144
+ const credits = creditsFor(readSessionID(props));
145
+ const view = createCreditsChipView(credits, readThemeTokens(context.theme));
146
+ return (() => credits().present ? view : null);
147
+ }
148
+ });
149
+ disposers.push(unregisterChip);
119
150
  return async () => {
120
151
  if (disposed) return;
121
152
  disposed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-kiro",
3
- "version": "0.5.0-beta.1",
3
+ "version": "0.5.0-beta.3",
4
4
  "description": "The ACP-compliant Kiro plugin for opencode: auth via the official kiro-cli login, the live Kiro model lineup through the Agent Client Protocol, and TUI credits display",
5
5
  "license": "MIT",
6
6
  "author": "Nacho F. Lizaur (https://github.com/NachoFLizaur)",
@@ -39,15 +39,15 @@
39
39
  "node": ">=20"
40
40
  },
41
41
  "peerDependencies": {
42
- "@opencode-ai/plugin": "0.0.0-next-16420"
42
+ "@opencode-ai/plugin": "0.0.0-dev-18686"
43
43
  },
44
44
  "dependencies": {
45
- "@opentui/solid": "0.4.5",
45
+ "@opentui/solid": "0.5.9",
46
46
  "kiro-acp-ai-provider": "3.0.0",
47
47
  "solid-js": "1.9.12"
48
48
  },
49
49
  "devDependencies": {
50
- "@opencode-ai/plugin": "0.0.0-next-16420",
50
+ "@opencode-ai/plugin": "0.0.0-dev-18686",
51
51
  "@types/node": "^20.19.42",
52
52
  "tsup": "^8.0.0",
53
53
  "typescript": "^5.7.0",