approval-md 0.3.0 → 0.4.1
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 +83 -914
- package/SPEC.md +101 -13
- package/cli.js +134 -7
- package/dist/src/adapters/smtp.d.ts +18 -0
- package/dist/src/adapters/smtp.js +24 -3
- package/dist/src/adapters/smtp.js.map +1 -1
- package/dist/src/channels/contract.d.ts +72 -0
- package/dist/src/channels/contract.js +161 -1
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/relay-server.d.ts +90 -0
- package/dist/src/channels/relay-server.js +295 -0
- package/dist/src/channels/relay-server.js.map +1 -0
- package/dist/src/channels/relay.d.ts +273 -0
- package/dist/src/channels/relay.js +564 -0
- package/dist/src/channels/relay.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +25 -0
- package/dist/src/channels/tagging.js +63 -6
- package/dist/src/channels/tagging.js.map +1 -1
- package/dist/src/channels/telegram-minimal.d.ts +251 -0
- package/dist/src/channels/telegram-minimal.js +618 -0
- package/dist/src/channels/telegram-minimal.js.map +1 -0
- package/dist/src/channels/telegram-webhook.d.ts +273 -0
- package/dist/src/channels/telegram-webhook.js +536 -0
- package/dist/src/channels/telegram-webhook.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +725 -20
- package/dist/src/channels/telegram.js +1332 -123
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/cli/amend.js +358 -5
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/attest.js +43 -6
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/audit-card.d.ts +1 -1
- package/dist/src/cli/audit-card.js +40 -1
- package/dist/src/cli/audit-card.js.map +1 -1
- package/dist/src/cli/audit.d.ts +7 -5
- package/dist/src/cli/audit.js +22 -10
- package/dist/src/cli/audit.js.map +1 -1
- package/dist/src/cli/channel-relay.d.ts +108 -0
- package/dist/src/cli/channel-relay.js +334 -0
- package/dist/src/cli/channel-relay.js.map +1 -0
- package/dist/src/cli/channel-telegram-webhook.d.ts +275 -0
- package/dist/src/cli/channel-telegram-webhook.js +716 -0
- package/dist/src/cli/channel-telegram-webhook.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +347 -28
- package/dist/src/cli/channel-telegram.js +648 -56
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel.d.ts +1 -1
- package/dist/src/cli/channel.js +5 -1
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/daemon.js +37 -5
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +63 -0
- package/dist/src/cli/doctor.js +856 -111
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/execute.d.ts +29 -1
- package/dist/src/cli/execute.js +387 -21
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/exit-codes.d.ts +12 -0
- package/dist/src/cli/exit-codes.js +13 -0
- package/dist/src/cli/exit-codes.js.map +1 -1
- package/dist/src/cli/gate.d.ts +2 -0
- package/dist/src/cli/gate.js +233 -2
- package/dist/src/cli/gate.js.map +1 -1
- package/dist/src/cli/help.d.ts +10 -1
- package/dist/src/cli/help.js +283 -54
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook.d.ts +485 -6
- package/dist/src/cli/hook.js +2370 -70
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/init.js +16 -7
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.js +24 -3
- package/dist/src/cli/instructions.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +55 -0
- package/dist/src/cli/log-anchor.js +199 -60
- package/dist/src/cli/log-anchor.js.map +1 -1
- package/dist/src/cli/log-unlock.d.ts +23 -0
- package/dist/src/cli/log-unlock.js +109 -0
- package/dist/src/cli/log-unlock.js.map +1 -0
- package/dist/src/cli/main.d.ts +0 -12
- package/dist/src/cli/main.js +72 -4
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/muse.d.ts +2 -0
- package/dist/src/cli/muse.js +62 -0
- package/dist/src/cli/muse.js.map +1 -0
- package/dist/src/cli/payload.js +79 -1
- package/dist/src/cli/payload.js.map +1 -1
- package/dist/src/cli/scaffold.js +6 -0
- package/dist/src/cli/scaffold.js.map +1 -1
- package/dist/src/cli/serve.d.ts +53 -0
- package/dist/src/cli/serve.js +270 -0
- package/dist/src/cli/serve.js.map +1 -0
- package/dist/src/cli/up.js +26 -2
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +2 -0
- package/dist/src/cli/verb-registry.js +338 -39
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/core/attest-bootstrap.d.ts +95 -0
- package/dist/src/core/attest-bootstrap.js +135 -0
- package/dist/src/core/attest-bootstrap.js.map +1 -0
- package/dist/src/core/attest.d.ts +20 -3
- package/dist/src/core/attest.js +25 -0
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +153 -12
- package/dist/src/core/audit.js +415 -20
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/channel-lease.d.ts +322 -0
- package/dist/src/core/channel-lease.js +663 -0
- package/dist/src/core/channel-lease.js.map +1 -0
- package/dist/src/core/command-class.d.ts +51 -0
- package/dist/src/core/command-class.js +766 -8
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/daemon-host.d.ts +178 -0
- package/dist/src/core/daemon-host.js +228 -0
- package/dist/src/core/daemon-host.js.map +1 -0
- package/dist/src/core/daemon-identity.d.ts +238 -0
- package/dist/src/core/daemon-identity.js +252 -0
- package/dist/src/core/daemon-identity.js.map +1 -0
- package/dist/src/core/gate.d.ts +364 -6
- package/dist/src/core/gate.js +802 -27
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/harness-version.d.ts +128 -4
- package/dist/src/core/harness-version.js +179 -12
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +82 -3
- package/dist/src/core/harness-wait.js +89 -3
- package/dist/src/core/harness-wait.js.map +1 -1
- package/dist/src/core/log-lock.d.ts +435 -0
- package/dist/src/core/log-lock.js +1093 -0
- package/dist/src/core/log-lock.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +21 -0
- package/dist/src/core/log-subscribe.js +16 -6
- package/dist/src/core/log-subscribe.js.map +1 -1
- package/dist/src/core/log-write-layer.d.ts +55 -0
- package/dist/src/core/log-write-layer.js +91 -0
- package/dist/src/core/log-write-layer.js.map +1 -0
- package/dist/src/core/log.d.ts +130 -22
- package/dist/src/core/log.js +473 -50
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/payload-census.d.ts +51 -0
- package/dist/src/core/payload-census.js +125 -2
- package/dist/src/core/payload-census.js.map +1 -1
- package/dist/src/core/payload-store.d.ts +31 -0
- package/dist/src/core/payload-store.js +113 -8
- package/dist/src/core/payload-store.js.map +1 -1
- package/dist/src/core/payload.d.ts +18 -3
- package/dist/src/core/payload.js +20 -4
- package/dist/src/core/payload.js.map +1 -1
- package/dist/src/core/policy-diff.d.ts +10 -0
- package/dist/src/core/policy-diff.js +10 -2
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-explain.d.ts +7 -1
- package/dist/src/core/policy-explain.js +16 -1
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +24 -0
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +38 -0
- package/dist/src/core/policy-match.js +35 -0
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +40 -0
- package/dist/src/core/policy-proposal.js +37 -16
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/prompt-layout.d.ts +186 -1
- package/dist/src/core/prompt-layout.js +345 -3
- package/dist/src/core/prompt-layout.js.map +1 -1
- package/dist/src/core/read-scope.d.ts +10 -0
- package/dist/src/core/read-scope.js +4 -0
- package/dist/src/core/read-scope.js.map +1 -1
- package/dist/src/core/run-payload.d.ts +160 -0
- package/dist/src/core/run-payload.js +304 -0
- package/dist/src/core/run-payload.js.map +1 -0
- package/dist/src/core/sender-identity.d.ts +59 -11
- package/dist/src/core/sender-identity.js +79 -11
- package/dist/src/core/sender-identity.js.map +1 -1
- package/dist/src/core/state.d.ts +52 -1
- package/dist/src/core/state.js +52 -6
- package/dist/src/core/state.js.map +1 -1
- package/dist/src/core/telegram-config.d.ts +79 -0
- package/dist/src/core/telegram-config.js +108 -0
- package/dist/src/core/telegram-config.js.map +1 -1
- package/dist/src/core/validate.d.ts +20 -6
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -1
- package/dist/src/core/verify.d.ts +23 -1
- package/dist/src/core/verify.js +20 -2
- package/dist/src/core/verify.js.map +1 -1
- package/dist/src/daemon/audit.d.ts +80 -5
- package/dist/src/daemon/audit.js +126 -6
- package/dist/src/daemon/audit.js.map +1 -1
- package/dist/src/daemon/daemon.d.ts +174 -2
- package/dist/src/daemon/daemon.js +298 -7
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +13 -2
- package/dist/src/daemon/projection.js +39 -6
- package/dist/src/daemon/projection.js.map +1 -1
- package/dist/src/mcp/server.d.ts +39 -3
- package/dist/src/mcp/server.js +22 -3
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/muse/server.d.ts +16 -0
- package/dist/src/muse/server.js +238 -0
- package/dist/src/muse/server.js.map +1 -0
- package/dist/src/serve/archive.d.ts +166 -0
- package/dist/src/serve/archive.js +363 -0
- package/dist/src/serve/archive.js.map +1 -0
- package/dist/src/serve/arguments.d.ts +93 -0
- package/dist/src/serve/arguments.js +228 -0
- package/dist/src/serve/arguments.js.map +1 -0
- package/dist/src/serve/credentials.d.ts +91 -0
- package/dist/src/serve/credentials.js +143 -0
- package/dist/src/serve/credentials.js.map +1 -0
- package/dist/src/serve/follow.d.ts +86 -0
- package/dist/src/serve/follow.js +101 -0
- package/dist/src/serve/follow.js.map +1 -0
- package/dist/src/serve/hook-thread.d.ts +134 -0
- package/dist/src/serve/hook-thread.js +333 -0
- package/dist/src/serve/hook-thread.js.map +1 -0
- package/dist/src/serve/hook-worker.d.ts +134 -0
- package/dist/src/serve/hook-worker.js +173 -0
- package/dist/src/serve/hook-worker.js.map +1 -0
- package/dist/src/serve/server.d.ts +412 -0
- package/dist/src/serve/server.js +1251 -0
- package/dist/src/serve/server.js.map +1 -0
- package/docs/README-extended.md +768 -0
- package/docs/cli-reference.md +2139 -67
- package/package.json +4 -2
- package/schema/event.schema.json +413 -30
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-age-without-created.json +19 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-lockfile-path.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-operator-cleared-system-actor.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-unknown-reason.json +14 -0
- package/schema/fixtures/event/invalid/audit-lock-reclaimed-without-age.json +13 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +6 -1
- package/schema/fixtures/event/invalid/audit-reviewed-bad-payload-hash.json +21 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +5 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-subject-hash.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-subject-seq.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-verdict-source.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-no-verdict.json +19 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +6 -1
- package/schema/fixtures/event/invalid/audit-sampled-no-policy-hash.json +14 -0
- package/schema/fixtures/event/invalid/daemon-id-malformed.json +15 -0
- package/schema/fixtures/event/invalid/execution-started-truncated-policy-sha256.json +18 -0
- package/schema/fixtures/event/invalid/harness-cap-at-margin.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-fractional.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-negative.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-string.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-without-harness-execution.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-zero.json +16 -0
- package/schema/fixtures/event/valid/approval-requested-harness-cap.json +18 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed-implausible-created.json +18 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed-legacy.json +14 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed-unlock.json +19 -0
- package/schema/fixtures/event/valid/audit-lock-reclaimed.json +19 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +5 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +6 -1
- package/schema/fixtures/event/valid/audit-sampled.json +2 -1
- package/schema/fixtures/event/valid/daemon-identity-declared.json +17 -0
- package/schema/fixtures/event/valid/daemon-identity.json +16 -0
- package/schema/fixtures/event/valid/execution-started-policy-sha256.json +18 -0
- package/schema/fixtures/event/valid/harness-kind-hermes.json +23 -0
- package/schema/fixtures/policy/invalid/daemons-malformed-id.json +10 -0
- package/schema/fixtures/policy/valid/daemons-allowlist.json +19 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-agent-may-request.md +21 -0
- package/schema/fixtures/policy-md/valid/agent-may-request.md +24 -0
- package/schema/policy.schema.json +102 -6
|
@@ -135,19 +135,43 @@
|
|
|
135
135
|
"12345678",
|
|
136
136
|
"hmac-sha256:3f2a9c11b4d7e6085a1c2f9d8e7b6a5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a"
|
|
137
137
|
]
|
|
138
|
+
},
|
|
139
|
+
"edgeos": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"minLength": 1,
|
|
142
|
+
"pattern": "^(?:[A-Za-z0-9][A-Za-z0-9._-]{0,127}|hmac-sha256:[0-9a-f]{64})$",
|
|
143
|
+
"description": "The EdgeOS account this person decides from, in one of two forms (SPEC.md §5.2/§10.3, APRV-455, on the ground APRV-324 laid for `telegram`).\n\nRAW: the stable human id the EdgeOS identity service returns from `/humans/me` for the session the person authenticated in, as a string. The runtime treats it as OPAQUE and compares it byte for byte; the grammar admits what such an id is made of (an ASCII letter or digit, then up to 127 letters, digits, `.`, `_` or `-`), which covers a UUID and a decimal id alike. It holds no `:` and no whitespace, so a raw id can never be mistaken for the keyed form and can never contain the separator the runtime's sender index uses. Never an email address or a display name: those are chosen or changed by the person, and a mapping keyed on one would transfer an identity with an edit.\n\nKEYED: `hmac-sha256:<64 lowercase hex>`, the HMAC-SHA-256 of that same id under the operator-held `APPROVAL_SENDER_KEY`, exactly as for `telegram` and for the same reason: a published policy and a published log otherwise disclose the account.\n\nThe id reaches the runtime through `approval channel relay`, whose authenticated caller (the operator's control plane, holding `APPROVAL_RELAY_SECRET`) reports which EdgeOS session made the gesture. The daemon trusts that report the way it trusts the Telegram Bot API's `from.id`: it is operator trust in a relay, stated as such, and the resident cannot choose the id the relay attributes to them.",
|
|
144
|
+
"examples": [
|
|
145
|
+
"4f1c2a9e-7b3d-4e8a-9c21-5d6e7f8a9b0c",
|
|
146
|
+
"hmac-sha256:3f2a9c11b4d7e6085a1c2f9d8e7b6a5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a"
|
|
147
|
+
]
|
|
138
148
|
}
|
|
139
149
|
},
|
|
140
150
|
"examples": [
|
|
141
151
|
{ "telegram": "12345678" },
|
|
142
152
|
{
|
|
143
153
|
"telegram": "hmac-sha256:3f2a9c11b4d7e6085a1c2f9d8e7b6a5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a"
|
|
144
|
-
}
|
|
154
|
+
},
|
|
155
|
+
{ "telegram": "12345678", "edgeos": "4f1c2a9e-7b3d-4e8a-9c21-5d6e7f8a9b0c" }
|
|
145
156
|
]
|
|
146
157
|
}
|
|
147
158
|
}
|
|
148
159
|
}
|
|
149
160
|
}
|
|
150
161
|
},
|
|
162
|
+
"daemons": {
|
|
163
|
+
"type": "array",
|
|
164
|
+
"uniqueItems": true,
|
|
165
|
+
"description": "SPEC.md §5.2 (amended, APRV-383): the daemon instance ids permitted to append to this log. An ALLOWLIST, and the counterpart of `approvers` one layer down: that key says which humans may decide, this one says which daemon processes may write the records of those decisions down.\n\nIt exists for the hosted case (`design/hosted-daemon-identity.md`): when one party runs the daemon for another, records the daemon appends carry `daemon` (see `event.schema.json`) naming the instance that wrote them, and this list is how the tenant's own attested policy states which of them it expects. A daemon whose id this list does not name is refused AT THE WRITE BOUNDARY with `daemon-not-allowed` and writes nothing.\n\nABSENT means NO RESTRICTION, not an empty list: a policy that never declares the key behaves exactly as every policy written before it existed, and the daemon-written records still carry their id. An empty array is a policy that admits no daemon at all, which is a thing an author may say and the runtime will enforce.\n\nWhat being LISTED buys is nothing (SPEC.md §11.1 invariant 4). The id is self-reported by the writing process, so it may only ever cost a daemon the ability to write: no verdict, autonomy, budget, floor, sampling draw, token or TTL reads this key or the record field, and a hostile host holding the tenant's log handle is not what this defends against. What it catches is a daemon started against the wrong tenant's log, a second daemon nobody meant to leave running, and a rebuilt container whose id changed, each of which used to write records indistinguishable from the intended daemon's.\n\nIt is a top-level key rather than a member of the `daemon` block below, and the distinction is worth the two names: `daemon` (singular) is how THIS daemon reads a log it already may write, every key in it a latency setting whose strictest value is the default, while `daemons` (plural) is an ENFORCEMENT key deciding WHICH daemons may write at all. Read only from an ATTESTED policy, like every other fact a refusal is derived from.",
|
|
166
|
+
"items": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"minLength": 1,
|
|
169
|
+
"maxLength": 64,
|
|
170
|
+
"pattern": "^[a-z0-9][a-z0-9._-]*$",
|
|
171
|
+
"description": "One daemon instance id, in the same grammar the `daemon` record field carries: lowercase letters, digits, `.`, `-` and `_`, starting with a letter or digit. Either an id the operator declared in that daemon's launch environment through `APPROVAL_DAEMON_ID`, or the derived `daemon-<instance id>` form that `approval status` and `approval doctor` print for the instance they are run against."
|
|
172
|
+
},
|
|
173
|
+
"examples": [["daemon-3f2a9c11"], ["village-goa-1", "village-goa-2"]]
|
|
174
|
+
},
|
|
151
175
|
"classes": {
|
|
152
176
|
"type": "object",
|
|
153
177
|
"additionalProperties": false,
|
|
@@ -213,7 +237,7 @@
|
|
|
213
237
|
"daemon": {
|
|
214
238
|
"type": "object",
|
|
215
239
|
"additionalProperties": false,
|
|
216
|
-
"description": "APRV-217 (docs/proposals/incremental-prefix-proof.md): how the long-lived readers of this log prove a cached prefix before they reuse it. Read by `approval daemon run` and `approval up` and by nothing else: one-shot processes, the Claude Code hook included, always prove in full, and `approval log verify` and `approval doctor` never use the cache at all. Every key here is a LATENCY setting whose strictest value is the default, so a policy that omits the block, or the whole block, behaves exactly as the runtime did before it existed.",
|
|
240
|
+
"description": "APRV-217 (docs/proposals/incremental-prefix-proof.md): how the long-lived readers of this log prove a cached prefix before they reuse it. Read by `approval daemon run` and `approval up` and by nothing else: one-shot processes, the Claude Code hook included, always prove in full, and `approval log verify` and `approval doctor` never use the cache at all. Every key here is a LATENCY setting whose strictest value is the default, so a policy that omits the block, or the whole block, behaves exactly as the runtime did before it existed. Not to be confused with the top-level `daemons` list above (APRV-383), which is an enforcement key naming WHICH daemon instances may append to this log; this block is how a daemon that may already write reads.",
|
|
217
241
|
"properties": {
|
|
218
242
|
"read_proof": {
|
|
219
243
|
"type": "string",
|
|
@@ -362,6 +386,58 @@
|
|
|
362
386
|
"$ref": "#/$defs/promptRows",
|
|
363
387
|
"description": "Visibility DOWN: the row never renders on this channel. Refused for the rows required for a decision — `action_key`, `class`, `command_breakdown`, `protected_path`, `policy_diff`, `policy_load` — and refused at policy load with the machine-readable keyword `prompt-row-required`, which fails the whole policy closed. `payload_hash` is NOT among them because the bound hash is stated inside the canonical block on every channel, so hiding the row removes a duplicate rather than the binding. Naming one row in both `always` and `hide` is refused rather than resolved by precedence: an author who wrote both believes one of them, and picking for them would be a guess.",
|
|
364
388
|
"examples": [["provenance", "chain", "requested_ts"]]
|
|
389
|
+
},
|
|
390
|
+
"style": {
|
|
391
|
+
"type": "string",
|
|
392
|
+
"enum": ["technical", "minimal"],
|
|
393
|
+
"default": "technical",
|
|
394
|
+
"description": "SPEC.md §5.2/§10.3 (APRV-489): how this channel lays out a prompt. `technical`, the default and what an absent key means, is the card every earlier build sent, byte for byte. `minimal` is a short card in plain words: a headline the runtime computes from the class, the payload's own words quoted from the bound bytes, a deadline in plain words, and the whole technical card, canonical rendering included, collapsed inside the same message as the Approve and Deny buttons (SPEC.md §10.3, amended APRV-489). Only Telegram draws it in this release; on `web` and `cli` the key is validated and IGNORED, which shows more than was asked for and never less. A request the minimal card cannot draw honestly (an opaque payload without a `say` declaration naming every key, an attestation, a policy edit, a digest, a truncated payload, an abnormal health row, a card over the message limit) is sent as the technical card. Review cards never change. The enum is closed: an unknown style fails validation, which fails the policy closed to all-`manual`.",
|
|
395
|
+
"examples": ["minimal"]
|
|
396
|
+
},
|
|
397
|
+
"say": {
|
|
398
|
+
"type": "object",
|
|
399
|
+
"propertyNames": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*(?:\\.[a-z0-9][a-z0-9_-]*)*$" },
|
|
400
|
+
"additionalProperties": { "$ref": "#/$defs/promptSay" },
|
|
401
|
+
"description": "SPEC.md §5.2 (APRV-489): what the minimal card says about a class, keyed by EXACT class name (no wildcard: one friendly phrase must never stand for several classes). Operator text the approver attests, never agent text. Read only under `style: minimal`.",
|
|
402
|
+
"examples": [
|
|
403
|
+
{
|
|
404
|
+
"digest.share": {
|
|
405
|
+
"does": "share a note about you",
|
|
406
|
+
"quote": { "scope": "Shared with", "expires_at": "Until", "text": "Note", "digest_id": null },
|
|
407
|
+
"note": "none"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
"promptSay": {
|
|
415
|
+
"type": "object",
|
|
416
|
+
"additionalProperties": false,
|
|
417
|
+
"description": "SPEC.md §5.2 (APRV-489): one class's minimal-card declaration. `does` is required for a class core does not phrase itself and refused for one it does, or for a class the command classifier emits (checked at load). It is drawn only over an opaque payload: over a command, a file change or an email the runtime draws its own kind phrase.",
|
|
418
|
+
"properties": {
|
|
419
|
+
"does": {
|
|
420
|
+
"type": "string",
|
|
421
|
+
"minLength": 1,
|
|
422
|
+
"maxLength": 120,
|
|
423
|
+
"description": "The plain-words verb phrase the headline completes: `Your agent wants to <does>`. Starts with a letter; no line break, sentence punctuation (. ! ? … : ;), markup or ⚠ (checked at load). Not allowed for a class core phrases itself or a class the command classifier emits (`prompt-say-kind`). Drawn only over an opaque payload: over a command, a file change or an email the runtime's kind phrase is drawn instead and the decision record notes `say_does_ignored`. The runtime chooses the entry by the exact class the log records."
|
|
424
|
+
},
|
|
425
|
+
"quote": {
|
|
426
|
+
"type": "object",
|
|
427
|
+
"propertyNames": { "type": "string", "minLength": 1, "maxLength": 64 },
|
|
428
|
+
"additionalProperties": {
|
|
429
|
+
"oneOf": [
|
|
430
|
+
{ "type": "string", "maxLength": 24 },
|
|
431
|
+
{ "type": "null" }
|
|
432
|
+
]
|
|
433
|
+
},
|
|
434
|
+
"description": "The closed field set for an OPAQUE payload: every top-level key the payload may carry, mapped to the label its value is quoted under (`\"\"` for no label), or `~` for a key known and deliberately left off the simple card (the card then names it under the quotations). At least one key must be shown (checked at load). A payload carrying any key this map does not name is sent as the technical card. Ignored for command, file-change and email payloads, whose excerpt is built in."
|
|
435
|
+
},
|
|
436
|
+
"note": {
|
|
437
|
+
"type": "string",
|
|
438
|
+
"enum": ["summary", "none"],
|
|
439
|
+
"default": "summary",
|
|
440
|
+
"description": "Whether the agent's own summary is shown, labelled as the agent's and as not checked: `summary` (the default) or `none`."
|
|
365
441
|
}
|
|
366
442
|
}
|
|
367
443
|
},
|
|
@@ -402,9 +478,9 @@
|
|
|
402
478
|
},
|
|
403
479
|
"senderChannel": {
|
|
404
480
|
"type": "string",
|
|
405
|
-
"enum": ["telegram"],
|
|
406
|
-
"description": "SPEC.md §10.3 (amended, APRV-324): a channel whose transport attributes a gesture to an account id, and therefore the only kind of channel an `approvers.<id>.senders` entry may name. `telegram` qualifies because the Bot API reports `callback_query.from.id`, which the sender cannot choose. `web` and `cli` are deliberately absent: an unauthenticated form post names nobody and a local process names whoever runs it, so a sender mapping for either would be a binding the runtime cannot check and an identity key the channel cannot support.",
|
|
407
|
-
"examples": ["telegram"]
|
|
481
|
+
"enum": ["telegram", "edgeos"],
|
|
482
|
+
"description": "SPEC.md §10.3 (amended, APRV-324; widened, APRV-455): a channel whose transport attributes a gesture to an account id, and therefore the only kind of channel an `approvers.<id>.senders` entry may name. `telegram` qualifies because the Bot API reports `callback_query.from.id`, which the sender cannot choose. `edgeos` qualifies on the same ground: the gesture arrives through `approval channel relay`, authenticated by a shared secret from the daemon's launch environment, from a control plane that attributes it to the EdgeOS `/humans/me` id of the session the person signed in with, which the person cannot choose either. In both cases the daemon trusts the relay's report of who acted, and that is operator trust (APRV-422: no hosted service has authority over decisions; the relay carries a person's gesture and decides nothing). `web` and `cli` are deliberately absent: an unauthenticated form post names nobody and a local process names whoever runs it, so a sender mapping for either would be a binding the runtime cannot check and an identity key the channel cannot support.",
|
|
483
|
+
"examples": ["telegram", "edgeos"]
|
|
408
484
|
},
|
|
409
485
|
"classPattern": {
|
|
410
486
|
"type": "string",
|
|
@@ -415,7 +491,7 @@
|
|
|
415
491
|
"type": "object",
|
|
416
492
|
"additionalProperties": false,
|
|
417
493
|
"required": ["autonomy"],
|
|
418
|
-
"description": "SPEC.md §5.1: a class rule. The shorthand form is `{ autonomy }` alone; the full form may add rates, `allow_irreversible`, `approvers` and `limits`. Both are this one shape — `autonomy` is always REQUIRED, since a rule without it cannot decide anything, and unknown keys are rejected so a typo'd constraint is never silently ignored.",
|
|
494
|
+
"description": "SPEC.md §5.1: a class rule. The shorthand form is `{ autonomy }` alone; the full form may add rates, `allow_irreversible`, `agent_may_request`, `approvers` and `limits`. Both are this one shape — `autonomy` is always REQUIRED, since a rule without it cannot decide anything, and unknown keys are rejected so a typo'd constraint is never silently ignored.",
|
|
419
495
|
"allOf": [
|
|
420
496
|
{
|
|
421
497
|
"$comment": "SPEC.md §5.2 (amended, APRV-127): `live_rate` belongs to `supervised-live` and to nothing else. Required there, because a live mode with no fraction declares a control without saying how much of it runs, and the runtime would have to invent the number an author did not write. Forbidden everywhere else, because a rate sitting on a `manual`, `supervised-retro` or `autonomous` rule is a fraction its author believes is in force that nothing reads — the same silent no-op this schema's closed shape exists to prevent. APRV-185 adds `human-only` to that everywhere-else, and the `else` branch below already carries it with no new clause: a level whose actions no agent may take has no fraction of them to gate, so a `live_rate` written there is a control its author believes is running over a population of size zero.",
|
|
@@ -467,6 +543,22 @@
|
|
|
467
543
|
}
|
|
468
544
|
}
|
|
469
545
|
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
"$comment": "SPEC.md §5.2 (amended, APRV-445): `agent_may_request: true` opens the `propose` verb to a class an operator declared, so the level it sits on must be one an agent may ask about. Refused on `human-only` as a load error: a human-only class is inert to agents (SPEC.md §11.1 invariant 9), and a flag saying an agent may file a request for one is a door the gate would only slam. False is admitted everywhere as the explicit spelling of the default.",
|
|
549
|
+
"if": {
|
|
550
|
+
"type": "object",
|
|
551
|
+
"properties": { "agent_may_request": { "const": true } },
|
|
552
|
+
"required": ["agent_may_request"]
|
|
553
|
+
},
|
|
554
|
+
"then": {
|
|
555
|
+
"type": "object",
|
|
556
|
+
"properties": {
|
|
557
|
+
"autonomy": {
|
|
558
|
+
"enum": ["supervised-live", "supervised", "supervised-retro", "autonomous", "manual"]
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
470
562
|
}
|
|
471
563
|
],
|
|
472
564
|
"properties": {
|
|
@@ -480,6 +572,10 @@
|
|
|
480
572
|
"type": "boolean",
|
|
481
573
|
"description": "SPEC.md §5.2/§7 (amended, APRV-317): explicit permission for truthful `reversible: false` actions matching this rule to retain its nonmanual autonomy. Omission or false preserves the manual floor. Every equally most-specific matching rule must say true; defaults and action metadata cannot supply it."
|
|
482
574
|
},
|
|
575
|
+
"agent_may_request": {
|
|
576
|
+
"type": "boolean",
|
|
577
|
+
"description": "SPEC.md §5.2 (amended, APRV-445): whether an agent may open a request in this class with `approval propose`, naming the class itself and carrying its payload inline. Absent and false mean no. It is read two ways and only these two: on a key that names the class EXACTLY, and on a declared family key `<prefix>.*` the class falls under. `propose` additionally requires the class to be an exact key of `classes`, so a wildcard rule alone never makes a class proposable; the exact key's own value wins when it states one, and otherwise the most specific family that states one decides. Adding or changing it is a policy edit like any other and takes a human re-attestation, which voids pending requests (`policy-drift`)."
|
|
578
|
+
},
|
|
483
579
|
"approvers": {
|
|
484
580
|
"type": "array",
|
|
485
581
|
"minItems": 1,
|