opencode-kiro 0.5.0-beta.1 → 0.5.0-beta.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 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.2` 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.2.md](./RELEASE_NOTES_0.5.0-beta.2.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`) | `1cf61593b5ec204619b3f679fe418fec10ca5934` |
40
+ | `@opencode-ai/plugin` | `0.0.0-dev-17968` (exact) |
41
+ | Package version | `0.5.0-beta.2` |
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,81 @@ 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.2"]
63
+ }
64
+ ```
58
65
 
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:
66
+ The object form pins the same version and leaves room for future options:
62
67
 
63
68
  ```json
64
69
  {
65
70
  "plugins": [
66
71
  {
67
- "package": "opencode-kiro@0.5.0-beta.1",
72
+ "package": "opencode-kiro@0.5.0-beta.2",
68
73
  "options": {}
69
74
  }
70
75
  ]
71
76
  }
72
77
  ```
73
78
 
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
79
  This loads the server entry (`./server` export): auth, model discovery, and provider
83
- ownership.
80
+ ownership. The server plugin declares **`tui: true`**, so the host TUI **auto-loads the
81
+ package's `./tui` entrypoint by itself** — the sidebar credits box and the composer
82
+ credits chip appear with no further configuration. **No `cli.json` TUI entry is
83
+ needed.** (The beta.1 two-file setup is obsolete: if you added `"opencode-kiro"` to
84
+ your global `cli.json` `plugins` array for beta.1, remove it.)
84
85
 
85
- ### 2. TUI plugin — `plugins` in your global `cli.json`
86
+ ### Disabling
86
87
 
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:
88
+ One `"-kiro"` directive in the same `plugins` array disables **everything**: it removes
89
+ the server plugin, and with it the `tui: true` auto-load, so the TUI half never
90
+ activates either:
90
91
 
91
92
  ```json
92
93
  {
93
- "plugins": ["opencode-kiro"]
94
+ "plugins": ["opencode-kiro@0.5.0-beta.2", "-kiro"]
94
95
  }
95
96
  ```
96
97
 
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
98
  ### Legacy `tui.json` (v1)
102
99
 
103
100
  `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.
101
+ the host may read it when migrating old setups, and this plugin **never modifies it**.
102
+ Do not add new entries to `tui.json` (or to `cli.json` neither is used by this
103
+ plugin anymore); the single `plugins` entry above is the only configuration.
107
104
 
108
105
  ### Local development (path source)
109
106
 
110
- Run a local checkout without npm — build first, then reference the repo directory by
111
- absolute path in both `plugins` arrays:
107
+ Run a local checkout without npm — build and pack first, then reference the tarball
108
+ with the `name@file:` form in the `plugins` array:
112
109
 
113
110
  ```bash
114
111
  git clone https://github.com/NachoFLizaur/opencode-kiro && cd opencode-kiro
115
- npm install && npm run build
112
+ npm install && npm run build && npm pack
116
113
  ```
117
114
 
118
115
  ```json
119
- { "plugins": ["/absolute/path/to/opencode-kiro"] }
116
+ { "plugins": ["opencode-kiro@file:/absolute/path/to/opencode-kiro-0.5.0-beta.2.tgz"] }
120
117
  ```
121
118
 
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.
119
+ A bare path or bare `file:` spec is rejected at the tested SHA — the `name@file:` form
120
+ is required. **Caveat (local `file:` installs only)**: the colon in the resulting
121
+ install dirname defeats the host's OpenTUI loader shim, so the TUI surfaces render a
122
+ contained per-slot error notice instead of the credits views (the rest of the TUI keeps
123
+ working). Registry installs (`opencode-kiro@0.5.0-beta.2`) use colon-free paths and are
124
+ fully green — this affects local tarball validation only.
125
+
126
+ The host resolves entrypoints from the package `exports` (`./server` for the server
127
+ half, `./tui` for the auto-loaded TUI half). Each entry module exports its own `id`:
128
+ the server plugin's id is `kiro` and the TUI plugin's id is `opencode-kiro`. Under
129
+ `tui: true` there is no separate TUI directive to manage — `-kiro` (the server id) is
130
+ the single kill-switch, and host logs use `kiro` for the server half and
131
+ `opencode-kiro` for the TUI half.
127
132
 
128
133
  ## Auth
129
134
 
@@ -138,6 +143,11 @@ Select the **Kiro** integration, then the **Kiro CLI Login** method:
138
143
  Complete the login there; the plugin polls for up to 120 seconds and stores a minimal
139
144
  credential record when kiro-cli reports success.
140
145
 
