@supernovae-st/nika 0.120.0 → 0.120.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
@@ -10,8 +10,9 @@
10
10
  <h1 align="center">@supernovae-st/nika</h1>
11
11
 
12
12
  <p align="center">
13
- <strong>The TypeScript door to Nika: audit a workflow, run it, watch it, prove it.</strong><br>
14
- Locally through the released engine, or against an authenticated <code>nika serve</code>.
13
+ <strong>Run AI workflows from TypeScript.</strong><br>
14
+ Audit a <code>.nika</code> file, run it, read the result, verify the receipt — locally
15
+ or against authenticated <code>nika serve</code>.
15
16
  </p>
16
17
 
17
18
  <p align="center">
@@ -29,22 +30,17 @@
29
30
  <a href="https://archive.softwareheritage.org/browse/origin/?origin_url=https://github.com/supernovae-st/nika-client"><img src="https://archive.softwareheritage.org/badge/origin/https://github.com/supernovae-st/nika-client/" alt="Archived by Software Heritage"></a>
30
31
  </p>
31
32
 
32
- ## Thirty seconds, no API key
33
+ ## Run a workflow from your app
33
34
 
34
- One package installs the client, the `nika` command and the engine payload for
35
- your platform (macOS and Linux, arm64 and x64):
35
+ Put repeatable AI work in a `.nika` file. From Node, run it and read the
36
+ result. No server and no API key for the first run: `mock/echo` is a
37
+ **local simulation** (output is prefixed `mock(echo) ·`, not a model answer).
36
38
 
37
39
  ```sh
38
- npm install @supernovae-st/nika@0.118.7
39
- ./node_modules/.bin/nika --version
40
- ```
41
-
42
- ```
43
- nika 0.118.7 (f3a31a6ee)
40
+ npm install @supernovae-st/nika
44
41
  ```
45
42
 
46
- Write `hello.nika`. The `mock/echo` model rehearses with no key and no
47
- network:
43
+ Save as `hello.nika`:
48
44
 
49
45
  ```yaml
50
46
  nika: hello
@@ -59,7 +55,35 @@ outputs:
59
55
  greeting: ${{ tasks.greeting.output }}
60
56
  ```
61
57
 
