@decidio/sdk 0.8.1 → 0.9.0

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 CHANGED
@@ -5,6 +5,47 @@ the gate, signing, receipts and the verifier - is conformance-locked between the
5
5
  first-run surfaces (the fork, `--demo`, the code sign-in) are TypeScript-first and named as such in the parity
6
6
  allowlist. Each entry names what changed, what a reader must do on upgrade, and what proved it.
7
7
 
8
+ ## 0.9.0 — the coding-agent hook catches what agents type, protects itself, and tells the approver what was asked
9
+
10
+ - **Wider matching, from real agent runs.** `sql-destructive` counts a statement sent through an interpreter or runner
11
+ (`python3 db.py "drop table x"`, `python3 -c`, `node -e`, a here-doc), checked per segment so a commit message that
12
+ says "drop table" is not gated; options are read by name before `=` (`--force-with-lease=<ref>`); git's `-C`/`-c` are
13
+ skipped; a command handed to a shell (`bash -c`, `sh -c`, `pwsh -Command`) is matched inside, two shells deep; a file
14
+ the command runs is followed one level inside the project (shell scripts, SQL files, `package.json` scripts, DROP or
15
+ TRUNCATE in a program file run directly) - the request carries the matched line as `scriptLine` and the file's sha256
16
+ in its keyed scope, never the file.
17
+ - **`decidio-guard`.** Write, Edit and MultiEdit to `.claude/settings*.json`, `~/.claude/settings*.json` or the Decidio
18
+ plugin's files are gated when the file after the change drops or alters a Decidio hook, sets `disableAllHooks`, turns
19
+ the plugin off, or is not valid JSON (adding a permission or another hook is not); shell writes to those files,
20
+ `claude plugin disable|uninstall` naming Decidio and `hook install --cli` are gated whatever they write. The request
21
+ binds tool + path + the after-file's sha256. `hook install` writes PreToolUse and PostToolUse handlers for the three
22
+ tools, one `if` per path (Claude Code's `if` holds one rule and is tool-specific), and keys its merge on event +
23
+ matcher + `if`: a re-run keeps each handler once and upgrades an 0.8 install in place.
24
+ - **A policy `proceed` prints nothing** (exit 0; the state is kept, so posttool still records the execution). Claude Code
25
+ records a hook `allow` as the hook's decision in place of its own permission check; a company rule that something
26
+ needs no person now leaves that check in place. A person's approval of the exact call still prints `allow`.
27
+ - **Card context.** The request context gains `mode`, `repo`, `branch`, `commit` and the user's prompt (`asked`, and
28
+ `askedBefore` when the last prompt is under 40 characters; each through the redaction, at most 1,000 characters;
29
+ `askedLength`), read from the transcript's tail only for a gated call. `DECIDIO_HOOK_SHARE_PROMPT=0` leaves the prompt
30
+ out. The request commitment no longer covers these (they change between the ask and the retry), so a retry after the
31
+ user typed "continue" matches its approval instead of a 409.
32
+ - **An agent running `approvals approve|reject`** (with the person's stored sign-in, which the agent's shell inherits) is
33
+ denied by the hook itself in its usual spellings (`npx`, `decidio`, the installed or cached `cli.js`, `python -m decidio`,
34
+ `uvx`, inside `bash -c`, an option before the subcommand); listing, `hook wait` and `receipt` are untouched. A shell
35
+ check is not a complete close (the Backlog names the server-side one).
36
+ - The prompt reader skips transcript entries that are not the user's words (a background task's notification, bash-mode
37
+ input and output, a local command's output, a compaction summary); a slash command the user typed counts. The settings
38
+ guard finds an Edit's string the way Claude Code's Edit does - verbatim, else with curly and straight quotes alike - and
39
+ an empty `old_string` on an empty or whitespace-only file as the whole new file.
40
+ - File-tool denials say "edit" and "Nothing was written."; shell denials are unchanged.
41
+
42
+ On upgrade: re-run `npx @decidio/sdk@0.9.0 hook install` (or update the plugin once it pins 0.9.0). A gate older than
43
+ this release stores the new context keys as ordinary details. Proved by: `cli-smoke.ts` (336 → 355 checks; the gated table 42 → 89
44
+ commands, verbatim from the lab's runs; the noise table 24 → 46; the guard's effect, binding, posttool spend and install;
45
+ a mock gate that 409s a reused key with another commitment, where a retry after the prompt changed is ALLOWED; mutation
46
+ runs of each change), and 15 real Claude Code sessions against a loopback gate (every gated case stopped; a policy
47
+ approval left Claude's classifier deciding).
48
+
8
49
  ## 0.7.0 — approvals bind to the exact request they approved; receipts pin to a real trust anchor
9
50
 
10
51
  - **The receipt trust anchor is built in — nobody pastes an issuer DID, and nothing pins to a receipt's own
package/README.md CHANGED
@@ -727,7 +727,7 @@ that fits every other confirm error.
727
727
  `quickstart cleanup` works later too.
728
728
  - `init [agentId] [--unique] [--force] [--expires-days N]` — sign in, register, mint the agent token, write `.env` (+ `.gitignore` entries). Refuses to overwrite a *different* agent's key in this directory; `--force` backs the old `.env` up first.
729
729
  - `doctor` — config, connectivity, and what your current token can do
730
- - `approvals [--mine] [--json]` / `approvals approve|reject <id> [reason]` — the human side, from your terminal (the list is workspace-wide; `--mine` narrows it to this directory's agent)
730
+ - `approvals [--mine] [--json]` / `approvals approve|reject <id> [reason]` — the human side, from your terminal (the list is workspace-wide; `--mine` narrows it to this directory's agent). Run by a coding agent behind the hook, `approve|reject` is refused: a person decides, not the agent
731
731
  - `receipt <id> [--json]` — download a sealed decision's Authority Receipt to a `.json` file (`--json`: stdout is the JSON result only; the identities your access covers print only on a terminal, or in the JSON)
732
732
  - `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. It pins to the trust anchor it prints first: `--issuer`, else the DID remembered for your Decidio at sign-in, else the built-in hosted-service issuer — never the receipt's own `issuer` field. Fully offline AND fully decoupled: it touches no network and reads no `.env` (only the issuer DIDs remembered in your profile) — a receipt verifies identically from any directory.
733
733
  - `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
@@ -738,6 +738,7 @@ that fits every other confirm error.
738
738
  - `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).
739
739
  - `login [email]` / `logout` — sign in ONCE (the CLI emails you an 8-character code; you type it) and forget that sign-in on this computer (the session token itself stays valid until it expires (up to 30 days) — there is no server-side revocation yet). 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).
740
740
  - `dev [--target URL] [--port N]` — local relay for the resume webhook (operator setups)
741
+ - `hook install [--project] [--approver <email>]` / `hook rules` / `hook wait <id>` / `hook justify <id> "<why>"` — the Claude Code hook (0.8.0; widened in 0.9.0). `install` writes PreToolUse/PostToolUse hooks into `.claude/settings.local.json` (`--project`: `settings.json`): the shell calls, plus Write/Edit/MultiEdit on Claude Code's own settings and the Decidio plugin (the `decidio-guard` category: a change that takes Decidio's check away needs a person). The pretool hook fails closed (`onFailure: block`, a hold capped under its 150 s timeout). `hook rules` lists what is gated, including the files a command runs that are followed one level. For a gated call the hook sends Decidio the command (secrets redacted), its directory, category, subject and any matched script line; the permission mode; the repository, branch and commit; and the user's last prompt in the session (with the one before it when the last is under 40 characters; at most 1,000 characters each, secrets redacted) - shown to people allowed to read the request, never on Slack or Teams. `DECIDIO_HOOK_SHARE_PROMPT=0` leaves the prompt out. A policy's auto-approval prints nothing, so Claude Code's own permission check still decides; a person's approval of the exact call allows it once.
741
742
 
742
743
  The CLI's stream contract: **stdout carries results only** (safe to pipe — `--json` output is
743
744
  pure JSON); errors, usage shown on *error* paths, and the per-run target-host announcement go
package/dist/cli.d.ts CHANGED
@@ -50,11 +50,42 @@ export type ParsedAdr = {
50
50
  export declare function parseAdr(text: string): ParsedAdr | null;
51
51
  export declare const AGENTS_MD_START = "<!-- decidio:start -->";
52
52
  export declare const AGENTS_MD_END = "<!-- decidio:end -->";
53
+ export type DeployRowView = {
54
+ decisionId: string;
55
+ dNumber: string | null;
56
+ repository: string;
57
+ environment: string;
58
+ sha: string;
59
+ ref: string;
60
+ state: string;
61
+ line: string;
62
+ checks: string | null;
63
+ requestedAt: string;
64
+ deployedAt: string | null;
65
+ runUrl: string | null;
66
+ recordUrl: string;
67
+ range: {
68
+ fromSha: string | null;
69
+ toSha: string;
70
+ compareUrl: string | null;
71
+ } | null;
72
+ };
73
+ /** PURE: one deploy as one line (plus its range line once deployed). */
74
+ export declare function deployLines(r: DeployRowView): string[];
75
+ /** PURE: what changed between two reads of the list (--watch) - a new deploy, or a deploy whose words moved. */
76
+ export declare function deployChanges(before: DeployRowView[], after: DeployRowView[]): string[];
77
+ /** PURE: a moment from the command line - an ISO date, "now", or a duration from now ("30m", "2h", "1d"). null: not one. */
78
+ export declare function parseWhen(s: string | undefined, now?: Date): Date | null;
79
+ /** PURE: the controls list, one line each. */
80
+ export declare function controlsLines(b: {
81
+ halts?: any[];
82
+ grants?: any[];
83
+ }): string[];
53
84
  /** Decidio's public GitHub Action (github.com/OmniTwin/decidio-actions): one action, step request or verify. It fetches the job's own
54
85
  * OIDC token (audience "decidio") - no repository secret - and fails the job in plain words when Decidio refuses. Pinned to the
55
- * v1.0.1 COMMIT, never a tag (invariant-guard 2026-10-20): the verify step enforces the approval in a production job with
86
+ * v1.3.1 COMMIT, never a tag (invariant-guard 2026-10-20): the verify step enforces the approval in a production job with
56
87
  * id-token: write, so a moved tag must not change it. Bump both pins together, with the release. */
57
- export declare const DECIDIO_ACTION = "OmniTwin/decidio-actions@c4506075600edef9576db821c4de756c300c15fe # v1.0.1";
88
+ export declare const DECIDIO_ACTION = "OmniTwin/decidio-actions@f0b43fcb8f8ac5e15d9ad43679e333ffa7038ae2 # v1.3.1";
58
89
  export declare const CHECKOUT_ACTION = "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0";
59
90
  export declare function githubRequestWorkflow(api: string, environment: string, branch?: string, deployWorkflow?: string): string;
60
91
  /** The verify step and the checkout of the approved commit: the first steps of any deploy job Decidio starts. */