146
+ There is no configuration prompt during login anymore: the beta.1
147
+ "Enable the Kiro credits sidebar?" consent select was removed (upstream deleted the
148
+ prompts API in favor of forms — and the sidebar no longer needs consent-driven config,
149
+ since `tui: true` auto-loads it).
150
+
141
151
  If the flow times out, authenticate directly with kiro-cli (`kiro-cli login`) and run
142
152
  `opencode auth login` again; the fast path then completes immediately.
143
153
 
@@ -176,42 +186,50 @@ so even the lowest level still produces a reasoning trail.
176
186
 
177
187
  Kiro is subscription-metered: requests consume **credits**, and the dollar cost
178
188
  OpenCode normally displays for Kiro turns is always $0.00. To surface credits the TUI
179
- plugin renders one surface:
189
+ plugin renders two surfaces:
180
190
 
181
- - a Kiro credits box in the sidebar (`sidebar.content` slot), showing the session's
191
+ - a Kiro credits box in the sidebar (`sidebar.content` claim), showing the session's
182
192
  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.
193
+ - a compact credits chip in the prompt footer row beside the host cost/context
194
+ display (`prompt.footer.status` claim, v1 placement restored) with the same total
195
+
196
+ Both are additive `append` claims they compose with the host's built-in content and
197
+ never replace it and both pick up the active theme's text tokens (feature-detected;
198
+ with no theme they fall back to default terminal styling). They render only for
199
+ sessions that carry Kiro credit data; other sessions are unchanged. The credits value
200
+ and unit come from the provider state the host persists on each message part
201
+ (`part.state.credits` / `part.state.creditsUnit`); nothing is hardcoded client-side.
202
+ The only configuration needed is the single `plugins` entry from
203
+ [Install](#install-and-configure) the TUI half auto-loads via `tui: true`.
204
+
205
+ Durable credits are read straight from host message state (the host persists provider
206
+ state on text end — fixed upstream since beta.1). While a turn is still streaming,
207
+ credits for just-ended text are picked up live through a transient overlay that works
208
+ around a host reducer bug at the pinned snapshot (the live event path still drops
209
+ provider state); once durable state arrives it is authoritative and nothing is
210
+ double-counted. See the release notes for details.
195
211
 
196
212
  ## Known limitations (prerelease)
197
213
 
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.
214
+ - **Live text credits use a transient overlay.** The durable credits path is fixed
215
+ upstream, but the live `session.text.ended` reducer still drops provider state at
216
+ the tested SHA, so in-turn updates come from the plugin's transient overlay
217
+ (durable state always wins on reconcile). See
218
+ [Credits in the TUI](#credits-in-the-tui) and the release notes.
211
219
  - **Credits render in the TUI only.** Every other cost surface (ACP clients, web,
212
220
  desktop, share pages, CLI cost output) shows $0.00 for Kiro sessions because the
213
221
  catalog declares Kiro's per-token `cost` as 0 (subscription-metered, no per-token
214
222
  pricing). That is expected, not a defect.
223
+ - **Local `file:` installs show a per-slot TUI error.** The colon in a `name@file:`
224
+ install dirname defeats the host's OpenTUI loader shim; the failure is contained to
225
+ the plugin's slots (dismissible error notice, host TUI unaffected). Registry
226
+ installs are colon-free and fully working — this affects local tarball validation
227
+ only.
228
+ - **Reduced toast feedback.** Auth-flow feedback is delivered as connect-flow text
229
+ rather than toasts; this plugin's core deliberately does not depend on the churning
230
+ TUI toast API.
231
+ - **One tested snapshot.** All pins are exact and the compatibility target is a single
232
+ OpenCode v2 SHA (see [Compatibility](#compatibility)); other snapshots may not work.
215
233
 
216
234
  ## How it works
217
235
 
@@ -241,10 +259,10 @@ details.
241
259
  |---|---|
242
260
  | `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
261
  | 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. |
262
+ | 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). |
263
+ | 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
264
  | `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). |
265
+ | 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
266
  | Provider visible but runs fail | The provider can be selectable before any credential exists. Run `opencode auth login` first. |
249
267
  | 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
268
 
@@ -255,7 +273,7 @@ v1 (`opencode >= 1.16.0`). It uses the v1 contract throughout: singular `plugin`
255
273
  arrays in `opencode.json` and `tui.json`, the `opencode plugin opencode-kiro`
256
274
  installer, and `part.metadata.kiro` credits. Its full documentation is the README at
257
275
  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.
276
+ (equivalently, `main`). Do not install `0.5.0-beta.2` into an OpenCode v1 setup.
259
277
 
260
278
  ## Development
261
279
 
@@ -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";
@@ -81,7 +10,6 @@ var NOT_INSTALLED_MESSAGE = "kiro-cli is not installed. Install it from https://
81
10
  var TIMEOUT_MESSAGE = "Kiro authentication timed out. Run `kiro-cli login` manually, then re-run `opencode auth login`.";
82
11
  var LOGIN_INSTRUCTIONS = "Complete Kiro authentication in the browser window that just opened. 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
  });
