@decidio/sdk 0.7.0 → 0.8.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/README.md +1 -1
- package/dist/adapters/_gate.d.ts +20 -0
- package/dist/adapters/_gate.js +16 -1
- package/dist/cli.d.ts +50 -0
- package/dist/cli.js +1696 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/resume.d.ts +12 -2
- package/dist/resume.js +12 -0
- package/dist/signing.d.ts +5 -0
- package/dist/signing.js +3 -0
- package/openapi.yaml +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -247,7 +247,7 @@ That's the whole contract: gate → human decision → your execution → owned,
|
|
|
247
247
|
## What happens on a call
|
|
248
248
|
|
|
249
249
|
1. The wrapper asks Decidio's gate to authorize `{action, amount, scope}` for your `agentId`.
|
|
250
|
-
2. Decidio's policy engine (Cedar) returns **proceed | route | block**:
|
|
250
|
+
2. Decidio's policy engine — your workspace's active policy, whose versioned rules decide every request (Cedar is compiled from them for validation and export) — returns **proceed | route | block**:
|
|
251
251
|
- **proceed** — your function runs immediately (auto-approved under a named, versioned rule).
|
|
252
252
|
- **route** — the action suspends (durable) or waits (blocking) for a human decision in Decidio's queue, then runs your function (or throws `DecidioRejectedError`).
|
|
253
253
|
- **block** — the wrapper throws `DecidioBlockedError`; your function never runs.
|
package/dist/adapters/_gate.d.ts
CHANGED
|
@@ -37,6 +37,26 @@ export declare function gateCheckApproval(config: GuardConfig, decisionId: strin
|
|
|
37
37
|
* Inngest step memo). Anything that is not a well-formed check is refused as unverifiable - the same
|
|
38
38
|
* "an answer you do not understand is never permission" rule the verdict allow-list applies. */
|
|
39
39
|
export declare function assertApprovalCheckShape(raw: unknown, decisionId: string): ApprovalCheck;
|
|
40
|
+
/** REFUSAL TWINS (chat approvals 2026-10-15 round 2): the one answer outside the allow-list, and only for a caller that ASKED for
|
|
41
|
+
* the check (`twinCheck`). It is not permission - nothing was created; the caller tells the user and re-asks with their
|
|
42
|
+
* justification. gateAuthorize (every adapter and the core path) never asks, so it can never receive one. */
|
|
43
|
+
export interface GateDuplicate {
|
|
44
|
+
decision: "duplicate";
|
|
45
|
+
duplicateOf: {
|
|
46
|
+
decisionId: string;
|
|
47
|
+
displayNumber?: string | null;
|
|
48
|
+
refusedAt?: string | null;
|
|
49
|
+
reason?: string | null;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export declare const isGateDuplicate: (a: unknown) => a is GateDuplicate;
|
|
53
|
+
/** The hook's door: asks for the refusal-twin check and may carry the re-ask's justification. A `duplicate` answer is returned as
|
|
54
|
+
* such (never permission); every other answer passes the same allow-list as gateAuthorize. */
|
|
55
|
+
export declare function gateAuthorizeTwinAware(config: GuardConfig, ctx: ApprovalContext, opts?: {
|
|
56
|
+
idempotencyKey?: string;
|
|
57
|
+
request?: CommittedRequest;
|
|
58
|
+
justification?: string;
|
|
59
|
+
}): Promise<GateAuth | GateDuplicate>;
|
|
40
60
|
export declare function gateAuthorize(config: GuardConfig, ctx: ApprovalContext, opts?: {
|
|
41
61
|
idempotencyKey?: string;
|
|
42
62
|
request?: CommittedRequest;
|
package/dist/adapters/_gate.js
CHANGED
|
@@ -40,7 +40,20 @@ export function assertApprovalCheckShape(raw, decisionId) {
|
|
|
40
40
|
return { ok: false, decisionId, state: c.state, code: "unverifiable", reason: "the approval check came back contradictory (ok with a refusal code)" };
|
|
41
41
|
return c;
|
|
42
42
|
}
|
|
43
|
+
export const isGateDuplicate = (a) => {
|
|
44
|
+
const d = a?.duplicateOf;
|
|
45
|
+
return a?.decision === "duplicate" && !!d && typeof d === "object" && typeof d.decisionId === "string" && decisionIdProblem(d.decisionId) === null;
|
|
46
|
+
};
|
|
47
|
+
export async function gateAuthorizeTwinAware(config, ctx, opts = {}) {
|
|
48
|
+
const a = await postAuthorize(config, ctx, { ...opts, twinCheck: true });
|
|
49
|
+
if (isGateDuplicate(a))
|
|
50
|
+
return a;
|
|
51
|
+
return assertAuthorizeVerdict(a, "unknown");
|
|
52
|
+
}
|
|
43
53
|
export async function gateAuthorize(config, ctx, opts = {}) {
|
|
54
|
+
return assertAuthorizeVerdict(await postAuthorize(config, ctx, opts), "unknown");
|
|
55
|
+
}
|
|
56
|
+
async function postAuthorize(config, ctx, opts = {}) {
|
|
44
57
|
ctx = canonicalize(ctx, "describe(...)");
|
|
45
58
|
assertDescribed(ctx);
|
|
46
59
|
assertSafeTransport(config.apiUrl, carriesCredential(config));
|
|
@@ -60,6 +73,8 @@ export async function gateAuthorize(config, ctx, opts = {}) {
|
|
|
60
73
|
...(requesterIdentity ? { requesterIdentity } : {}), ...(resumeUrl ? { resumeUrl } : {}),
|
|
61
74
|
...(commitment ? { requestCommitment: commitment } : {}),
|
|
62
75
|
...(opts.idempotencyKey ? { idempotencyKey: opts.idempotencyKey } : {}),
|
|
76
|
+
...(opts.twinCheck ? { twinCheck: true } : {}),
|
|
77
|
+
...(opts.justification ? { justification: opts.justification } : {}),
|
|
63
78
|
}),
|
|
64
79
|
});
|
|
65
80
|
if (!res.ok) {
|
|
@@ -71,7 +86,7 @@ export async function gateAuthorize(config, ctx, opts = {}) {
|
|
|
71
86
|
catch { }
|
|
72
87
|
throw new DecidioHttpError(res.status, "/agent/authorize", parsed);
|
|
73
88
|
}
|
|
74
|
-
return
|
|
89
|
+
return res.json();
|
|
75
90
|
}
|
|
76
91
|
export async function gateConfirm(config, decisionId, result, evidence) {
|
|
77
92
|
const f = config.fetchImpl ?? fetch;
|
package/dist/cli.d.ts
CHANGED
|
@@ -17,3 +17,53 @@ export declare function defaultAgentId(dir?: string): string;
|
|
|
17
17
|
* distinct decision ids can never share a file (first-run review of 0.4.1: dots were STRIPPED, `a.b` and `ab`
|
|
18
18
|
* collided). `~` is outside the allowed set, so the escape is unambiguous. Exported for the smokes. */
|
|
19
19
|
export declare function receiptFileName(id: string): string;
|
|
20
|
+
/** Scoped packs (GitHub App design §2): a pack's scope in words - the organisation's, a unit's (its type, and its name when
|
|
21
|
+
* the server sent one: names that fail its person check are withheld there), or the reader's own. Null for an older server. */
|
|
22
|
+
export declare function packScopeLabel(scope: any): string | null;
|
|
23
|
+
/** Where `context pull --here` is: a folder's .decidio.json names a unit; else the checkout's GitHub remote names the repository
|
|
24
|
+
* (its binding's unit decides); else nowhere in particular (the organisation's packs and your own). */
|
|
25
|
+
export declare function hereQuery(cwd?: string): {
|
|
26
|
+
unit?: string;
|
|
27
|
+
repo?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
};
|
|
30
|
+
/** PROJECT FILES ARE TOUCHED ONLY THROUGH PLAIN PATHS (invariant-guard 2026-10-16): a repository can commit `.claude/skills/x`
|
|
31
|
+
* or `.claude/rules` as a SYMLINK to anywhere, so every component from the project's `.claude` down is lstat'ed and a symlink
|
|
32
|
+
* refuses the whole path - nothing is written through it and nothing is removed through it. A missing component is fine. */
|
|
33
|
+
export declare function plainProjectPath(parts: string[]): boolean;
|
|
34
|
+
/** Is this file one `context pull` wrote? A skill file's frontmatter names `decidio-<slug>` and its heading says it is a
|
|
35
|
+
* Decidio context pack; a rules file's heading says it is one to read first. A person's own file with our prefix is not. */
|
|
36
|
+
export declare function isPackFileOf(text: string, slug: string, kind: "skill" | "rules"): boolean;
|
|
37
|
+
/** The skill file for one pack: the trigger IS the description (the host reads it every turn), the rows are the body. */
|
|
38
|
+
export declare function packSkillMarkdown(idx: any): string;
|
|
39
|
+
/** An ALWAYS-pack (stackable packs, 2026-10-15): a Claude Code rules file, loaded whole at launch with the project's own
|
|
40
|
+
* rules - no frontmatter, no trigger; the foundation every other pack can assume was read. */
|
|
41
|
+
export declare function packRulesMarkdown(idx: any): string;
|
|
42
|
+
export type ParsedAdr = {
|
|
43
|
+
title: string;
|
|
44
|
+
status: string | null;
|
|
45
|
+
decided: string;
|
|
46
|
+
rationale: string;
|
|
47
|
+
alternatives: string[];
|
|
48
|
+
};
|
|
49
|
+
/** PURE: one ADR's title, status, decision, context (with its consequences) and the options it considered; null when it has no title. */
|
|
50
|
+
export declare function parseAdr(text: string): ParsedAdr | null;
|
|
51
|
+
export declare const AGENTS_MD_START = "<!-- decidio:start -->";
|
|
52
|
+
export declare const AGENTS_MD_END = "<!-- decidio:end -->";
|
|
53
|
+
/** Decidio's public GitHub Action (github.com/OmniTwin/decidio-actions): one action, step request or verify. It fetches the job's own
|
|
54
|
+
* OIDC token (audience "decidio") - no repository secret - and fails the job in plain words when Decidio refuses. Pinned to the
|
|
55
|
+
* v1.0.0 COMMIT, never a tag (invariant-guard 2026-10-20): the verify step enforces the approval in a production job with
|
|
56
|
+
* 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@e40ba8e75a223f2475038d38a16df5a5885afde4 # v1.0.0";
|
|
58
|
+
export declare const CHECKOUT_ACTION = "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0";
|
|
59
|
+
export declare function githubRequestWorkflow(api: string, environment: string, branch?: string): string;
|
|
60
|
+
/** The verify step and the checkout of the approved commit: the first steps of any deploy job Decidio starts. */
|
|
61
|
+
export declare function githubVerifySteps(api: string): string;
|
|
62
|
+
export declare function githubDeployWorkflow(api: string, environment: string): string;
|
|
63
|
+
/** The AGENTS.md section: a link to where the decisions live and how an agent checks a plan - never a decision's text (founder
|
|
64
|
+
* 2026-10-17: decisions embedded in a repository leak and go stale). */
|
|
65
|
+
export declare function agentsMdSection(appUrl: string): string;
|
|
66
|
+
/** PURE: AGENTS.md with our section added, or replaced where it already is (idempotent). Ours is recognised only whole - the start
|
|
67
|
+
* marker straight followed by our heading, then the end marker after it - so a stray marker in a person's own text is never
|
|
68
|
+
* read as ours and nothing of theirs is ever removed (invariant-guard 2026-10-17). */
|
|
69
|
+
export declare function withAgentsMdSection(text: string, section: string): string;
|