@decidio/sdk 0.4.0 → 0.4.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/CHANGELOG.md +208 -0
- package/README.md +21 -6
- package/dist/adapters/_gate.d.ts +6 -6
- package/dist/adapters/_gate.js +1 -1
- package/dist/cli.d.ts +6 -1
- package/dist/cli.js +791 -171
- package/dist/inngest.d.ts +14 -6
- package/dist/resume.d.ts +7 -1
- package/dist/resume.js +61 -11
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,214 @@ Release notes for the client SDK. The Python package `decidio` ships the same ch
|
|
|
4
4
|
version (the two are conformance-locked twins). Each entry names what changed, what a reader must do
|
|
5
5
|
on upgrade, and what proved it.
|
|
6
6
|
|
|
7
|
+
## 0.4.3 — the stale-install gate, frames you can read at a glance, and a phone door that lands
|
|
8
|
+
|
|
9
|
+
The founder's UAT round 2, second half (2026-09-04). Nobody is on 0.4.2 yet, so it was never tagged; this carries both.
|
|
10
|
+
|
|
11
|
+
- **A stale install must not keep running its defects** (founder ruling). Network commands (`quickstart`, `init`,
|
|
12
|
+
`login`, `doctor`, `approvals`, `receipt`, `tokens`) ask the public registry once a day whether a newer version
|
|
13
|
+
exists — npm for the TypeScript CLI, PyPI for Python, never our servers, cached in the user's profile, silent when
|
|
14
|
+
offline. If one does, an interactive run asks `<latest> is available (you have <yours>). Upgrade now? [y/n]`: `y`
|
|
15
|
+
runs the package manager's own upgrade (`npm i -g @decidio/sdk@latest` / `pip install -U decidio`, with its
|
|
16
|
+
integrity checks) and re-runs your command on the new version; `n` exits 1 naming the command to run when ready.
|
|
17
|
+
Under `npx` there is nothing installed to upgrade, so it prints the `@latest` form and exits. A headless run prints
|
|
18
|
+
the notice and continues; `DECIDIO_REQUIRE_LATEST=1` makes it exit instead (pinned CI is a choice, not a
|
|
19
|
+
surprise). `DECIDIO_NO_UPDATE_CHECK=1` skips the check. `verify`, `--help` and `--version` stay fully offline.
|
|
20
|
+
Seven vectors in both suites: not newer prints nothing; headless + newer notices and runs; the strict flag exits 1;
|
|
21
|
+
interactive `n` exits 1 running nothing; interactive `y` runs the upgrade (a labelled dry-run in the suite); an
|
|
22
|
+
unreachable registry is silent; `verify` never checks.
|
|
23
|
+
- **Frames carry a tone.** ③ (the choice) in orange, ④ and the summary in green for an approval and red for a
|
|
24
|
+
rejection — the frame and its title only, 256-colour orange with a yellow fallback, nothing under `NO_COLOR`
|
|
25
|
+
or a pipe. Founder: "it's hard for the eye to pick out which box means what."
|
|
26
|
+
- **The phone door lands.** iOS opens the installed app for `app.decidioai.com/q/*` (the association file now
|
|
27
|
+
claims it); the app consumes the code, signs in, and opens the decision. A phone without the app lands in its
|
|
28
|
+
browser on the phone build (`/phone/q/<code>`), the same single-decision screen the app shows. The desktop landing
|
|
29
|
+
is a hand-off page: the one decision on stage with its actions in view, the queue a click away (`?stage=1`).
|
|
30
|
+
- **`quickstart --phone web`** is the founder's test fork: the QR points at the phone web rendering, a path the app
|
|
31
|
+
never claims, so it opens in the browser even with the app installed. The default QR opens the app when it is
|
|
32
|
+
there. (`--demo` will pick the sandbox the same way, once it exists.)
|
|
33
|
+
- Founder decisions recorded (backlog): the sandbox for uninvited developers is TOUR-ONLY in v1 (no own agents —
|
|
34
|
+
surface risk and a second policy to maintain), its boxes are torn down at cleanup or by a one-hour sweeper, and
|
|
35
|
+
its phone surface is the web page, never the native app.
|
|
36
|
+
|
|
37
|
+
## 0.4.2 — the zero-miss release: three gates, and every finding of the two 0.4.1 reviews
|
|
38
|
+
|
|
39
|
+
Built to the plan in `docs/Decidio_SDK_Zero_Miss_Plan.md` after an outside engineer's first-run review of 0.4.1 (B+)
|
|
40
|
+
and the prodsec retest of 0.4.1 (A-, safety A). Every item of both reports has a disposition below; the gates that
|
|
41
|
+
would have caught them now run on every pull request, on Linux AND Windows.
|
|
42
|
+
|
|
43
|
+
**The founder's UAT, round 2 (2026-09-04), folded in before the tag.** Steps 0–6 green on the previous build; the
|
|
44
|
+
web-app door landed on home (fixed twice the same night: the desktop's deep link is `/queue?focus=<id>`, and a browser
|
|
45
|
+
with no session bounced to `/login` before the link was consumed), and approving from the queue proved the terminal
|
|
46
|
+
follows an outside decision. Then six findings on the tour itself, all taken but one (which became a founder call):
|
|
47
|
+
- **One line per event.** The tour's paragraphs are gone from the default output: every step is a frame with two to
|
|
48
|
+
four terse rows, the outcome is one line, the reminder is two, and "Where next" is one. `--explain` prints the
|
|
49
|
+
narration that used to be the default. Target met: the approval tour is about 40 lines without the QR.
|
|
50
|
+
- **Every step framed** (①②③④ and the summary), with an escape-aware word wrap so a styled row never overflows a
|
|
51
|
+
narrow window; continuation lines hang under the label column.
|
|
52
|
+
- **A short hand-off link.** `POST /demo/session-link` now returns `https://app.decidioai.com/q/<13-char code>`
|
|
53
|
+
instead of a 110-character URL: it fits the frame, stays clickable AND copyable, and the credential row remembers
|
|
54
|
+
which decision the code was minted for (`handoff_decision_id`, cleared in the same single-use burn), so the web
|
|
55
|
+
app's new `/q/<code>` route signs in and lands on that decision. A hand-off never flips `emailVerified`.
|
|
56
|
+
- **A small QR, still printed by default** (the founder's call: "it looks impressive in a demo"). The QR carries the
|
|
57
|
+
short link upper-cased, so the encoder uses its alphanumeric mode, at the lowest error-correction level with a
|
|
58
|
+
one-module quiet zone: 25 modules instead of 41, about 14 rows instead of 23.
|
|
59
|
+
- **The lab is invite-only, and the CLI says so.** An uninvited address running `login` used to be told "we emailed
|
|
60
|
+
you a link" and wait for nothing (the server's anti-enumeration answer). The CLI now says a link arrives only for
|
|
61
|
+
an invited address and where to request access.
|
|
62
|
+
- **"The wrapper IS the SDK."** The first sentence of the help (and the READMEs) settles the SDK-versus-wrapper
|
|
63
|
+
question: `guard.protect(fn, describe)` ships in this package; the CLI is the tour, the sign-in and the token admin.
|
|
64
|
+
- Founder calls recorded, not built: a sandbox tenant with device-bound identities and a single-decision preview page
|
|
65
|
+
for developers who are not invited (the SDK's distribution unlock); scan → native app → the decision (iOS claims
|
|
66
|
+
`/d/*` for the installed app; the QR opens Safari today); `record kind: agent-receipt` wording.
|
|
67
|
+
|
|
68
|
+
**Round 2, second look (2026-09-04, same night).** The founder re-ran on the deployed build: web-app approval and the
|
|
69
|
+
deep link now work; the QR was MISSING; the text above and below the frames was still dense. Fixes: (1) `qrcode@1.5`'s
|
|
70
|
+
half-block renderer throws "Invalid array length" on an ODD margin, so the 1-module quiet zone silently produced no QR
|
|
71
|
+
(the server logged it, the CLI showed nothing) - margin 2 with level L renders 25 modules in 13 rows, and the CLI now
|
|
72
|
+
says when no QR came back instead of dropping the row; (2) the sign-in block is six lines instead of eleven (one intro,
|
|
73
|
+
prompts that carry their own hints, one signed-in line); (3) the tour no longer prints the stderr "decidio →" host line
|
|
74
|
+
when its own Target line already said it; (4) ④ is three short rows (VALID + file / record kind + evidence / issuer)
|
|
75
|
+
instead of one wrapping line; (5) the reminder is two lines, the take-away has a breath around it.
|
|
76
|
+
|
|
77
|
+
**The founder's UAT pass (2026-09-03), folded in before the tag.** The script's steps 1–5 went green in a real
|
|
78
|
+
PowerShell; step 6 stopped on the sign-in: every command that needed the workspace asked for a fresh emailed link,
|
|
79
|
+
and "nobody would log in this many times". Ruled: sign in ONCE, no better and no worse than the web app; and a
|
|
80
|
+
human step you can believe from outside the terminal.
|
|
81
|
+
- **Sign in once.** `login [email]`: the CLI requests the emailed link itself, you paste it; the 30-day session is
|
|
82
|
+
kept in your user profile (`%LOCALAPPDATA%\decidio` / `~/.config/decidio`, owner-only, written through the same
|
|
83
|
+
atomic secret writer as `.env`), bound to the terminal window that created it (a second window gets its own) and to
|
|
84
|
+
the token's expiry. `logout` ends it; so does `quickstart cleanup` (the tour's sign-in ends with the tour, the way
|
|
85
|
+
the web app drops its session on sign-out). An exported `DECIDIO_SESSION_TOKEN` still outranks it for headless
|
|
86
|
+
shells. No server-side session revocation exists yet (the web app has none either); the stored token is discarded.
|
|
87
|
+
- **Three doors at the human step.** The ③ frame offers: answer **here** (y/n), open the decision in the **web app**
|
|
88
|
+
(a link that lands on it signed in), or scan a **QR** with **your phone** (the same link). The link comes from a
|
|
89
|
+
new authenticated endpoint, `POST /demo/session-link {decisionId}`: it mints the SAME single-use sign-in token the
|
|
90
|
+
emailed link carries, for the caller's own credential, five minutes instead of fifteen because it is shown on a
|
|
91
|
+
screen and lands in terminal scrollback, and it replaces any pending emailed link (one slot per credential). It
|
|
92
|
+
grants a sign-in and only a sign-in: the token is prefixed `h.` and the password-reset door refuses it, so a
|
|
93
|
+
session can move itself to a browser but never mint itself a durable password (invariant-guard block, fixed, with a
|
|
94
|
+
negative-smoke lock). The terminal races the prompt against the gated call: a decision
|
|
95
|
+
made in the app or on the phone closes the prompt, the CLI posts no approve/reject of its own, says "approved (or
|
|
96
|
+
rejected) in the web app (or on your phone) — this terminal saw it and followed", and the function runs
|
|
97
|
+
once, after the approval, exactly as before. If the hand-off endpoint fails, the terminal door still works alone.
|
|
98
|
+
- **Layout.** Each step is a headed section; the doors and the closing summary are frames sized to their content and
|
|
99
|
+
the terminal; the summary is a table (gated / decided / executed / receipt); the help page is in sections. Colour,
|
|
100
|
+
bold and clickable (OSC-8) links only on a TTY and never under `NO_COLOR`; every phrase the gates assert is unchanged.
|
|
101
|
+
- **Two platform defects found by the doors vector, both fixed:** on Windows a console read abandoned when the app
|
|
102
|
+
decided first kept libuv's reader thread blocked and Node's teardown waited forever (the process printed its last
|
|
103
|
+
line and never exited) — the CLI now drains stdout/stderr and exits explicitly on that path only; and Python's
|
|
104
|
+
`isatty()` is true for `NUL`, so the console-key reader spun on a headless run — a real console is now detected
|
|
105
|
+
through `GetConsoleMode` (and that same check now decides whether the CLI may prompt at all, so `NUL` is headless on every OS), pipes and `NUL` read on a thread, POSIX uses `select`.
|
|
106
|
+
- Vectors, both languages: `login` under a real terminal (email → link → stored session); the next command in the
|
|
107
|
+
same terminal asks nothing; another terminal is refused; `logout` removes the file; a headless tour on the stored
|
|
108
|
+
session; cleanup signs out; and the browser-decided tour (the fake gate approves after two polls; the CLI must post
|
|
109
|
+
no approval, run the function once, exit 0).
|
|
110
|
+
|
|
111
|
+
**Gates (structural):**
|
|
112
|
+
- **The stranger run.** Both CLI suites drive the interactive tour under a REAL terminal on every platform through one
|
|
113
|
+
shared driver (`packages/conformance/tty_driver.py`: POSIX pty, Windows ConPTY). `y`, `n` and three blank answers
|
|
114
|
+
are exercised end to end; a missing terminal driver is red on CI, never a skip. A `windows-latest` gates job runs
|
|
115
|
+
both CLI suites under ConPTY.
|
|
116
|
+
- **Claims as vectors.** `packages/conformance/vectors/claims.json`: retired phrases are tombstones that fail the build
|
|
117
|
+
if they reappear in shipped text; the help must carry what is true (`y approves`, `n rejects`, exit 3 at the human
|
|
118
|
+
moment, `--help` anywhere, `receipt <id> [--json]`); the no-answer stop's exit code is asserted from what the help
|
|
119
|
+
promises. The tombstone gate found a Python twin of a 0.4.1 text fix that had been missed.
|
|
120
|
+
- **Twin parity.** `packages/conformance/parity.mjs`: the vector labels of the TS and Python suites must match; a
|
|
121
|
+
one-sided vector is red unless `parity-allowlist.json` names it with a reason. The pre-0.4.2 asymmetry is
|
|
122
|
+
recorded as a baseline to burn down before 1.0.
|
|
123
|
+
- **No swallowed ledger read.** `packages/conformance/sdk-lint.mjs` refuses `.catch(() => undefined)`,
|
|
124
|
+
`except Exception: x = None` and empty catches around ledger and journal reads; it was red on seven such sites in
|
|
125
|
+
0.4.1 and is green now, with two justified allowances that carry their reason.
|
|
126
|
+
|
|
127
|
+
**Fixed, both languages unless noted:**
|
|
128
|
+
- The help said "YOUR Enter is the approval" (false since 0.4.1); it now says y approves, n rejects, Enter is not an
|
|
129
|
+
answer. `--help`/`-h` after any command prints the one help page (exit 0); the bare word `help` past the usage
|
|
130
|
+
positions stays a positional. `receipt <id> [--json]` is documented.
|
|
131
|
+
- The rejection half of the tour downloads and verifies the DENIAL's receipt, pinned, and names its record kind; all
|
|
132
|
+
three verifier doors print `kind:` (read from the receipt's own canonical record). The approval half names its kind
|
|
133
|
+
too. The tour's receipt goes through the same no-overwrite writer as the `receipt` command.
|
|
134
|
+
- Receipt filenames escape rather than strip: `a.b` and `ab` can no longer share a file (`~` + hex code point).
|
|
135
|
+
- The unset-session reminder prints on every end of the tour (approved, rejected, stopped) with both the bash and the
|
|
136
|
+
PowerShell command; the README carries both.
|
|
137
|
+
- An answer typed after the tour's blocking call expired (5 minutes) is not posted; the tour says the request must be
|
|
138
|
+
decided in the queue.
|
|
139
|
+
- `resolve()` on a decision whose entry was consumed between reading and claiming it now reads the ledger through one
|
|
140
|
+
shared mapping: `already_resolved` when it ran, never "durable state undefined" or a false live-attempt claim; the
|
|
141
|
+
resolved hook fires for every non-`unknown` outcome including the ledger-fallback ones.
|
|
142
|
+
- **A corrupt terminal ledger head is loud** (prodsec V041-01): `resolve()` propagates the state-corrupt error instead
|
|
143
|
+
of returning `unknown`; the poll worker says it once per decision and keeps serving the other actions; the webhook
|
|
144
|
+
doors answer 500 naming the error.
|
|
145
|
+
- A landed `init` token whose `persisted` journal note failed to write is held, not an orphan (the credential file is
|
|
146
|
+
the proof); the failed note is reported, never silent. `quickstart cleanup` names a held token instead of saying
|
|
147
|
+
the journal owes nothing (prodsec V041-02).
|
|
148
|
+
- The tour prints the call it actually makes (TS: the blocking primitive, with the note that real agents use
|
|
149
|
+
`guard.protect`; Python already ran `guard.protect(..., mode="blocking")` and now prints that too).
|
|
150
|
+
- Node relay: `--target` is never echoed raw, even on the shape-check refusal; the relay binds `127.0.0.1`.
|
|
151
|
+
|
|
152
|
+
**Guard round (one, by ruling): no block, six warnings, all taken.** The verifiers no longer print a record kind on an
|
|
153
|
+
INVALID receipt (an attacker-supplied field on a rejected document); Python's `receipt --json` keeps stdout JSON-only
|
|
154
|
+
and both suites now assert that on a stdout-only capture; an unreadable store directory is corruption, not "no
|
|
155
|
+
revisions", and an unlistable store makes the worker say so once instead of idling silently; the lint reaches
|
|
156
|
+
directory listings and multi-form excepts and refuses a `lint-allow` without a reason; the three-door gate carries a
|
|
157
|
+
correctly signed DENIAL fixture and all three doors print `kind: denial` (69/69); the claims scan walks the adapters too.
|
|
158
|
+
|
|
159
|
+
**Not reproduced:** the reviewer saw the no-answer stop exit 1 in PowerShell; under ConPTY on Windows and under a pty
|
|
160
|
+
on Linux both CLIs exit 3, and the gates now assert that code from the help's own table on both platforms.
|
|
161
|
+
|
|
162
|
+
**Unchanged by ruling:** adapters stay Beta (no real-engine suites yet); provenance and repository metadata stay
|
|
163
|
+
post-1.0.
|
|
164
|
+
|
|
165
|
+
## 0.4.1 — "no" means no
|
|
166
|
+
|
|
167
|
+
Two first-run defects found by an outside engineer within sixteen minutes of installing 0.4.0. Both were
|
|
168
|
+
in the CLI's own tour and cleanup, not in the SDK's gate, ledger or verifier; both are fixed in both
|
|
169
|
+
languages, and the two blind spots that let them ship are now test axes.
|
|
170
|
+
|
|
171
|
+
- **The tour's approval prompt is a question.** "Press Enter to approve" accepted ANY completed line —
|
|
172
|
+
the literal `no` included — as approval, because the answer was never read. Now `y` approves, `n`
|
|
173
|
+
REJECTS (the tour rehearses the rejection half: the request is refused, your function does not run, and
|
|
174
|
+
the tour says so), and anything else asks again; three non-answers stop the tour with the decision still
|
|
175
|
+
pending (exit 3) rather than guess. The rule is one pure function, `approvalAnswer`, driven by a shared
|
|
176
|
+
corpus (`packages/conformance/vectors/consent-answers.json`) in both languages, and the interactive
|
|
177
|
+
branch itself is exercised under a real terminal on the Linux gate. The automated `--auto-approve-demo`
|
|
178
|
+
path is unchanged and still labeled as automated authority.
|
|
179
|
+
- **A lost `.env` no longer loses the token.** 0.3.5's recovery journal retired a token id the moment its
|
|
180
|
+
credential file was written, so moving or deleting `.env` afterwards left `quickstart cleanup` saying the
|
|
181
|
+
journal owed nothing over a live seven-day token. Only a revocation retires a journal entry now. Cleanup
|
|
182
|
+
revokes tour tokens from the journal even when the credential file is gone, and still revokes a token
|
|
183
|
+
whose credential file never landed (the double fault the journal was built for). It never revokes an
|
|
184
|
+
`init` token that DID land: one a credential file here holds is named and left alone; one that was written
|
|
185
|
+
to a file no longer found here is named with the `tokens revoke` command (it may simply live elsewhere);
|
|
186
|
+
`doctor` shows a held token as live-and-held. **Upgrading from 0.3.5/0.4.0:** rows those versions retired as `persisted` count as
|
|
187
|
+
outstanding again. A tour you already cleaned up will show in `doctor` until you run
|
|
188
|
+
`npx @decidio/sdk quickstart cleanup` once, which reports the token as already revoked and retires the row.
|
|
189
|
+
- **Truth and disclosure.** The failure text after a refused registration no longer claims nothing was
|
|
190
|
+
written (the `.gitignore` entries were); the preflight discloses the five-minute wait for your answer and
|
|
191
|
+
that receipts are shareable but carry business metadata; cleanup names the retained journal; the
|
|
192
|
+
worker's "prints once" is "once per worker process"; the tour tells you to unset `DECIDIO_SESSION_TOKEN`
|
|
193
|
+
before launching agent code from the same shell.
|
|
194
|
+
- **`dev --target` obeys the wire rule:** the relay refuses a remote plaintext `http://` target, since the
|
|
195
|
+
forwarded body is signed resume material.
|
|
196
|
+
- **A race loser says so** (prodsec retest of 0.4.0, V040-01). A resume signal that finds the pre-invocation
|
|
197
|
+
claim held by another live attempt, or loses the invocation-boundary race to one, answers the new additive
|
|
198
|
+
status `lost_race` (it did nothing; the winner reports). It used to read `unknown` — the word for "not
|
|
199
|
+
ours" — and `no_handler` although the handler was registered. A boundary loss to a DENIAL is final and
|
|
200
|
+
answers `already_resolved` with the denial named; a store fault at the boundary answers `lost_race` with
|
|
201
|
+
"nothing ran … a later signal may retry". A signal that arrives after the entry was consumed now consults
|
|
202
|
+
the ledger: already ran or denied → `already_resolved`; in flight → `indeterminate`; a claim held by another
|
|
203
|
+
attempt → `lost_race`; only a decision with no record at all is `unknown`. Exhaustive switches over the
|
|
204
|
+
status union gain one case.
|
|
205
|
+
- **Adapter declarations aligned** (V040-02): the Temporal compatibility floor checked at bind time is now the
|
|
206
|
+
same `>=1.9 <2` the package declares; the peer-dependency comment says what the manifest does (optional
|
|
207
|
+
peers, checked at bind time); the Inngest step outcome is a discriminated union, so `confirmation` is
|
|
208
|
+
statically present when `status` is `"executed"`. Adapters stay Beta; real-engine evidence (V040-03) and
|
|
209
|
+
provenance metadata (V040-04) remain open and disclosed.
|
|
210
|
+
- Deliberately not changed, recorded in the backlog for a product decision: per-subcommand `--help`, the
|
|
211
|
+
tour showing the exact call it makes (`withApproval` in blocking mode rather than the `guard.protect`
|
|
212
|
+
line it prints as the takeaway), dot-stripping in receipt filenames, and a shared no-overwrite receipt
|
|
213
|
+
writer for the tour.
|
|
214
|
+
|
|
7
215
|
## 0.4.0 — the scope statement
|
|
8
216
|
|
|
9
217
|
- **README: "What 0.4 does not yet claim" and "Compatibility and support."** The stability promise is
|
package/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
One line to put a human-or-policy approval gate in front of any AI-agent action — and get a signed, verifiable Authority Receipt for every decision, with execution evidence accruing on the record.
|
|
4
4
|
|
|
5
5
|
`@decidio/sdk` is the client for Decidio — the system of record for enterprise authority, from OmniTwin Technologies.
|
|
6
|
+
The wrapper IS the SDK: `guard.protect(fn, describe)` ships in this package, and the `npx @decidio/sdk` command line
|
|
7
|
+
is the two-minute tour, the sign-in and the token admin around it. Install, wrap one function, run — the tour is optional.
|
|
6
8
|
|
|
7
9
|
## The two-minute tour
|
|
8
10
|
|
|
@@ -16,7 +18,11 @@ you approve right there (or from your Decidio app), executes on approval, then d
|
|
|
16
18
|
sealed Authority Receipt and verifies it offline in front of you, pinned to your workspace's
|
|
17
19
|
issuer. It mints a **7-day** tour token (`init` mints 90-day tokens — different jobs, different
|
|
18
20
|
lifetimes) and ends by offering to clean itself up (`quickstart cleanup` works later too).
|
|
19
|
-
Sign in when
|
|
21
|
+
Sign in once — `npx @decidio/sdk login`, or when the tour asks: the CLI emails you a link, you paste it, and
|
|
22
|
+
the sign-in is kept in your profile (owner-only) for 30 days, or until this terminal closes, `logout`, or
|
|
23
|
+
`quickstart cleanup`. At the human step the tour offers three doors: answer here, open the decision in
|
|
24
|
+
the web app (a one-time link, signed in for you), or scan the QR with your phone — whichever answers
|
|
25
|
+
first wins, and the terminal follows. No config: with no `DECIDIO_API_URL` set it targets the hosted synthetic
|
|
20
26
|
sandbox. Headless, the tour **stops at the routed decision** — approving is a human act, so it
|
|
21
27
|
prints the approve command and exits rather than approve its own request (CI can pass
|
|
22
28
|
`--auto-approve-demo`, which labels that authority as automated).
|
|
@@ -78,10 +84,15 @@ What `.env` contains after a successful init: `DECIDIO_API_URL`, `DECIDIO_AGENT_
|
|
|
78
84
|
`DECIDIO_WORKSPACE_ID`, and `DECIDIO_WEBHOOK_SECRET` (operator deployments only). Pick any
|
|
79
85
|
agent name — if it's already taken in your workspace, `init` stops cleanly and suggests a
|
|
80
86
|
fresh one (or pass `--unique` to auto-append a random suffix — collision-proof for shared
|
|
81
|
-
workspaces and CI).
|
|
87
|
+
workspaces and CI). Interactive terminals sign in once (`npx @decidio/sdk login` — stored owner-only in your
|
|
88
|
+
profile, bound to this terminal window, 30 days). Headless terminal (no prompts possible)? Export `DECIDIO_SESSION_TOKEN` first:
|
|
82
89
|
request a sign-in link, then exchange its `?login=` value at `POST /demo/login-consume` — the
|
|
83
90
|
response's `token` is your session token (the CLI prints these exact instructions when it
|
|
84
|
-
can't prompt).
|
|
91
|
+
can't prompt). That session token is your workspace login, not the agent's credential: unset it
|
|
92
|
+
before launching agent code from the same shell, because child processes inherit exported
|
|
93
|
+
variables (`unset DECIDIO_SESSION_TOKEN` in bash/zsh, `Remove-Item Env:DECIDIO_SESSION_TOKEN` in
|
|
94
|
+
PowerShell). In the tour's human step, `y` approves and `n` rejects — the rejection half is worth
|
|
95
|
+
watching once: the request is refused and your function does not run.
|
|
85
96
|
|
|
86
97
|
**3. Protect one function** — save this as `quickstart.mjs`:
|
|
87
98
|
|
|
@@ -636,7 +647,10 @@ that fits every other confirm error.
|
|
|
636
647
|
- `receipt <id>` — download a sealed decision's Authority Receipt to a `.json` file
|
|
637
648
|
- `verify [--issuer <did>] <file>...` — verify receipts offline (the canonical verifier; the standalone `@decidio/verify` package is the same implementation, for auditors who want zero Decidio dependency — the sdk depends on the verifier released alongside it, at the same version). Unknown flags are refused, never ignored. Fully offline AND fully decoupled: it touches no network and reads no `.env` — a receipt verifies identically from an empty directory.
|
|
638
649
|
- `tokens list [--mine]` / `tokens revoke <jti>` — see and kill agent tokens (values are never re-shown; the jti is the revocation handle). Each row carries its status — `active`, `expired`, or `REVOKED <date>` — so the list confirms the revocations it performs; re-revoking is an honest no-op (exit 1). The list is workspace-wide (a shared workspace shows every member's agents); `--mine` narrows to this directory's agent
|
|
639
|
-
- `quickstart cleanup` — revoke the tour's token and delete its local credentials. What stays, honestly: sealed records (permanent by design) and the agent's registration row (inert without key or token; server-side deregistration is on the roadmap)
|
|
650
|
+
- `quickstart cleanup` — revoke the tour's token and delete its local credentials. What stays, honestly: sealed records (permanent by design) and the agent's registration row (inert without key or token; server-side deregistration is on the roadmap) Also ends the stored sign-in.
|
|
651
|
+
- **Update check.** Network commands ask the public npm registry once a day whether a newer version exists; if so, an interactive run asks `Upgrade now? [y/n]` first (`y` upgrades through npm and re-runs your command; `n` exits), a headless run prints the notice and continues (`DECIDIO_REQUIRE_LATEST=1` makes it exit). `DECIDIO_NO_UPDATE_CHECK=1` skips it. Nothing is sent to Decidio; `verify` never checks.
|
|
652
|
+
- `quickstart --phone web` — the QR at the human step opens the phone web rendering even when the Decidio app is installed (by default it opens the app when there is one).
|
|
653
|
+
- `login [email]` / `logout` — sign in ONCE (the CLI emails you a link; you paste it) and end that sign-in. The session is stored in your user profile, owner-only, bound to the terminal window that created it, for 30 days; `DECIDIO_SESSION_TOKEN` in the environment outranks it (headless shells).
|
|
640
654
|
- `dev [--target URL]` — local relay for the resume webhook (operator setups)
|
|
641
655
|
|
|
642
656
|
The CLI's stream contract: **stdout carries results only** (safe to pipe — `--json` output is
|
|
@@ -644,8 +658,9 @@ pure JSON); errors, usage shown on *error* paths, and the per-run target-host an
|
|
|
644
658
|
to **stderr**. Asked-for help (an explicit `--help`, or invoking the bare CLI) is itself a
|
|
645
659
|
result and prints on stdout. Exit codes: 0 success · 1 failure or honest no-op · 2 usage error · 3 `quickstart` stopped at the human moment (headless: the decision routed, but nothing executed and no receipt was written, so the run is deliberately not reported as a completed tour).
|
|
646
660
|
|
|
647
|
-
Commands that administer your workspace (`init`, `approvals`, `receipt`) use a **session**
|
|
648
|
-
sign-in
|
|
661
|
+
Commands that administer your workspace (`init`, `approvals`, `receipt`, `tokens`) use a **session**
|
|
662
|
+
sign-in: `login` stores it in your user profile (owner-only, bound to this terminal window, 30 days;
|
|
663
|
+
`logout` or `quickstart cleanup` ends it), or `DECIDIO_SESSION_TOKEN` supplies it for headless shells. What lands on disk in `.env`: the agent's floor-limited
|
|
649
664
|
API token, its locally-generated private signing key, and a webhook secret (used only in
|
|
650
665
|
operator deployments) — `init` adds `.env` to `.gitignore` for you; move secrets to a secret
|
|
651
666
|
manager for production.
|
package/dist/adapters/_gate.d.ts
CHANGED
|
@@ -27,12 +27,12 @@ export declare function gateConfirm(config: GuardConfig, decisionId: string, res
|
|
|
27
27
|
export declare function verdictFrom(payload: any): string;
|
|
28
28
|
/** Throw the right error for a non-approving verdict; return for approved/auto_approved. */
|
|
29
29
|
export declare function assertVerdict(verdict: string, decisionId: string, reason?: string): void;
|
|
30
|
-
/** Engine versions these adapters are written and tested against.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
30
|
+
/** Engine versions these adapters are written and tested against. The two engines that ARE declared in
|
|
31
|
+
* package.json are declared as OPTIONAL peers (peerDependenciesMeta), so a consumer who uses none of them
|
|
32
|
+
* installs nothing extra; Inngest and the OpenAI Agents SDK are not declared at all. Either way the
|
|
33
|
+
* contract is CHECKED here at bind time — a mismatch names the version it found, the range it wants, the
|
|
34
|
+
* beta status and the exact remediation, rather than failing somewhere deep in the engine later. These
|
|
35
|
+
* ranges and package.json's peer ranges are the SAME numbers (prodsec V040-02: they had drifted). */
|
|
36
36
|
export declare const ENGINE_RANGES: Record<string, {
|
|
37
37
|
pkg: string;
|
|
38
38
|
min: string;
|
package/dist/adapters/_gate.js
CHANGED
|
@@ -105,7 +105,7 @@ export function assertVerdict(verdict, decisionId, reason) {
|
|
|
105
105
|
}
|
|
106
106
|
export const ENGINE_RANGES = {
|
|
107
107
|
langgraph: { pkg: "@langchain/langgraph", min: "0.2.0", lessThan: "2.0.0" },
|
|
108
|
-
temporal: { pkg: "@temporalio/workflow", min: "1.
|
|
108
|
+
temporal: { pkg: "@temporalio/workflow", min: "1.9.0", lessThan: "2.0.0" },
|
|
109
109
|
openai: { pkg: "@openai/agents", min: "0.0.1", lessThan: "2.0.0" },
|
|
110
110
|
inngest: { pkg: "inngest", min: "3.0.0", lessThan: "4.0.0" },
|
|
111
111
|
};
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
2
|
+
export type ApprovalAnswer = "approve" | "reject" | "again";
|
|
3
|
+
export declare function approvalAnswer(answer: unknown): ApprovalAnswer;
|
|
4
|
+
/** `decidio-receipt-<id>.json`, with every character outside [A-Za-z0-9_-] escaped as `~` + hex code point, so two
|
|
5
|
+
* distinct decision ids can never share a file (first-run review of 0.4.1: dots were STRIPPED, `a.b` and `ab`
|
|
6
|
+
* collided). `~` is outside the allowed set, so the escape is unambiguous. Exported for the smokes. */
|
|
7
|
+
export declare function receiptFileName(id: string): string;
|