approval-md 0.1.0 → 0.2.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 +584 -553
- package/SPEC.md +42 -13
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +119 -5
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +173 -51
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +186 -80
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +155 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +294 -7
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +4 -2
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +176 -8
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -0
- package/dist/src/core/agents-md.d.ts +276 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +13 -1
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +43 -8
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +44 -6
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +68 -13
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +2 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +31 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +15 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +20 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +514 -35
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +9 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +389 -36
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +12 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +2 -1
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/policy.schema.json +21 -1
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential-custody transport for zzz.bot messages (APRV-320).
|
|
3
|
+
*
|
|
4
|
+
* The adapter sends only the JSON value a grant binds. Destination selection
|
|
5
|
+
* is a strict tagged union over two fixed service origins; neither the CLI nor
|
|
6
|
+
* the payload can supply an arbitrary URL. The Bearer credential comes only
|
|
7
|
+
* from the scoped provider opened by the shared adapter contract.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { canonicalize } from "../core/jcs.js";
|
|
11
|
+
import { payloadHash } from "../core/payload.js";
|
|
12
|
+
import { CREDENTIAL_REFUSAL_CODES, PROVIDER_REF_DETAIL_KEY, } from "./contract.js";
|
|
13
|
+
export const ZZZ_CLASS = "communicate.zzz.external";
|
|
14
|
+
export const ZZZ_PRODUCTION_API_BASE = "https://zzz.bot";
|
|
15
|
+
export const ZZZ_PREVIEW_API_BASE = "https://zzz-preview.soycarts.workers.dev";
|
|
16
|
+
export const ZZZ_DEFAULT_TIMEOUT_MS = 15_000;
|
|
17
|
+
export const ZZZ_TOKEN_NAME = "zzz.agent_token";
|
|
18
|
+
const TOKEN = /^[A-Za-z0-9_-]{32,256}$/u;
|
|
19
|
+
const PROVIDER_ID = /^(?:thr|pst)_[a-f0-9]{32}$/u;
|
|
20
|
+
const MAX_MESSAGE_BYTES = 65_536;
|
|
21
|
+
const MAX_RESPONSE_BYTES = 16_384;
|
|
22
|
+
export const ZZZ_FAILURE_CODES = [
|
|
23
|
+
"zzz-payload-invalid",
|
|
24
|
+
"zzz-config-invalid",
|
|
25
|
+
"zzz-invalid-request",
|
|
26
|
+
"zzz-unauthorized",
|
|
27
|
+
"zzz-forbidden",
|
|
28
|
+
"zzz-not-found",
|
|
29
|
+
"zzz-idempotency-conflict",
|
|
30
|
+
"zzz-payload-too-large",
|
|
31
|
+
"zzz-rejected",
|
|
32
|
+
"zzz-rate-limited",
|
|
33
|
+
"zzz-unreachable",
|
|
34
|
+
"zzz-cancelled",
|
|
35
|
+
...CREDENTIAL_REFUSAL_CODES,
|
|
36
|
+
];
|
|
37
|
+
export const ZZZ_CREDENTIAL_SPECS = [
|
|
38
|
+
{
|
|
39
|
+
name: ZZZ_TOKEN_NAME,
|
|
40
|
+
kind: "secret",
|
|
41
|
+
label: "ZZZ agent token",
|
|
42
|
+
describe: "an invited zzz.bot principal credential with write scope",
|
|
43
|
+
required: true,
|
|
44
|
+
validate(value) {
|
|
45
|
+
return TOKEN.test(value)
|
|
46
|
+
? { ok: true }
|
|
47
|
+
: {
|
|
48
|
+
ok: false,
|
|
49
|
+
message: "the ZZZ agent token must be 32 to 256 ASCII letters, digits, underscores, or hyphens",
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
function object(value) {
|
|
55
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
56
|
+
? value
|
|
57
|
+
: null;
|
|
58
|
+
}
|
|
59
|
+
function exactKeys(value, allowed) {
|
|
60
|
+
const extra = Object.keys(value).find((key) => !allowed.includes(key));
|
|
61
|
+
return extra === undefined ? null : extra;
|
|
62
|
+
}
|
|
63
|
+
function boundedString(value, min, max) {
|
|
64
|
+
return typeof value === "string" && value.length >= min && value.length <= max;
|
|
65
|
+
}
|
|
66
|
+
function validReferences(value) {
|
|
67
|
+
if (value === undefined)
|
|
68
|
+
return true;
|
|
69
|
+
if (!Array.isArray(value) || value.length > 20)
|
|
70
|
+
return false;
|
|
71
|
+
return value.every((candidate) => {
|
|
72
|
+
const ref = object(candidate);
|
|
73
|
+
if (ref === null || exactKeys(ref, ["kind", "target", "label", "relationship"]) !== null)
|
|
74
|
+
return false;
|
|
75
|
+
if (!boundedString(ref["target"], 1, 2048) || !boundedString(ref["label"], 1, 120))
|
|
76
|
+
return false;
|
|
77
|
+
if (ref["kind"] !== "external" && ref["kind"] !== "post")
|
|
78
|
+
return false;
|
|
79
|
+
if (!["source", "context", "supersedes"].includes(String(ref["relationship"])))
|
|
80
|
+
return false;
|
|
81
|
+
if (ref["kind"] === "external") {
|
|
82
|
+
try {
|
|
83
|
+
const url = new URL(ref["target"]);
|
|
84
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export function validateZzzPayload(value) {
|
|
95
|
+
const input = object(value);
|
|
96
|
+
if (input === null)
|
|
97
|
+
return { ok: false, message: "the payload must be one JSON object" };
|
|
98
|
+
if (input["environment"] !== "production" && input["environment"] !== "preview") {
|
|
99
|
+
return { ok: false, message: "environment must be production or preview" };
|
|
100
|
+
}
|
|
101
|
+
if (input["operation"] !== "create_thread" && input["operation"] !== "create_reply") {
|
|
102
|
+
return { ok: false, message: "operation must be create_thread or create_reply" };
|
|
103
|
+
}
|
|
104
|
+
const common = ["environment", "operation", "body", "metadata", "tags", "references"];
|
|
105
|
+
const allowed = input["operation"] === "create_thread"
|
|
106
|
+
? [...common, "room_id", "title"]
|
|
107
|
+
: [...common, "thread_id"];
|
|
108
|
+
const extra = exactKeys(input, allowed);
|
|
109
|
+
if (extra !== null)
|
|
110
|
+
return { ok: false, message: `unknown payload field ${JSON.stringify(extra)}` };
|
|
111
|
+
if (!boundedString(input["body"], 1, 65_536))
|
|
112
|
+
return { ok: false, message: "body must contain 1 to 65536 characters" };
|
|
113
|
+
if (input["tags"] !== undefined &&
|
|
114
|
+
(!Array.isArray(input["tags"]) || input["tags"].length > 10 ||
|
|
115
|
+
!input["tags"].every((tag) => boundedString(tag, 1, 40)))) {
|
|
116
|
+
return { ok: false, message: "tags must contain at most 10 strings of 1 to 40 characters" };
|
|
117
|
+
}
|
|
118
|
+
if (!validReferences(input["references"]))
|
|
119
|
+
return { ok: false, message: "references do not match the ZZZ API contract" };
|
|
120
|
+
let path;
|
|
121
|
+
if (input["operation"] === "create_thread") {
|
|
122
|
+
if (!boundedString(input["room_id"], 1, 256) || !boundedString(input["title"], 1, 200)) {
|
|
123
|
+
return { ok: false, message: "create_thread needs a room_id and a title of 1 to 200 characters" };
|
|
124
|
+
}
|
|
125
|
+
path = `/api/v1/rooms/${encodeURIComponent(input["room_id"])}/threads`;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
if (!boundedString(input["thread_id"], 1, 256))
|
|
129
|
+
return { ok: false, message: "create_reply needs a thread_id" };
|
|
130
|
+
path = `/api/v1/threads/${encodeURIComponent(input["thread_id"])}/posts`;
|
|
131
|
+
}
|
|
132
|
+
const body = {
|
|
133
|
+
...(input["operation"] === "create_thread" ? { title: input["title"] } : {}),
|
|
134
|
+
body: input["body"],
|
|
135
|
+
...(input["metadata"] === undefined ? {} : { metadata: input["metadata"] }),
|
|
136
|
+
...(input["tags"] === undefined ? {} : { tags: input["tags"] }),
|
|
137
|
+
...(input["references"] === undefined ? {} : { references: input["references"] }),
|
|
138
|
+
};
|
|
139
|
+
if (new TextEncoder().encode(canonicalize(body)).byteLength > MAX_MESSAGE_BYTES) {
|
|
140
|
+
return { ok: false, message: "the canonical ZZZ message exceeds 65536 UTF-8 bytes" };
|
|
141
|
+
}
|
|
142
|
+
return { ok: true, payload: input, path, body };
|
|
143
|
+
}
|
|
144
|
+
function idempotencyKey(actionKey, hash) {
|
|
145
|
+
const bytes = canonicalize({ action_key: actionKey, payload_hash: hash });
|
|
146
|
+
return `approval:${createHash("sha256").update(bytes, "utf8").digest("hex")}`;
|
|
147
|
+
}
|
|
148
|
+
function codeForStatus(status) {
|
|
149
|
+
if (status === 400)
|
|
150
|
+
return "zzz-invalid-request";
|
|
151
|
+
if (status === 401)
|
|
152
|
+
return "zzz-unauthorized";
|
|
153
|
+
if (status === 403)
|
|
154
|
+
return "zzz-forbidden";
|
|
155
|
+
if (status === 404)
|
|
156
|
+
return "zzz-not-found";
|
|
157
|
+
if (status === 409)
|
|
158
|
+
return "zzz-idempotency-conflict";
|
|
159
|
+
if (status === 413)
|
|
160
|
+
return "zzz-payload-too-large";
|
|
161
|
+
if (status === 422)
|
|
162
|
+
return "zzz-rejected";
|
|
163
|
+
if (status === 429)
|
|
164
|
+
return "zzz-rate-limited";
|
|
165
|
+
return "zzz-rejected";
|
|
166
|
+
}
|
|
167
|
+
async function boundedJson(response) {
|
|
168
|
+
const reader = response.body?.getReader();
|
|
169
|
+
if (reader === undefined)
|
|
170
|
+
throw new Error("ZZZ returned no response body");
|
|
171
|
+
const chunks = [];
|
|
172
|
+
let size = 0;
|
|
173
|
+
while (true) {
|
|
174
|
+
const item = await reader.read();
|
|
175
|
+
if (item.done)
|
|
176
|
+
break;
|
|
177
|
+
size += item.value.byteLength;
|
|
178
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
179
|
+
await reader.cancel();
|
|
180
|
+
throw new Error("ZZZ returned an oversized response");
|
|
181
|
+
}
|
|
182
|
+
chunks.push(item.value);
|
|
183
|
+
}
|
|
184
|
+
const joined = new Uint8Array(size);
|
|
185
|
+
let offset = 0;
|
|
186
|
+
for (const chunk of chunks) {
|
|
187
|
+
joined.set(chunk, offset);
|
|
188
|
+
offset += chunk.byteLength;
|
|
189
|
+
}
|
|
190
|
+
return JSON.parse(new TextDecoder().decode(joined));
|
|
191
|
+
}
|
|
192
|
+
function originFor(environment, options) {
|
|
193
|
+
return options.origins?.[environment] ??
|
|
194
|
+
(environment === "production" ? ZZZ_PRODUCTION_API_BASE : ZZZ_PREVIEW_API_BASE);
|
|
195
|
+
}
|
|
196
|
+
export function zzzAdapter(options = {}) {
|
|
197
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
198
|
+
const timeoutMs = options.timeoutMs ?? ZZZ_DEFAULT_TIMEOUT_MS;
|
|
199
|
+
return {
|
|
200
|
+
name: "zzz",
|
|
201
|
+
classes: [ZZZ_CLASS],
|
|
202
|
+
requiredCredentials: [ZZZ_TOKEN_NAME],
|
|
203
|
+
precheck(input) {
|
|
204
|
+
const checked = validateZzzPayload(input.payload);
|
|
205
|
+
return checked.ok
|
|
206
|
+
? { ok: true }
|
|
207
|
+
: { ok: false, code: "zzz-payload-invalid", message: `${checked.message}. Nothing was sent` };
|
|
208
|
+
},
|
|
209
|
+
async act(input) {
|
|
210
|
+
const checked = validateZzzPayload(input.payload);
|
|
211
|
+
if (!checked.ok)
|
|
212
|
+
return { ok: false, code: "zzz-payload-invalid", message: `${checked.message}. Nothing was sent` };
|
|
213
|
+
const credential = input.credentials.get(ZZZ_TOKEN_NAME);
|
|
214
|
+
if (!credential.ok)
|
|
215
|
+
return { ok: false, code: credential.code, message: credential.message };
|
|
216
|
+
if (!TOKEN.test(credential.value)) {
|
|
217
|
+
return { ok: false, code: "zzz-config-invalid", message: "the vault's ZZZ agent token does not match the service credential shape. Nothing was sent" };
|
|
218
|
+
}
|
|
219
|
+
if (input.signal?.aborted === true) {
|
|
220
|
+
return { ok: false, code: "zzz-cancelled", message: "the ZZZ message was cancelled before the HTTP request. Nothing was sent" };
|
|
221
|
+
}
|
|
222
|
+
const environment = checked.payload.environment;
|
|
223
|
+
const controller = new AbortController();
|
|
224
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
225
|
+
const abort = () => controller.abort();
|
|
226
|
+
input.signal?.addEventListener("abort", abort, { once: true });
|
|
227
|
+
try {
|
|
228
|
+
const response = await fetchImpl(new URL(checked.path, originFor(environment, options)), {
|
|
229
|
+
method: "POST",
|
|
230
|
+
redirect: "error",
|
|
231
|
+
signal: controller.signal,
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Bearer ${credential.value}`,
|
|
234
|
+
"Content-Type": "application/json",
|
|
235
|
+
"Idempotency-Key": idempotencyKey(input.actionKey, payloadHash(input.payload)),
|
|
236
|
+
},
|
|
237
|
+
body: canonicalize(checked.body),
|
|
238
|
+
});
|
|
239
|
+
const definiteRefusal = [400, 401, 403, 404, 409, 413, 422, 429].includes(response.status);
|
|
240
|
+
if (response.status !== 200 && response.status !== 201 && !definiteRefusal) {
|
|
241
|
+
await response.body?.cancel().catch(() => undefined);
|
|
242
|
+
throw new Error(`ZZZ answered an ambiguous HTTP status ${String(response.status)}`);
|
|
243
|
+
}
|
|
244
|
+
if (definiteRefusal) {
|
|
245
|
+
await response.body?.cancel().catch(() => undefined);
|
|
246
|
+
return { ok: false, code: codeForStatus(response.status), message: `ZZZ refused the message with HTTP ${String(response.status)}. No response text was recorded` };
|
|
247
|
+
}
|
|
248
|
+
const answer = await boundedJson(response);
|
|
249
|
+
const parsed = object(answer);
|
|
250
|
+
const expectedPrefix = checked.payload.operation === "create_thread" ? "thr_" : "pst_";
|
|
251
|
+
if (parsed === null || exactKeys(parsed, ["id", "replayed"]) !== null ||
|
|
252
|
+
typeof parsed["id"] !== "string" || !PROVIDER_ID.test(parsed["id"]) ||
|
|
253
|
+
!parsed["id"].startsWith(expectedPrefix) || typeof parsed["replayed"] !== "boolean" ||
|
|
254
|
+
parsed["replayed"] !== (response.status === 200)) {
|
|
255
|
+
throw new Error("ZZZ returned an inconsistent success receipt");
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
ok: true,
|
|
259
|
+
detail: {
|
|
260
|
+
[PROVIDER_REF_DETAIL_KEY]: parsed["id"],
|
|
261
|
+
replayed: parsed["replayed"],
|
|
262
|
+
operation: checked.payload.operation,
|
|
263
|
+
payload_hash: payloadHash(input.payload),
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
input.signal?.removeEventListener("abort", abort);
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/** Read-only authentication probe. It proves no write scope or room access. */
|
|
275
|
+
export async function probeZzz(token, options = {}) {
|
|
276
|
+
if (!TOKEN.test(token))
|
|
277
|
+
return { ok: false, code: "zzz-config-invalid", message: "the ZZZ agent token does not match the service credential shape" };
|
|
278
|
+
const controller = new AbortController();
|
|
279
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? ZZZ_DEFAULT_TIMEOUT_MS);
|
|
280
|
+
try {
|
|
281
|
+
const response = await (options.fetch ?? globalThis.fetch)(new URL("/api/v1/rooms", originFor("production", options)), {
|
|
282
|
+
method: "GET",
|
|
283
|
+
redirect: "error",
|
|
284
|
+
signal: controller.signal,
|
|
285
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
286
|
+
});
|
|
287
|
+
await response.body?.cancel().catch(() => undefined);
|
|
288
|
+
if (response.status === 200)
|
|
289
|
+
return { ok: true };
|
|
290
|
+
return { ok: false, code: codeForStatus(response.status), message: `ZZZ answered HTTP ${String(response.status)}; no response text was recorded` };
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return { ok: false, code: "zzz-unreachable", message: "ZZZ could not be reached for the read-only credential check" };
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
clearTimeout(timer);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=zzz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zzz.js","sourceRoot":"","sources":["../../../src/adapters/zzz.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batching: one human gesture over many requests, without losing audit
|
|
3
|
+
* granularity (SPEC.md §10.3, requirement B7).
|
|
4
|
+
*
|
|
5
|
+
* > Channels MAY present multiple pending requests as a batch and collect one
|
|
6
|
+
* > human gesture over the set, and SHOULD for high-volume `record.*` classes.
|
|
7
|
+
* > The log never batches: each granted or rejected request receives its own
|
|
8
|
+
* > `approval.granted` or `approval.rejected` event carrying the batch's channel
|
|
9
|
+
* > delivery id in its payload, so audit granularity survives ergonomic
|
|
10
|
+
* > grouping. A batch MUST NOT mix `manual` classes with differing
|
|
11
|
+
* > payload-display requirements in a way that hides any full payload behind the
|
|
12
|
+
* > fold of another.
|
|
13
|
+
*
|
|
14
|
+
* Two halves, and this module is both: {@link assembleBatch} enforces the
|
|
15
|
+
* forbidden mix *before* anything is delivered, and {@link recordBatchDecisions}
|
|
16
|
+
* records one gate decision per member afterwards.
|
|
17
|
+
*
|
|
18
|
+
* ## The B7 operationalization — FLAGGED FOR HUMAN REVIEW
|
|
19
|
+
*
|
|
20
|
+
* "Hides any full payload behind the fold of another" is a sentence about
|
|
21
|
+
* rendering, and rendering is the one thing the runtime cannot inspect. So B7 is
|
|
22
|
+
* operationalized here as a property of the *batch material* rather than of the
|
|
23
|
+
* pixels:
|
|
24
|
+
*
|
|
25
|
+
* > A batch MUST carry every member's full payload whole. The forbidden mix is
|
|
26
|
+
* > any member whose `fullPayload` is absent or truncated while the batch
|
|
27
|
+
* > contains more than one distinct payload — because with one payload there is
|
|
28
|
+
* > no "another" to hide behind, and with two or more, a member the channel was
|
|
29
|
+
* > not given in full is a member the approver cannot have read.
|
|
30
|
+
*
|
|
31
|
+
* Concretely, {@link assembleBatch} refuses when the batch holds more than one
|
|
32
|
+
* distinct `payload_hash` **and** any member's `fullPayload` is `null` or
|
|
33
|
+
* `truncated`. A single-payload batch (the same bytes requested twice, or one
|
|
34
|
+
* request) is always assemblable, and a multi-payload batch is assemblable
|
|
35
|
+
* exactly when every member arrives whole.
|
|
36
|
+
*
|
|
37
|
+
* What this deliberately does **not** try to be: a judgment about whether a
|
|
38
|
+
* particular channel's layout actually put payload #2 below the fold. That is a
|
|
39
|
+
* per-channel rendering property, and `channels/conformance.ts` checks the
|
|
40
|
+
* observable half of it (every member's full payload text present in the
|
|
41
|
+
* rendering, delineated from the claimed summary). The interpretation to
|
|
42
|
+
* confirm is the reduction of "differing payload-display requirements" to
|
|
43
|
+
* "distinct payload bytes": it is the reading that makes the rule mechanical,
|
|
44
|
+
* and it is stricter than the spec's letter in one direction (two whole payloads
|
|
45
|
+
* are fine only if both are whole) and silent in another (it says nothing about
|
|
46
|
+
* a channel that renders both whole and then collapses one). A human should
|
|
47
|
+
* confirm this is the intended reading before APRV-23/25/26 build against it.
|
|
48
|
+
*/
|
|
49
|
+
import type { DecideOptions } from "../core/gate.js";
|
|
50
|
+
import { type ChannelActorOptions, type ChannelBatch, type ChannelDecision, type ChannelRequest, type DecisionOutcome, type DeliveryId } from "./contract.js";
|
|
51
|
+
export { BATCH_DELIVERY_ID_FIELD, BATCH_DELIVERY_NOTE_PREFIX, batchDeliveryIdOf, batchNote, type ChannelBatch, } from "./contract.js";
|
|
52
|
+
/** The one way a batch can be refused (SPEC.md §10.3, B7). */
|
|
53
|
+
export declare const BATCH_REFUSAL_CODE = "batch-forbidden-mix";
|
|
54
|
+
export interface BatchRefusal {
|
|
55
|
+
ok: false;
|
|
56
|
+
code: typeof BATCH_REFUSAL_CODE;
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
export type AssembleBatchResult = {
|
|
60
|
+
ok: true;
|
|
61
|
+
batch: ChannelBatch;
|
|
62
|
+
} | BatchRefusal;
|
|
63
|
+
/**
|
|
64
|
+
* Assemble `requests` into a batch, or refuse the forbidden mix.
|
|
65
|
+
*
|
|
66
|
+
* The returned batch carries every member request **whole** — the same
|
|
67
|
+
* {@link ChannelRequest} objects a channel would receive individually, tags and
|
|
68
|
+
* full payloads included. A channel therefore has everything it needs to render
|
|
69
|
+
* each member in full; if it chooses not to, that is a rendering defect the
|
|
70
|
+
* conformance suite catches, not a gap in the material it was handed.
|
|
71
|
+
*
|
|
72
|
+
* `deliveryId` is left unset: it is assigned by the channel at `notify`.
|
|
73
|
+
*/
|
|
74
|
+
export declare function assembleBatch(requests: ChannelRequest[]): AssembleBatchResult;
|
|
75
|
+
/** One member's outcome. `token` is held back from the channel, as always. */
|
|
76
|
+
export interface BatchMemberResult {
|
|
77
|
+
action_key: string;
|
|
78
|
+
outcome: DecisionOutcome;
|
|
79
|
+
/** The raw execution token for a successful grant. Runtime-only. */
|
|
80
|
+
token?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface BatchDecisionsResult {
|
|
83
|
+
/** True when every member was recorded. */
|
|
84
|
+
ok: boolean;
|
|
85
|
+
/** One entry per input decision, in input order. */
|
|
86
|
+
results: BatchMemberResult[];
|
|
87
|
+
batchDeliveryId: DeliveryId;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Record one gate decision per batch member.
|
|
91
|
+
*
|
|
92
|
+
* **Unit decisions mean unit outcomes.** The log never batches (SPEC.md §10.3),
|
|
93
|
+
* so this is a loop over `decide()` and not a transaction: each member is
|
|
94
|
+
* checked and appended on its own, each carries the batch delivery id in its
|
|
95
|
+
* payload as the first-class `batch_delivery_id` field (amended SPEC.md §10.3,
|
|
96
|
+
* APRV-38), and a member that refuses does **not** stop the rest. That is
|
|
97
|
+
* deliberate and it is the only coherent semantics available — an "all or
|
|
98
|
+
* nothing" batch would have to un-append events the log forbids un-appending,
|
|
99
|
+
* and abandoning the remaining members because the third one expired would
|
|
100
|
+
* discard a human's answer to the other four. So: partial success is a real
|
|
101
|
+
* outcome, `ok` is false when any member refused, and `results` says exactly
|
|
102
|
+
* which ones landed. A caller that wants atomicity does not want a batch.
|
|
103
|
+
*
|
|
104
|
+
* Ordering: members are recorded in the order given. Each append passes the
|
|
105
|
+
* gate's compare-and-append precondition against the head *it* read, so a
|
|
106
|
+
* concurrent writer refuses one member with `append-failed`/`head-moved`
|
|
107
|
+
* rather than corrupting the set.
|
|
108
|
+
*/
|
|
109
|
+
export declare function recordBatchDecisions(logPath: string, decisions: ChannelDecision[], batchDeliveryId: DeliveryId, actorOptions: ChannelActorOptions, gateOptions?: DecideOptions): BatchDecisionsResult;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI channel (APRV-23) — the zero-config channel, written against the
|
|
3
|
+
* contract in `channels/contract.ts` and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* It is the first implementation of {@link TestableChannel}, and it exists as
|
|
6
|
+
* much to prove the contract is implementable as to be useful: a terminal has no
|
|
7
|
+
* stylesheet, no buttons and no webhook, so everything the contract asks a
|
|
8
|
+
* channel to do has to be done in plain text. If SPEC.md §9's display rule
|
|
9
|
+
* cannot survive a channel this poor, it cannot survive a rich one either.
|
|
10
|
+
*
|
|
11
|
+
* ## The rendering convention (SPEC.md §9)
|
|
12
|
+
*
|
|
13
|
+
* Every line carries a marker in the first column, and the marker is the first
|
|
14
|
+
* thing a reader sees:
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* [computed] class communicate.email.external (log)
|
|
18
|
+
* [claimed] summary chase invoice 41 (agent:drafter)
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* `[computed]` means the runtime derived the value — from the verified log, the
|
|
22
|
+
* attested policy, the budget evaluator, the payload bytes, or arithmetic on the
|
|
23
|
+
* display instant — and the parenthetical names *which* derivation. `[claimed]`
|
|
24
|
+
* means the party under oversight wrote it, and the parenthetical names that
|
|
25
|
+
* party. The marker is not decoration: it is the difference between "the runtime
|
|
26
|
+
* says this is a manual action" and "an agent says this costs two cents", and a
|
|
27
|
+
* channel that gave them the same weight would be a conformance failure rather
|
|
28
|
+
* than a styling preference.
|
|
29
|
+
*
|
|
30
|
+
* Claimed lines are additionally indented under a `claimed by …` heading, so the
|
|
31
|
+
* split survives a terminal that eats no colour and a reader who skims. There is
|
|
32
|
+
* no bold, no colour and no unicode box drawing anywhere in this file — a marker
|
|
33
|
+
* a `less`, a pipe, a CI log or a screen reader can lose is a marker that was
|
|
34
|
+
* never there.
|
|
35
|
+
*
|
|
36
|
+
* ## The full payload (SPEC.md §10.4)
|
|
37
|
+
*
|
|
38
|
+
* For a manual action the payload is printed verbatim inside an explicitly
|
|
39
|
+
* delimited block:
|
|
40
|
+
*
|
|
41
|
+
* ```
|
|
42
|
+
* --- BEGIN FULL PAYLOAD (bound sha256 <hash>) ---
|
|
43
|
+
* { … }
|
|
44
|
+
* --- END FULL PAYLOAD ---
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* The block is never mixed with the claimed summary, and the summary is never
|
|
48
|
+
* printed inside it. `channels/tagging.ts` has already checked those bytes
|
|
49
|
+
* against the recorded binding, so what is inside the delimiters is what the
|
|
50
|
+
* execution token will spend.
|
|
51
|
+
*
|
|
52
|
+
* ## What this file does NOT do
|
|
53
|
+
*
|
|
54
|
+
* It does not read the log, write the log, decide anything, or hold a token.
|
|
55
|
+
* The decision handler is registered by the runtime (`cli/channel.ts`) and its
|
|
56
|
+
* only job is to call {@link recordChannelDecision}. The prompt loop collects a
|
|
57
|
+
* gesture and hands it over; whether that gesture becomes an event is the gate's
|
|
58
|
+
* answer, not this file's.
|
|
59
|
+
*
|
|
60
|
+
* It also does not dispatch (APRV-55). This channel is **one-shot by design**:
|
|
61
|
+
* the runtime derives the queue once, renders it, collects decisions on the
|
|
62
|
+
* requests in front of the operator, and the process ends. There is no cycle in
|
|
63
|
+
* which a newly appended request could arrive, so there is nothing to push —
|
|
64
|
+
* the operator running the verb again is the refresh. Only the long-lived push
|
|
65
|
+
* channel (Telegram) needs a per-cycle send; the long-lived pull channel (web)
|
|
66
|
+
* re-derives per page view.
|
|
67
|
+
*/
|
|
68
|
+
import { type PromptLayout } from "../core/prompt-layout.js";
|
|
69
|
+
import type { ChannelBatch, ChannelDecision, ChannelHealth, ChannelRequest, DecisionOutcome, DeliveryId, RenderedRequest, TestableChannel } from "./contract.js";
|
|
70
|
+
/** The narrowest thing this channel needs to write to. `process.stdout` fits. */
|
|
71
|
+
export interface OutputSink {
|
|
72
|
+
write(text: string): unknown;
|
|
73
|
+
}
|
|
74
|
+
/** The narrowest thing this channel needs to read lines from. `process.stdin` fits. */
|
|
75
|
+
export type InputSource = NodeJS.ReadableStream;
|
|
76
|
+
export interface CliChannelOptions {
|
|
77
|
+
/** Where the rendering goes. Defaults to `process.stdout`. */
|
|
78
|
+
output?: OutputSink;
|
|
79
|
+
/** Where the gesture comes from. Defaults to `process.stdin`. */
|
|
80
|
+
input?: InputSource;
|
|
81
|
+
/** Channel name recorded for audit. Defaults to `cli`. */
|
|
82
|
+
name?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Which rows the rendering shows (APRV-218), from `channels.cli.prompt`.
|
|
85
|
+
*
|
|
86
|
+
* Resolved by the verb, which has already loaded the policy; this channel
|
|
87
|
+
* neither reads a policy file nor holds an opinion about what an operator
|
|
88
|
+
* should see. Defaults to {@link CLI_PROMPT_LAYOUT}, so a channel constructed
|
|
89
|
+
* without one renders exactly what it rendered before the key existed.
|
|
90
|
+
*/
|
|
91
|
+
layout?: PromptLayout;
|
|
92
|
+
}
|
|
93
|
+
/** The marker prefixes. Exported because the help text and the tests pin them. */
|
|
94
|
+
export declare const COMPUTED_MARKER = "[computed]";
|
|
95
|
+
export declare const CLAIMED_MARKER = "[claimed]";
|
|
96
|
+
/** The full-payload delimiters (SPEC.md §10.4). Pinned by tests. */
|
|
97
|
+
export declare const PAYLOAD_BEGIN = "--- BEGIN FULL PAYLOAD";
|
|
98
|
+
export declare const PAYLOAD_END = "--- END FULL PAYLOAD ---";
|
|
99
|
+
/** One line of the legend, printed above every delivery. */
|
|
100
|
+
export declare const RENDERING_LEGEND: string;
|
|
101
|
+
/** What {@link CliChannel.collectDecision} produced. */
|
|
102
|
+
export type CliCollection = {
|
|
103
|
+
kind: "decided";
|
|
104
|
+
decision: ChannelDecision;
|
|
105
|
+
outcome: DecisionOutcome;
|
|
106
|
+
}
|
|
107
|
+
/** The human declined to answer this one; nothing was reported. */
|
|
108
|
+
| {
|
|
109
|
+
kind: "skipped";
|
|
110
|
+
}
|
|
111
|
+
/** Input ended before an answer arrived; nothing was reported. */
|
|
112
|
+
| {
|
|
113
|
+
kind: "aborted";
|
|
114
|
+
};
|
|
115
|
+
export declare class CliChannel implements TestableChannel {
|
|
116
|
+
readonly name: string;
|
|
117
|
+
private readonly output;
|
|
118
|
+
private readonly input;
|
|
119
|
+
private handler;
|
|
120
|
+
private rendered;
|
|
121
|
+
private reader;
|
|
122
|
+
private counter;
|
|
123
|
+
/** The policy's row layout for this channel (APRV-218). Pure input to the rendering. */
|
|
124
|
+
private readonly layout;
|
|
125
|
+
constructor(options?: CliChannelOptions);
|
|
126
|
+
/**
|
|
127
|
+
* Present a request, or a batch, and return the delivery id.
|
|
128
|
+
*
|
|
129
|
+
* Writes to the output sink and nowhere else: no log, no state, no decision.
|
|
130
|
+
* A batch is rendered member by member — each member carries its own full
|
|
131
|
+
* payload, and folding one behind another is exactly what `channels/batch.ts`
|
|
132
|
+
* refuses upstream.
|
|
133
|
+
*/
|
|
134
|
+
notify(request: ChannelRequest | ChannelBatch): DeliveryId;
|
|
135
|
+
onDecision(handler: (decision: ChannelDecision) => DecisionOutcome): void;
|
|
136
|
+
health(): ChannelHealth;
|
|
137
|
+
lastRendered(): RenderedRequest[];
|
|
138
|
+
/**
|
|
139
|
+
* Release the input interface. Safe to call when nothing was ever read.
|
|
140
|
+
*
|
|
141
|
+
* The source is paused as well as closed: a `readline` interface over
|
|
142
|
+
* `process.stdin` leaves the stream flowing, and a process that finished
|
|
143
|
+
* asking would sit there holding the event loop open.
|
|
144
|
+
*/
|
|
145
|
+
close(): void;
|
|
146
|
+
/**
|
|
147
|
+
* The prompt loop: `g` grants, `r` rejects, `s` skips.
|
|
148
|
+
*
|
|
149
|
+
* A reject demands a note and re-asks until it gets one — a refusal an agent
|
|
150
|
+
* cannot read is a refusal it will retry. A grant's note is optional; there is
|
|
151
|
+
* nothing to explain about "yes, do the thing I was shown".
|
|
152
|
+
*
|
|
153
|
+
* The gesture is reported to the handler registered by the runtime, and the
|
|
154
|
+
* handler's {@link DecisionOutcome} is returned unchanged, refusals included.
|
|
155
|
+
* Nothing here inspects it, and nothing here retries: a gate refusal is an
|
|
156
|
+
* answer, and re-asking a human who already answered is how double-grants get
|
|
157
|
+
* made.
|
|
158
|
+
*/
|
|
159
|
+
collectDecision(actionKey: string, deliveryId: DeliveryId, options?: {
|
|
160
|
+
batchDeliveryId?: DeliveryId;
|
|
161
|
+
}): Promise<CliCollection>;
|
|
162
|
+
/**
|
|
163
|
+
* The checkpoint prompt, in a terminal (APRV-257).
|
|
164
|
+
*
|
|
165
|
+
* The same question the Telegram tap asks, in the channel that has no
|
|
166
|
+
* buttons: the lines the runtime composed, then `s` to sign and `n` to
|
|
167
|
+
* decline. The text is the runtime's and identical on both surfaces, because
|
|
168
|
+
* the thing being consented to is identical and two phrasings of one gesture
|
|
169
|
+
* would be two claims about it.
|
|
170
|
+
*
|
|
171
|
+
* It collects a gesture and nothing else. What signing MEANS — the key, the
|
|
172
|
+
* head, the append — is the runtime's, exactly as a decision is: this channel
|
|
173
|
+
* can no more sign a checkpoint than it can grant a request.
|
|
174
|
+
*
|
|
175
|
+
* End of input is `null`, never a default. A closed pipe has not declined,
|
|
176
|
+
* and it certainly has not signed.
|
|
177
|
+
*/
|
|
178
|
+
collectCheckpoint(lines: readonly string[]): Promise<boolean | null>;
|
|
179
|
+
private report;
|
|
180
|
+
/**
|
|
181
|
+
* Ask for a note. Returns `undefined` for "none given" and `null` when input
|
|
182
|
+
* ended — the two are different, and conflating them would let a closed pipe
|
|
183
|
+
* look like a human who chose to say nothing.
|
|
184
|
+
*/
|
|
185
|
+
private askNote;
|
|
186
|
+
private readLine;
|
|
187
|
+
/**
|
|
188
|
+
* Build one request's rendering split. This is the same function whose output
|
|
189
|
+
* is written to the terminal — {@link lastRendered} is not a parallel
|
|
190
|
+
* description of the rendering, it *is* the rendering.
|
|
191
|
+
*/
|
|
192
|
+
private renderRequest;
|
|
193
|
+
}
|