@@ -374,6 +286,81 @@ async function registerDiscovery(context, resources) {
374
286
  };
375
287
  }
376
288
 
289
+ // src/server/aisdk.ts
290
+ var KIRO_SDK_PACKAGE = "kiro-acp-ai-provider";
291
+ function createAisdkResources() {
292
+ return {
293
+ ownedSdks: /* @__PURE__ */ new Set(),
294
+ cache: /* @__PURE__ */ new Map(),
295
+ disposeHook: void 0
296
+ };
297
+ }
298
+ function isKiroPackage(pkg) {
299
+ return pkg === KIRO_SDK_PACKAGE || pkg === `aisdk:${KIRO_SDK_PACKAGE}`;
300
+ }
301
+ function isJsonSafe(value) {
302
+ if (value === null) return true;
303
+ const kind = typeof value;
304
+ if (kind === "string" || kind === "number" || kind === "boolean") return true;
305
+ if (Array.isArray(value)) return value.every(isJsonSafe);
306
+ if (kind === "object") {
307
+ const proto = Object.getPrototypeOf(value);
308
+ if (proto !== Object.prototype && proto !== null) return false;
309
+ return Object.values(value).every(isJsonSafe);
310
+ }
311
+ return false;
312
+ }
313
+ function stableStringify(value) {
314
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
315
+ if (typeof value === "object" && value !== null) {
316
+ const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
317
+ return `{${entries.join(",")}}`;
318
+ }
319
+ return JSON.stringify(value);
320
+ }
321
+ function stableOptionsKey(options) {
322
+ return isJsonSafe(options) ? stableStringify(options) : void 0;
323
+ }
324
+ async function registerAisdkHook(context, resources) {
325
+ const registration = await context.aisdk.hook(
326
+ "sdk",
327
+ async (event) => {
328
+ if (!isKiroPackage(event.package)) return;
329
+ const { createKiroAcp } = await import("kiro-acp-ai-provider");
330
+ const key = stableOptionsKey(event.options);
331
+ let sdk = key === void 0 ? void 0 : resources.cache.get(key);
332
+ if (sdk === void 0) {
333
+ sdk = createKiroAcp(event.options);
334
+ resources.ownedSdks.add(sdk);
335
+ if (key !== void 0) resources.cache.set(key, sdk);
336
+ }
337
+ event.sdk = sdk;
338
+ },
339
+ { providerID: KIRO_PROVIDER_ID }
340
+ );
341
+ resources.disposeHook = registration.dispose;
342
+ return async () => {
343
+ const errors = [];
344
+ resources.disposeHook = void 0;
345
+ try {
346
+ await registration.dispose();
347
+ } catch (error) {
348
+ errors.push(error);
349
+ }
350
+ const owned = Array.from(resources.ownedSdks);
351
+ resources.ownedSdks.clear();
352
+ resources.cache.clear();
353
+ for (const sdk of owned) {
354
+ try {
355
+ await sdk.shutdown();
356
+ } catch (error) {
357
+ errors.push(error);
358
+ }
359
+ }
360
+ if (errors.length > 0) throw new AggregateError(errors, "kiro aisdk cleanup failures");
361
+ };
362
+ }
363
+
377
364
  // src/server/lifecycle.ts
378
365
  function createServerState() {
379
366
  return {
@@ -409,6 +396,9 @@ function buildCleanup(state) {
409
396
  // src/server.ts
410
397
  var plugin = {
411
398
  id: "kiro",
399
+ // tui: true (dist/promise/plugin.d.ts:40) — the host auto-loads this
400
+ // package's `./tui` entrypoint for npm-channel installs (single config entry)
401
+ tui: true,
412
402
  async setup(context) {
413
403
  const state = createServerState();
414
404
  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.2",
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-17968"
43
43
  },
44
44
  "dependencies": {
45
- "@opentui/solid": "0.4.5",
45
+ "@opentui/solid": "0.5.7",
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-17968",
51
51
  "@types/node": "^20.19.42",
52
52
  "tsup": "^8.0.0",
53
53
  "typescript": "^5.7.0",