62
- Audit it before anything runs:
58
+ Save as `demo.mjs`:
59
+
60
+ ```js
61
+ import { Nika, isNikaRunSucceeded } from '@supernovae-st/nika';
62
+
63
+ const run = await new Nika({ cwd: process.cwd() }).run('hello.nika', { maxCostUsd: 0 });
64
+ const result = await run.result();
65
+ if (!isNikaRunSucceeded(result)) {
66
+ console.error(result.status, result.error?.code, result.error?.message);
67
+ process.exitCode = 1;
68
+ } else {
69
+ console.log(result.outputs);
70
+ // { greeting: "mock(echo) · Say hello from the Nika SDK." }
71
+ }
72
+ ```
73
+
74
+ ```sh
75
+ node demo.mjs
76
+ ```
77
+
78
+ `run()` already admits: a red file throws `NikaOperationError` and never
79
+ returns a handle. The `.nika` file is the contract; the SDK does not parse
80
+ YAML. Pin the version you tested — see [Install](#install): the SDK and the
81
+ standalone engine CLI release on independent clocks.
82
+
83
+ `check()`, `run.events()`, and `traceVerify()` are the next steps. They stay
84
+ taught and tested; they are not required to see the first result.
85
+
86
+ ### Next: audit without running (`check`)
63
87
 
64
88
  ```sh
65
89
  ./node_modules/.bin/nika check hello.nika
@@ -74,45 +98,31 @@ Audit it before anything runs:
74
98
  layers · valid ✔ · access ready ✔ · capacity fit ✔ · run ready ✔
75
99
  ```
76
100
 
77
- Now drive the same engine from TypeScript:
78
-
79
- ```ts
80
- import { Nika } from '@supernovae-st/nika';
81
-
82
- const nika = new Nika({
83
- cwd: process.cwd(),
84
- // bin: '/absolute/path/to/nika', // or set NIKA_BIN
85
- });
101
+ The same report is `await nika.check('hello.nika', { nativeStrict: true })`.
102
+ A red check never becomes a run.
86
103
 
87
- const report = await nika.check('hello.nika', {
88
- nativeStrict: true,
89
- });
90
- if (!report.clean) throw new Error('workflow did not pass nika check');
104
+ ### Next: watch the run (`run.events()`)
91
105
 
92
- const run = await nika.run('hello.nika', { maxCostUsd: 0 });
106
+ ```ts
93
107
  for await (const event of run.events()) {
94
- // The same lifecycle words on both transports; the engine's own frame,
95
- // in its protocol vocabulary, stays on event.raw.
96
108
  console.log([event.kind, event.task, event.status].filter(Boolean).join(' '));
97
109
  }
98
-
99
- const result = await run.result();
100
- console.log(result.status, result.outputs, result.receipt);
101
110
  ```
102
111
 
103
- Expected output: `run.started`, `task.scheduled greeting`,
104
- `task.started greeting`, `task.completed greeting`, `engine.event`, then
105
- `run.settled succeeded`, then the terminal `succeeded` line with the outputs
106
- and the receipt. The `engine.event` is the native journal's own
107
- `workflow_completed` frame: the SDK gives it no lifecycle name and no state,
108
- drops nothing, and shows it on `event.raw.kind`.
109
-
110
- That is six frames for one task; the same shape with 90 tasks was measured at
111
- 273 (`3N + 3`). A session retains the most recent 4096 frames by default, so
112
- `run.events()` opened after `run.result()` replays them all, and is refused,
113
- never shortened, past that bound; see
112
+ Expected native events for this one-task file: `run.started`,
113
+ `task.scheduled greeting`, `task.started greeting`,
114
+ `task.completed greeting`, `engine.event` (`workflow_completed` on
115
+ `event.raw.kind`), then `run.settled succeeded`. Six `run.events()` frames,
116
+ sealed or not (probed on 0.120.0). A session retains the most recent 4096
117
+ frames by default; see
114
118
  [Observing a run after the fact](#observing-a-run-after-the-fact).
115
119
 
120
+ ### Next: verify a seal (`traceVerify`)
121
+
122
+ `traceVerify` **verifies** an existing seal. It does not create one. A keyless
123
+ machine still **succeeds**; the receipt is unsealed — see
124
+ [Admission, execution, seal](#admission-execution-seal).
125
+
116
126
  <p align="center">
117
127
  <img src="https://raw.githubusercontent.com/supernovae-st/nika-client/main/media/local-driver.gif" alt="The typed driver over the released binary: check the workflow, gate on the report, run it to the end under a cost ceiling, count the events" width="960">
118
128
  </p>
@@ -183,8 +193,11 @@ An engine without the capability rejects with `NikaCompatibilityError`
183
193
  (`capability: 'inputsLiteral'` or `'jobInputs'`) and nothing runs. The SDK
184
194
  never falls back to `--var`, and a resident that merely answers 202 has
185
195
  negotiated nothing: one from before the envelope accepts the field and ignores
186
- its values. The engine payload pinned by this package version advertises
187
- neither capability yet, so `inputs` is refused on it until the pin moves.
196
+ its values.
197
+
198
+ The **published 0.120.0** payload advertises `inputsLiteral` (`nika --sdk-identity`).
199
+ Native `run({ inputs })` works on it. It does **not** advertise `jobInputs`;
200
+ HTTP literal inputs stay a compatibility refusal until a resident that does.
188
201
 
189
202
  `vars` is deprecated. It remains the native `--var KEY=VALUE` operator channel,
190
203
  unchanged: the engine reads `@env:NAME` from its environment and coerces text
@@ -197,10 +210,9 @@ A `try { await run.result() } catch {}` alone therefore never catches a failed
197
210
  workflow: a CI job or an application must read `result.status` and treat
198
211
  anything but `succeeded` as its own failure, or a red run passes silently.
199
212
 
200
- On the development branch, `isNikaRunSucceeded` provides that TypeScript
201
- narrowing. It is **unreleased** and is not exported by the published
202
- `0.118.7` package; that package uses `result.status === 'succeeded'` directly.
203
- For CI built from this branch:
213
+ `isNikaRunSucceeded` is exported by published **0.120.0**. Use it (or
214
+ `result.status === 'succeeded'`) before treating a result as success.
215
+ Paused, failed, cancelled, and interrupted all return false:
204
216
 
205
217
  ```ts
206
218
  import { Nika, isNikaRunSucceeded } from '@supernovae-st/nika';
@@ -229,17 +241,40 @@ into a fabricated output map.
229
241
  secret and the cost floor. A red check never becomes a run.
230
242
  - **Sovereign by default.** The same file runs on local models (Ollama,
231
243
  llama.cpp, vLLM), on Mistral, Hugging Face, OpenAI, xAI, Anthropic and the
232
- rest of the engine's catalog; `mock/echo` rehearses with no key and no
233
- network.
234
- - **Traced after.** Every native run leaves a hash-chained journal and hands
235
- back a receipt; `traceVerify()` asks the engine to verify it. The SDK never
236
- re-implements the proof.
244
+ rest of the engine's catalog; `mock/echo` is a local simulation with no key
245
+ and no network.
246
+ - **Traced after.** A native run writes a hash-chained journal and a receipt.
247
+ `traceVerify()` asks the engine to verify that evidence. A succeeded run can
248
+ still be unsealed. The SDK never re-implements the proof.
237
249
  - **One vocabulary, two transports.** `check`, `run`, `events`, `cancel`,
238
250
  `traceVerify` and `schedule` read the same against a local process and an
239
251
  authenticated `nika serve`; only the constructor changes. Run events carry
240
252
  the same lifecycle words on both (`run.started`, `run.waiting`,
241
253
  `run.settled`), with the engine's own frame kept on `event.raw`.
242
254
 
255
+ ## Admission, execution, seal
256
+
257
+ Three different facts. Do not collapse them.
258
+
259
+ | Fact | How you see it | What it is not |
260
+ |---|---|---|
261
+ | **Admission** | `check()` returns `clean: false` and findings. `run()` **throws** `NikaOperationError` (`NIKA-PARSE-005`, `NIKA-1708`, …) and yields no handle. | Not a successful execution. |
262
+ | **Execution** | `run.result()` / `isNikaRunSucceeded(result)`. Admitted failure is **data** (`status: "failed"`). A human gate is `paused` (`run.waiting`), not success. | Not proof the journal is sealed. |
263
+ | **Seal** | `result.receipt.sealed` and `traceVerify(receipt)`. Tamper-evident, not tamper-proof; not replayed unless you pass `--replay`; not anchored without a sidecar. | Not “the workflow was correct” and not “a human read the output”. |
264
+
265
+ Probed on published 0.120.0 (2026-09-19): `run.events()` still yields the
266
+ same six lifecycle frames with or without a signing key. With
267
+ `~/.nika/keys/run-signing.*`, the hello fixture sealed and `traceVerify`
268
+ returned `{ verified: true, verdict: "verified" }`. With an isolated `HOME`
269
+ and no key files (keychain skipped: stderr not a TTY), the same file
270
+ **succeeded**, `sealed: false`, and `traceVerify` returned
271
+ `{ verified: false, verdict: "invalid", reason: "receipt_mismatch" }` while
272
+ the engine still said the journal chain was OK and **UNSEALED**. In this
273
+ keyless case that reason means no signed binding, not a failed workflow.
274
+ `receipt_mismatch` is also the engine's word for a tampered or
275
+ field-mismatched receipt — do not treat every occurrence as merely unsigned.
276
+ The CLI verify line that counts journal events is not `run.events()`.
277
+
243
278
  ## One vocabulary
244
279
 
245
280
  `Nika` exposes `check`, `run`, `attachRun`, `traceVerify`, `listWorkflows`,
@@ -287,19 +322,34 @@ not parse YAML or reconstruct proof in TypeScript.
287
322
  Socratic risk matrix
288
323
  - [Migrating to 0.116](docs/migrating-to-0.116.md) · the intentional breaking
289
324
  migration to the smaller durable client surface
290
- - [docs.nika.sh](https://docs.nika.sh) · the language, the engine and the
291
- other doors
325
+ - [docs.nika.sh](https://docs.nika.sh) · language and engine
326
+ - [SDK quickstart](https://docs.nika.sh/sdk/start/quickstart) · app walkthrough
327
+ (install **npm `@supernovae-st/nika`**, matching this README)
292
328
 
293
329
  ## Install
294
330
 
295
- Pin the version you tested, then verify the package the project actually
296
- resolved:
331
+ ```sh
332
+ npm install @supernovae-st/nika
333
+ ```
334
+
335
+ Every SDK package bundles its own matching engine: the `nika` binary under
336
+ `node_modules/.bin` is the exact engine that package was qualified against.
337
+ The standalone engine CLI (GitHub releases, brew, install script) releases on
338
+ an independent clock, so its newest tag can be ahead of or behind the engine
339
+ bundled in the latest npm package. The
340
+ [npm registry](https://www.npmjs.com/package/@supernovae-st/nika?activeTab=versions)
341
+ lists current published versions. Pin the version you tested, then verify the
342
+ package the project actually resolved:
297
343
 
298
344
  ```sh
299
- npm install @supernovae-st/nika@0.118.7
300
345
  node -p "require('@supernovae-st/nika/package.json').version"
346
+ ./node_modules/.bin/nika --version
301
347
  ```
302
348
 
349
+ This repository's source train is **0.120.2** (`package.json`), lockstep with
350
+ public engine tag `v0.120.2` (`289a9adea`). A source train is not a published
351
+ npm version until the release workflow publishes it.
352
+
303
353
  This package metadata subpath is exported for CommonJS, ESM build tools and CI
304
354
  pin checks. It reports the installed dependency, not a moving registry tag.
305
355
  The native payloads `@supernovae-st/nika-<os>-<arch>` are optional
@@ -318,19 +368,20 @@ deprecated`. The repository keeps its name (`supernovae-st/nika-client`).
318
368
 
319
369
  ## Scaffold with the engine
320
370
 
321
- The lowest-friction creation door is the engine-owned scaffold:
371
+ On the published 0.120.0 binary, `nika new` is **retired** (`unrecognized
372
+ subcommand`). The creation door is `compile`. Exact skeleton name or `hello`;
373
+ free-text intent stays incomplete and writes nothing:
322
374
 
323
375
  ```sh
324
376
  ./node_modules/.bin/nika init --project-file
325
- ./node_modules/.bin/nika new 01-hello hello.nika
377
+ ./node_modules/.bin/nika compile hello hello.nika
326
378
  ```
327
379
 
328
- `nika.yaml` is the project control plane. `hello.nika` is executable
329
- workflow intent and is the file passed to `check()` and `run()`. The scaffold
330
- writes the engine's own annotated `01-hello` example (its task is named
331
- `greet` and its prompt asks for French); the contract this README relies on is
332
- the `outputs.greeting` key and the `mock/echo` model, which the hand-written
333
- file above satisfies too.
380
+ No destination means preview only. `nika.yaml` is the project control plane
381
+ (needed for `nika serve`). `hello.nika` is the executable contract passed to
382
+ `check()` and `run()`. The engine's `hello` skeleton names its task `greet`
383
+ and asks for French; the hand-written file above is enough if it keeps
384
+ `outputs.greeting` and `model: mock/echo`.
334
385
 
335
386
  ## Verify a local trace
336
387
 
@@ -340,7 +391,12 @@ Pass that receipt back unchanged:
340
391
  ```ts
341
392
  if (!result.receipt) throw new Error('run did not issue a receipt');
342
393
  const proof = await nika.traceVerify(result.receipt);
343
- if (!proof.verified) throw new Error(proof.output ?? 'trace verification failed');
394
+ // proof.verified is the seal/binding, not the workflow outcome.
395
+ if (isNikaRunSucceeded(result) && proof.verified) {
396
+ // run succeeded and the journal is sealed
397
+ } else if (isNikaRunSucceeded(result) && !proof.verified) {
398
+ // succeeded, unsigned or otherwise unverified — read proof.reason
399
+ }
344
400
  ```
345
401
 
346
402
  The SDK does not implement cryptography or inspect the trace itself. It asks the
@@ -388,9 +444,8 @@ settles on the terminal the resident records, `cancelled`, `succeeded`,
388
444
  `failed`, or `interrupted` once its grace expired. A job that already ended
389
445
  replays its result with `accepted: false` and `status: 'already_settled'`. The
390
446
  native transport signals its process the same way; the result is whatever the
391
- engine then wrote, `cancelled` when it settled the request (the released
392
- 0.118.7 engine does), or `interrupted` when the process ended with no
393
- settlement frame.
447
+ engine then wrote, `cancelled` when it settled the request, or `interrupted`
448
+ when the process ended with no settlement frame.
394
449
 
395
450
  ## Connect to `nika serve`
396
451
 
@@ -646,8 +701,9 @@ A view opened late is seeded with every frame the session observed, or it is
646
701
  refused. It is never handed a shortened replay. How many frames a session
647
702
  retains is `eventBufferSize`, **4096 by default**.
648
703
 
649
- **The measured frame count: `3N + 3`, for one shape.** On the released 0.118.7
650
- engine, a clean run of N independent `mock/echo` `infer` tasks wrote
704
+ **The measured frame count: `3N + 3`, for one sealed shape.** On the released
705
+ 0.118.7 engine (and still the native 0.120.0 hello fixture when sealed), a
706
+ clean run of N independent `mock/echo` `infer` tasks wrote
651
707
  `workflow_started`, then `task_scheduled`, `task_started` and `task_completed`
652
708
  per task, then `workflow_completed` and `run_settled`. Two points were
653
709
  measured: 1 task is 6 frames, 90 tasks are 273. That is this fixture, not a
@@ -957,7 +1013,7 @@ The repository also carries 100 distinct use-case workflows and provider proof
957
1013
  under `gauntlet/`.
958
1014
 
959
1015
  <!-- engine hero pinned to the release tag it demonstrates · re-pin on lockstep bumps -->
960
- ![nika check audits the workflow, then runs and seals its trace](https://raw.githubusercontent.com/supernovae-st/nika/v0.118.7/media/nika-hero.gif)
1016
+ ![nika check audits the workflow, then runs and seals its trace](https://raw.githubusercontent.com/supernovae-st/nika/v0.120.1/media/nika-hero.gif)
961
1017
 
962
1018
  ## Keeping it fresh
963
1019
 
@@ -77,7 +77,9 @@ Some operations deliberately have one authority:
77
77
 
78
78
  1. `run()` resolves only after stable admission and returns an immutable
79
79
  `NikaRun` handle: `id`, `events()`, `result()`, `status()`, `cancel()`, and
80
- `done`, the compatibility alias of `result()`. Its members are closures
80
+ `done`, the compatibility alias of `result()`. Admission is not execution;
81
+ a succeeded `result()` is not a sealed receipt (`traceVerify` can return
82
+ `receipt_mismatch` on an UNSEALED journal). Its members are closures
81
83
  over the run's one session, so an extracted method still works. The handle
82
84
  owns the lifecycle and nothing else: checking, proof, catalogs and
83
85
  authoring stay on the facade. A run handle never means "maybe a run": a refusal
package/docs/testing.md CHANGED
@@ -52,7 +52,15 @@ starts is a 200 `cancelled` whose terminal is one of those two writer kinds,
52
52
  only with `status=cancelled`. The verifiers bind each cancel reply to the
53
53
  terminals it may lead to, demand the run status of that terminal, and refuse any
54
54
  other pairing. The parsed deterministic and packed-project results must match
55
- exactly except for the recovery job UUID. The hostile comparison excludes
55
+ exactly except for the recovery job UUID and the depth `package_sha256`.
56
+ That digest is provenance of the tarball this replay packed (README lives
57
+ inside the npm pack): the verifier requires `depth-package.json` beside the
58
+ ledger, hashes the artifact, checks filename/size/sha512 integrity, refuses
59
+ a missing or substituted pack, then compares behavior without requiring the
60
+ committed baseline digest (that digest is a labelled historical ledger
61
+ identity, not a re-hash of this run). A documentation-only
62
+ README change retargets the digest and must still reproduce every behavioral
63
+ verdict. The hostile comparison excludes
56
64
  `generated_at` and per-scenario duration and canonicalizes only the two ratified
57
65
  writer kinds of a cancelled terminal after checking the exact pairing. This proves that the
58
66
  attested public release currently reproduces the committed behavioral claims.
package/openapi.json CHANGED
@@ -1014,7 +1014,7 @@
1014
1014
  "info": {
1015
1015
  "description": "Authenticated loopback remote execution and declarative schedules. Artifacts, schedule list/delete/trigger/backfill, /v1/arm, and POST /v1/run are absent.",
1016
1016
  "title": "nika serve",
1017
- "version": "0.120.0"
1017
+ "version": "0.120.2"
1018
1018
  },
1019
1019
  "openapi": "3.1.0",
1020
1020
  "paths": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supernovae-st/nika",
3
- "version": "0.120.0",
3
+ "version": "0.120.2",
4
4
  "description": "One TypeScript SDK for local and remote Nika execution",
5
5
  "repository": {
6
6
  "type": "git",
@@ -69,10 +69,10 @@
69
69
  "prepublishOnly": "npm run build"
70
70
  },
71
71
  "optionalDependencies": {
72
- "@supernovae-st/nika-darwin-arm64": "0.120.0",
73
- "@supernovae-st/nika-darwin-x64": "0.120.0",
74
- "@supernovae-st/nika-linux-arm64": "0.120.0",
75
- "@supernovae-st/nika-linux-x64": "0.120.0"
72
+ "@supernovae-st/nika-darwin-arm64": "0.120.2",
73
+ "@supernovae-st/nika-darwin-x64": "0.120.2",
74
+ "@supernovae-st/nika-linux-arm64": "0.120.2",
75
+ "@supernovae-st/nika-linux-x64": "0.120.2"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@types/node": "^26.4.1",
@@ -87,7 +87,7 @@
87
87
  "license": "Apache-2.0",
88
88
  "mcpName": "io.github.supernovae-st/nika",
89
89
  "nikaRelease": {
90
- "preparedCommit": "5bfedbe293912d9cadde17badace85087007207d",
91
- "version": "0.120.0"
90
+ "preparedCommit": "ddf7522cac40e9606a7d94e83af74234e066a96e",
91
+ "version": "0.120.2"
92
92
  }
93
93
  }