@skein-code/cli 0.3.28 → 0.3.30

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.
@@ -152,16 +152,17 @@ The shortest setup path is the interactive user-level wizard:
152
152
  skein agents setup
153
153
  ```
154
154
 
155
- It asks for a connection name, provider, endpoint, credential environment
156
- variable, and default model. The wizard writes only the environment-variable
157
- name and saves shared settings under the user Skein namespace, so the same
158
- connection is available in every workspace. The non-interactive equivalent is
159
- useful for provisioning:
155
+ It asks for a connection name, explicit relay transport, inference endpoint,
156
+ optional model-catalog endpoint, `env` or `none` authentication, and default
157
+ model. The wizard writes only the environment-variable name and saves shared
158
+ settings under the user Skein namespace, so the same connection is available
159
+ in every workspace. The non-interactive equivalent is useful for provisioning:
160
160
 
161
161
  ```bash
162
162
  skein agents setup --yes \
163
163
  --name team-relay \
164
164
  --provider compatible \
165
+ --protocol openai-responses \
165
166
  --base-url https://relay.example/v1 \
166
167
  --api-key-env TEAM_RELAY_API_KEY \
167
168
  --model openai/coding-model
@@ -172,25 +173,86 @@ the same next action without placing a secret in the session transcript.
172
173
 
173
174
  ### Authentication paths
174
175
 
175
- Skein keeps subscription login and API routing separate because they have
176
- different ownership and billing semantics:
176
+ Skein keeps primary relay routing and delegated CLI login separate because they
177
+ have different ownership and billing semantics:
177
178
 
178
179
  - Subscription users authenticate once in each installed official CLI. Codex
179
180
  supports ChatGPT subscription login, Claude Code supports Claude.ai/Teams
180
181
  login, and Gemini CLI supports Google account login. A Skein route with
181
182
  `runtime: "codex"`, `"claude"`, or `"grok"` reuses that CLI's local login;
182
183
  Skein does not read, copy, or persist its tokens.
183
- - Direct API users reference the provider's environment variable on each
184
- route, or let a compatible parent route inherit the same endpoint.
184
+ - Existing direct API routes remain readable for backward compatibility; they
185
+ are not the evolution path for new primary connections.
185
186
  - Relay/gateway users define one named `connection` and reuse it across model
186
- routes. This matches gateways such as OpenRouter or LiteLLM that expose many
187
- models behind one OpenAI-compatible endpoint and bearer key.
187
+ routes. This matches gateways such as OpenRouter, LiteLLM, and Vercel AI
188
+ Gateway that expose Responses, Chat Completions, and/or Anthropic Messages
189
+ behind one relay credential.
190
+
191
+ ### Primary model connection selection
192
+
193
+ The primary agent and team routes share the same connection catalog. User
194
+ configuration under `agents.connections` is merged with strict named
195
+ environment profiles:
196
+
197
+ ```bash
198
+ export SKEIN_CONNECTIONS=work,local
199
+ export SKEIN_CONNECTION_WORK_PROVIDER=compatible
200
+ export SKEIN_CONNECTION_WORK_PROTOCOL=openai-responses
201
+ export SKEIN_CONNECTION_WORK_BASE_URL=https://relay.example/v1
202
+ export SKEIN_CONNECTION_WORK_API_KEY_ENV=WORK_RELAY_KEY
203
+ export SKEIN_CONNECTION_WORK_MODEL=coder
204
+ export SKEIN_CONNECTION_LOCAL_PROVIDER=compatible
205
+ export SKEIN_CONNECTION_LOCAL_PROTOCOL=openai-chat
206
+ export SKEIN_CONNECTION_LOCAL_BASE_URL=http://127.0.0.1:11434/v1
207
+ export SKEIN_CONNECTION_LOCAL_AUTH=none
208
+ ```
209
+
210
+ IDs are lowercase shell-safe names. IDs that collide after environment
211
+ normalization (for example `team-a` and `team_a`) are rejected. Endpoint and
212
+ credential fields never cross profile boundaries. Selection precedence is an
213
+ explicit `--connection`, the configured default, then automatic selection only
214
+ when exactly one complete profile exists. Multiple complete profiles open a
215
+ TTY selector; headless runs fail with `Pass --connection <name>`.
216
+
217
+ New named connections are relay-only: `provider` is `compatible`, authentication
218
+ is exactly `env` or `none`, and the transport is `openai-responses`,
219
+ `openai-chat`, or `anthropic-messages`. OAuth, keychain, command helpers, and
220
+ official account login are not connection schema variants. Custom legacy native
221
+ provider endpoints do not inherit the provider's official API key.
222
+
223
+ Responses is the default because OpenAI recommends it for new projects. Skein
224
+ sets `store: false` and replays the complete returned output items plus tool
225
+ outputs on the next request, which also works with stateless relay
226
+ implementations. Protocol failures never trigger an automatic request through a
227
+ different transport, avoiding duplicate inference and billing.
228
+
229
+ Some relays publish two SDK base URLs for the same account. The OpenAI base
230
+ usually includes `/v1`; the Anthropic base may be a root or provider prefix and
231
+ expects the client to append `/v1/messages`. This is why inference `baseUrl` and
232
+ OpenAI-style `modelsBaseUrl` are distinct. An Anthropic transport must configure
233
+ `modelsBaseUrl` explicitly:
234
+
235
+ ```json
236
+ {
237
+ "provider": "compatible",
238
+ "protocol": "anthropic-messages",
239
+ "baseUrl": "https://relay.example/anthropic",
240
+ "modelsBaseUrl": "https://relay.example/v1",
241
+ "defaultModel": "anthropic/coding-model",
242
+ "auth": {"type": "env", "name": "TEAM_RELAY_API_KEY"}
243
+ }
244
+ ```
188
245
 
189
246
  Official authentication references: [Codex](https://developers.openai.com/codex/auth),
190
247
  [Claude Code](https://code.claude.com/docs/en/authentication), and
191
248
  [Gemini CLI](https://geminicli.com/docs/get-started/authentication/). Unified
192
249
  gateway examples: [OpenRouter](https://openrouter.ai/docs/quickstart) and
193
250
  [LiteLLM](https://docs.litellm.ai/docs/learn/gateway_quickstart).
251
+ Transport references: [OpenAI Responses migration](https://developers.openai.com/api/docs/guides/migrate-to-responses),
252
+ [OpenRouter Responses](https://openrouter.ai/docs/api/reference/responses/overview),
253
+ [OpenRouter Anthropic Messages](https://openrouter.ai/docs/api/api-reference/anthropic-messages/create-a-message),
254
+ [LiteLLM supported endpoints](https://docs.litellm.ai/docs/supported_endpoints),
255
+ and [Vercel AI Gateway APIs](https://vercel.com/docs/ai-gateway/sdks-and-apis).
194
256
 
195
257
  ```json
196
258
  {
@@ -247,8 +309,10 @@ role. Add a profile entry only when that role needs a different model:
247
309
  "connections": {
248
310
  "team-relay": {
249
311
  "provider": "compatible",
312
+ "protocol": "openai-responses",
250
313
  "baseUrl": "https://relay.example/v1",
251
- "apiKeyEnv": "TEAM_RELAY_API_KEY"
314
+ "defaultModel": "openai/coding-model",
315
+ "auth": {"type": "env", "name": "TEAM_RELAY_API_KEY"}
252
316
  }
253
317
  },
254
318
  "routes": {
@@ -268,8 +332,9 @@ need different connections:
268
332
  "connections": {
269
333
  "team-relay": {
270
334
  "provider": "compatible",
335
+ "protocol": "openai-responses",
271
336
  "baseUrl": "https://relay.example/v1",
272
- "apiKeyEnv": "TEAM_RELAY_API_KEY"
337
+ "auth": {"type": "env", "name": "TEAM_RELAY_API_KEY"}
273
338
  }
274
339
  },
275
340
  "routes": {
@@ -295,16 +360,25 @@ need different connections:
295
360
  ```
296
361
 
297
362
  Run `skein agents connections` or `/connections` to inspect the resolved
298
- endpoint, environment-variable reference, and route count without revealing
299
- the key. For compatible/OpenAI connections, run
300
- `skein agents models team-relay` to inspect the provider's `/models` catalog
301
- before choosing route IDs. Discovery is read-only and does not rewrite config;
302
- native Anthropic/Gemini subscription catalogs remain owned by their official
303
- CLIs. Named connections are best kept in user-level configuration. A
363
+ inference endpoint, model-catalog endpoint, environment-variable reference,
364
+ and route count without revealing the key. Run `skein agents models
365
+ team-relay` to inspect the relay's OpenAI-style `/models` catalog before
366
+ choosing route IDs. Discovery is read-only and does not rewrite config. Its
367
+ process-local cache is bounded to 32 endpoint/auth fingerprints with a 15-minute
368
+ TTL and ETag revalidation; secret values are hashed into the in-memory
369
+ fingerprint and never cached. `401`/`403` invalidates the entry and never returns
370
+ stale models as an authentication success. Named connections are best kept in
371
+ user-level configuration. A
304
372
  repository-owned connection or route remains disabled until the project config
305
373
  is explicitly trusted, preventing a cloned repository from redirecting a
306
374
  developer's key and source context to an attacker-controlled endpoint.
307
375
 
376
+ External CLI runtimes remain separate from native connections. Skein launches
377
+ them with a minimal environment containing only the safe executable path,
378
+ home/temporary/locale facts, and that runtime's own config directory. Provider
379
+ keys, `SKEIN_*` credentials, unrelated connection keys, and `NODE_OPTIONS` are
380
+ not inherited by the child process.
381
+
308
382
  Routing precedence is explicit and predictable: a profile route overrides team
309
383
  defaults; a route that specifies `provider` without `connection` bypasses the
310
384
  default connection; otherwise the current parent model is used when no team
@@ -9,7 +9,7 @@ one of the milestones below.
9
9
 
10
10
  - Product name: `Skein`; primary executable: `skein`.
11
11
  - Compatibility executables: `mosaic` and `mosaic-code`.
12
- - Current repository version: `0.3.28`.
12
+ - Current repository version: `0.3.30`.
13
13
  - Runtime requirement: Node.js `>=22.16.0` (the runtime uses unflagged
14
14
  `node:sqlite` with FTS5, and current CLI/build dependencies require this
15
15
  Node 22 baseline).
@@ -24,7 +24,8 @@ one of the milestones below.
24
24
  all sizes support prompt
25
25
  history, `@file` completion, command completion, multiline editing, queued
26
26
  follow-ups, live context inspection, permission approval, themes, ASCII mode,
27
- `NO_COLOR`, and narrow-height degradation.
27
+ `NO_COLOR`, `TERM=dumb`, screen-reader output, reduced motion, and
28
+ narrow-height degradation.
28
29
  - Storage: sessions, checkpoints, local index, and project configuration still
29
30
  use `.mosaic/` paths for compatibility. `SKEIN_*` environment variables are
30
31
  preferred, while `MOSAIC_*` aliases remain supported.
@@ -41,16 +42,19 @@ npm audit --omit=dev
41
42
  npm run release:verify -- --output-dir artifacts/package
42
43
  ```
43
44
 
44
- The latest verified package is `skein-code-cli-0.3.28.tgz`. The verifier writes
45
- its SHA-256 to `artifacts/package/skein-code-cli-0.3.28.tgz.sha256`, and CI
45
+ The latest verified package is `skein-code-cli-0.3.30.tgz`. The verifier writes
46
+ its SHA-256 to `artifacts/package/skein-code-cli-0.3.30.tgz.sha256`, and CI
46
47
  retains the checksum beside the package metadata. The checksum is deliberately
47
48
  not copied into this packaged document because doing so would change the
48
49
  archive it describes.
49
50
 
50
- The final verification included a fresh install for all three executable
51
- aliases. PTY coverage included 20, 24 ASCII, 40, 80, 120 columns and a 40x10
52
- short-height case. The current full-suite count is recorded from the latest
53
- `npm run check` in the release evidence.
51
+ The final verification includes a fresh install for all three executable
52
+ aliases. PTY coverage includes 20, 24 ASCII/`NO_COLOR`, 40, 80, and 120 columns;
53
+ a 40x10 short-height case; 40-column `TERM=dumb`; and 80-column screen-reader
54
+ interaction. Raw output is replayed through a headless terminal so the gate
55
+ checks the final visible screen rather than only accumulated logs. The current
56
+ full-suite count is recorded from the latest `npm run check` in the release
57
+ evidence.
54
58
 
55
59
  ## Recommended Order
56
60
 
@@ -366,25 +370,43 @@ Implementation progress:
366
370
  stopped attempt in telemetry, and feeds only the fresh result into the
367
371
  caller's aggregation. Completed attempts remain immutable until the next
368
372
  report-inspection increment.
369
- - Named `agents.connections` now let API routes share one provider, base URL,
370
- and credential environment-variable reference. This supports the common
371
- relay/gateway case where one key grants access to many model families while
372
- keeping subscription-backed official CLIs on their own login path.
373
+ - Named `agents.connections` and `SKEIN_CONNECTION_*` profiles now let the
374
+ primary agent and API routes share one provider, protocol, base URL, default
375
+ model, and typed authentication reference. One complete connection is
376
+ selected automatically; multiple candidates require a TTY choice or
377
+ `--connection` in headless mode. New connections are relay-only and accept
378
+ only `env` or `none`; subscription-backed official CLIs remain isolated
379
+ delegated runtimes rather than primary connections.
373
380
  - `skein agents connections` and `/connections` expose redacted connection
374
381
  status and route counts. Repository-owned connections are stripped until
375
382
  project config is trusted, just like direct model routes.
376
- - `skein agents models <connection>` can query a compatible/OpenAI connection's
377
- standard `/models` endpoint, giving users model IDs without trial-and-error
378
- configuration. The command is read-only and does not persist the catalog.
383
+ - `skein agents models <connection>` queries the relay's independent
384
+ OpenAI-style `/models` endpoint. A 32-entry process-local cache uses a
385
+ 15-minute TTL, ETag revalidation, endpoint/credential fingerprint isolation,
386
+ and hard invalidation on `401`/`403`; it never stores credential values or
387
+ treats stale data as an authentication success.
379
388
  - Team routing now supports `agents.defaultConnection` and optional
380
389
  `agents.defaultModel`. Most users configure one shared gateway once; profile
381
390
  routes only contain model or provider overrides when needed. CLI and TUI
382
391
  surfaces label inherited versus overridden routes, and unknown defaults fail
383
392
  validation before any agent starts.
384
393
  - `skein agents setup` now provides a guided user-level setup for a shared
385
- connection and default model, with a non-interactive provisioning form. The
386
- wizard stores only credential environment-variable names and preserves other
387
- user configuration.
394
+ connection and default model, with explicit Responses, Chat Completions, and
395
+ Anthropic Messages transports plus separate inference/model-catalog bases.
396
+ The first-run TUI uses the same relay-only model and stores only credential
397
+ environment-variable names while preserving other user configuration.
398
+ - The Responses transport uses `POST /responses`, `store: false`, typed SSE,
399
+ normalized text/function/usage events, and exact output-item replay for
400
+ stateless tool and reasoning continuation. It never retries a failed request
401
+ through a different protocol. Anthropic relay transport accepts SDK-style
402
+ root bases, appends `/v1/messages` when needed, and uses relay bearer auth.
403
+ - `skein doctor` reports unsupported `SEKIN_API`/`SKEIN_BASEURL` spellings and
404
+ connection readiness without reading or printing their values. Custom native
405
+ endpoints cannot inherit official provider keys, and external CLI runtimes
406
+ receive a minimal environment allowlist instead of the parent secret set.
407
+ - This relay-only connection catalog, Responses transport, independent model
408
+ directory, redacted status, and delegated-runtime isolation slice is complete
409
+ and release-verified in `0.3.30`.
388
410
  - Team budgets default to `observe`: telemetry is retained, but configured
389
411
  thresholds do not warn or terminate work. `guard` adds non-blocking threshold
390
412
  warnings, while `strict` is an explicit hard-stop policy for controlled jobs.
@@ -462,14 +484,19 @@ already separated short-term working memory and durable records:
462
484
  Never promote model-inferred facts directly into durable memory without the
463
485
  existing candidate/approval path.
464
486
 
465
- ### P2: Terminal Accessibility And Visual Regression
466
-
467
- Keep the current restrained graphite/cinder/mono theme system and add actual
468
- final-frame regression tests for widths 20-160 and heights 8-60. Include CJK,
469
- wide emoji, screen-reader mode, `TERM=dumb`, `NO_COLOR`, ASCII glyph mode,
470
- permission panels, multiline composer, history search, and active palettes.
471
- Use a terminal emulator fixture where raw ANSI logs cannot prove what remains
472
- visible in the final frame.
487
+ ### P2: Terminal Accessibility And Visual Regression (complete in 0.3.29)
488
+
489
+ Version `0.3.29` adds an explicit screen-reader profile, automatic `TERM=dumb`
490
+ fallback, semantic Ink roles, reduced motion, and non-incremental low-capability
491
+ rendering. The PTY release gate now covers the contracted 20/24/40/80/120-column
492
+ matrix, 40x10 height constraint, CJK/emoji component fixtures, ASCII,
493
+ `NO_COLOR`, permission/error/ready states, history search, and file completion.
494
+ It replays logs through `@xterm/headless` and rejects final-frame wraps, stale
495
+ panels, control-probe leaks, joined announcements, or missing status. The
496
+ single-process benchmark enforces 25 ms input and 150 ms streaming-render p95
497
+ budgets while requiring the last chunk in the final frame. Future expansion may
498
+ sample additional widths through 160 and heights through 60 without changing
499
+ the shipped gate's factual scope.
473
500
 
474
501
  ### P3: Distribution
475
502
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skein-code/cli",
3
- "version": "0.3.28",
3
+ "version": "0.3.30",
4
4
  "description": "A context-first, model-agnostic coding agent with an auditable terminal workspace.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -16,6 +16,17 @@
16
16
  },
17
17
  "skein": {
18
18
  "releaseNotes": [
19
+ "Named primary connections now target third-party compatible relays only, use explicit env or none authentication, and resolve one complete profile automatically while requiring an explicit choice for ambiguity",
20
+ "OpenAI Responses is the recommended new transport with POST /responses, store false, typed SSE, normalized tools and usage, and bounded exact output-item replay for stateless reasoning continuation",
21
+ "OpenAI Chat Completions and Anthropic Messages remain explicit relay transports without cross-protocol retries; inference and OpenAI-style model catalog base URLs are configured independently",
22
+ "Relay model discovery now uses a bounded 15-minute ETag cache isolated by endpoint and credential fingerprints, invalidates authentication failures, and never stores or echoes credential values",
23
+ "First-run and agents setup flows are relay-only, save only credential environment-variable names, and expose redacted connection state consistently across TUI, text, JSON, JSONL, doctor, and config output",
24
+ "Provider diagnostics redact secrets, authorization headers, URL credentials, and query strings while Responses replay state is capped at 128 items and 4 MiB in memory and persisted sessions",
25
+ "External delegated CLIs now receive a minimal runtime-owned environment instead of inheriting provider, Skein, relay, or NODE_OPTIONS secrets",
26
+ "TERM=dumb and screen-reader sessions now use deterministic ASCII, monochrome, reduced-motion, non-incremental rendering without losing keyboard input",
27
+ "SKEIN_SCREEN_READER=1 enables Ink accessibility output with semantic timeline, permission, and composer roles plus clean phase boundaries",
28
+ "PTY release gates now replay eight terminal scenarios through a headless emulator and reject final-frame overflow, stale panels, announcement joins, and missing status",
29
+ "A local terminal UI benchmark enforces 25 ms input and 150 ms streaming-render p95 budgets while retaining the final streamed chunk",
19
30
  "MCP trust and revoke confirmation rendering now has a deterministic cross-platform release regression gate",
20
31
  "MCP now follows a no-network search and inspect review before explicit workspace-bound manifest trust and lazy activation",
21
32
  "Declarative capability manifests expose redacted source, version, tools, permissions, network, command, path, sensitive-field, process, and completion-evidence effects",
@@ -99,6 +110,7 @@
99
110
  "benchmark:context": "tsx scripts/benchmark-local-index.ts",
100
111
  "benchmark:duplication": "tsx scripts/benchmark-duplication.ts",
101
112
  "benchmark:token-economy": "tsx scripts/benchmark-token-economy.ts",
113
+ "benchmark:terminal-ui": "tsx scripts/benchmark-terminal-ui.ts",
102
114
  "verify:package": "node scripts/verify-package.mjs",
103
115
  "release:verify": "npm run check && npm run verify:package --",
104
116
  "typecheck": "tsc --noEmit",
@@ -126,6 +138,7 @@
126
138
  "@types/diff": "^8.0.0",
127
139
  "@types/node": "^22.19.7",
128
140
  "@types/react": "^19.2.7",
141
+ "@xterm/headless": "^6.0.0",
129
142
  "tsup": "^8.5.1",
130
143
  "tsx": "^4.21.0",
131
144
  "vitest": "^4.0.18"