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,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary of a daemon advance cycle, as facts about the log (APRV-204).
|
|
3
|
+
*
|
|
4
|
+
* Pure over records: no git, no filesystem, no clock. It lives in `core/`
|
|
5
|
+
* because three layers ask the same two questions of it — the daemon that
|
|
6
|
+
* writes the cycles (`daemon/advance.ts`), the doctor row that reports the last
|
|
7
|
+
* one (`cli/doctor.ts`), and the trigger arithmetic that must not count the
|
|
8
|
+
* daemon's own bookkeeping — and a CLI module may not import the daemon
|
|
9
|
+
* (`tests/layering.test.ts`). One home, so the three cannot disagree about
|
|
10
|
+
* which records are an advance's own.
|
|
11
|
+
*/
|
|
12
|
+
import type { EventRecord } from "./log.js";
|
|
13
|
+
import { type RequestState } from "./state.js";
|
|
14
|
+
/**
|
|
15
|
+
* Who proposes an advance.
|
|
16
|
+
*
|
|
17
|
+
* `agent:daemon`, not the `system:daemon` of `envelope.drift`: the gate's
|
|
18
|
+
* proposing side is a PRINCIPAL (`human:` or `agent:`), and an advance is a
|
|
19
|
+
* request to act on the world rather than a fact the runtime observed about
|
|
20
|
+
* itself. The distinction is load-bearing in the log — a reader tells the
|
|
21
|
+
* daemon's observations from the daemon's actions by the actor alone.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ADVANCE_ACTOR = "agent:daemon";
|
|
24
|
+
/** The class an advance is gated as. Declared, resolved, and never assumed. */
|
|
25
|
+
export declare const ADVANCE_CLASS = "log.advance";
|
|
26
|
+
/** The task id every advance cycle registers under, plus its head seq. */
|
|
27
|
+
export declare const ADVANCE_TASK_PREFIX = "daemon-advance";
|
|
28
|
+
/** The idempotency key prefix. One key per (published head → working head) span. */
|
|
29
|
+
export declare const ADVANCE_KEY_PREFIX = "daemon-log-advance";
|
|
30
|
+
/** The task id for a cycle that publishes up to `toSeq`. */
|
|
31
|
+
export declare function advanceTaskId(toSeq: number): string;
|
|
32
|
+
/** The idempotency key for the span `fromSeq..toSeq`. */
|
|
33
|
+
export declare function advanceActionKey(fromSeq: number, toSeq: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Is this record part of an advance cycle's own bookkeeping?
|
|
36
|
+
*
|
|
37
|
+
* Keyed on the task id the daemon registers under, which nothing else writes.
|
|
38
|
+
* A record whose task merely LOOKS like one of those but was written by another
|
|
39
|
+
* actor is still excluded, deliberately: the exclusion only ever makes the
|
|
40
|
+
* cadence advance LESS eagerly, so a false positive costs latency while a false
|
|
41
|
+
* negative would cost an endless cadence (one cycle appends three records, the
|
|
42
|
+
* last of them after the commit).
|
|
43
|
+
*/
|
|
44
|
+
export declare function isAdvanceBookkeeping(record: EventRecord): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The most recent advance cycle's request, as the log derives it (APRV-211).
|
|
47
|
+
*
|
|
48
|
+
* The whole answer a tick needs before it considers asking anything: which key
|
|
49
|
+
* the last question was opened under, what the human did with it, what bytes it
|
|
50
|
+
* bound to, and whether anything has spent it yet.
|
|
51
|
+
*/
|
|
52
|
+
export interface OpenAdvanceRequest {
|
|
53
|
+
actionKey: string;
|
|
54
|
+
task: string | null;
|
|
55
|
+
state: RequestState;
|
|
56
|
+
/** The `payload_hash` the request declared, so an adopting tick binds to it. */
|
|
57
|
+
payloadHash: string | null;
|
|
58
|
+
/** True once an `execution.started` has spent this cycle. */
|
|
59
|
+
spent: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The latest advance request in the log, with its derived state, or `null`.
|
|
63
|
+
*
|
|
64
|
+
* ## Why the daemon asks this before it asks the gate anything
|
|
65
|
+
*
|
|
66
|
+
* A gated advance leaves an `approval.requested` open and its own two records
|
|
67
|
+
* on the log. Until APRV-211 the next tick recomputed a key from the moving
|
|
68
|
+
* head, found no request under it, and opened a second question about the same
|
|
69
|
+
* owed work; the human got one phone buzz per tick for one advance. So the tick
|
|
70
|
+
* now reads the log for what it already asked, and the answer here is that
|
|
71
|
+
* reading.
|
|
72
|
+
*
|
|
73
|
+
* PURE, and over records the caller verified: the enforcement path never reads
|
|
74
|
+
* an unverified log (SPEC.md §11.1). The TTL is applied through
|
|
75
|
+
* {@link requestState}, so a request whose window lapsed reads `expired` here
|
|
76
|
+
* whether or not the daemon has yet materialised an `approval.expired` record —
|
|
77
|
+
* an adopting tick must not wait forever on a question nobody can answer.
|
|
78
|
+
*/
|
|
79
|
+
export declare function openAdvanceRequest(records: readonly EventRecord[], ts: string, ttlMs: number | null): OpenAdvanceRequest | null;
|
|
80
|
+
/** What the log says about the most recent advance cycle. */
|
|
81
|
+
export interface LastAdvance {
|
|
82
|
+
/** The working head the cycle was registered for. */
|
|
83
|
+
toSeq: number;
|
|
84
|
+
ts: string;
|
|
85
|
+
/**
|
|
86
|
+
* `completed` / `failed` for a cycle that executed, `awaiting` for one the
|
|
87
|
+
* gate sent to a human and that nobody has answered, `requested` for one
|
|
88
|
+
* whose question was decided but never executed, `registered` for a cycle
|
|
89
|
+
* that got no further.
|
|
90
|
+
*/
|
|
91
|
+
outcome: "completed" | "failed" | "awaiting" | "requested" | "registered";
|
|
92
|
+
/**
|
|
93
|
+
* Why it failed, as the verb said it (APRV-211): the refusal code and message
|
|
94
|
+
* `cli/log-advance.ts` produced, copied onto `execution.failed` at the write
|
|
95
|
+
* boundary. `null` for every other outcome and for a failure recorded before
|
|
96
|
+
* the field existed — an exit status with no reason, which is the defect this
|
|
97
|
+
* carries the fix for and not a shape any reader may assume away.
|
|
98
|
+
*/
|
|
99
|
+
code: string | null;
|
|
100
|
+
message: string | null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The most recent advance cycle in the log, or `null` when there is none.
|
|
104
|
+
*
|
|
105
|
+
* Read from the LOG rather than from a status file, because the log already
|
|
106
|
+
* carries every fact this answer needs and a second copy could disagree with
|
|
107
|
+
* it. That is what lets `approval doctor` answer it in a different process from
|
|
108
|
+
* the daemon that made the attempt, with no shared state between them, and what
|
|
109
|
+
* makes the answer outlive the daemon's own event stream.
|
|
110
|
+
*/
|
|
111
|
+
export declare function lastAdvance(records: readonly EventRecord[]): LastAdvance | null;
|
|
112
|
+
/**
|
|
113
|
+
* The one-line repair for a pile of dangling executions, spelled once.
|
|
114
|
+
*
|
|
115
|
+
* Every surface that reports an advance nobody closed ends with this command:
|
|
116
|
+
* the daemon's refusal, its warning line, and the `log-advance-cadence` doctor
|
|
117
|
+
* row. Spelled here because a repair an operator has to reconstruct from three
|
|
118
|
+
* slightly different sentences is a repair they retype by hand five times,
|
|
119
|
+
* which is exactly what was observed on 2026-09-05 and exactly what this task
|
|
120
|
+
* removes.
|
|
121
|
+
*/
|
|
122
|
+
export declare const RESOLVE_DANGLING_COMMAND = "approval execution resolve --dangling";
|
|
123
|
+
/**
|
|
124
|
+
* The seq the span named by `daemon-log-advance-<from>-<to>` ends at, or `null`.
|
|
125
|
+
*
|
|
126
|
+
* `null` for a key this runtime did not mint the shape of. A key whose tail is
|
|
127
|
+
* not an integer names no span, so nothing about it can be proved from the
|
|
128
|
+
* refs, and it is reported as unprovable rather than guessed at.
|
|
129
|
+
*/
|
|
130
|
+
export declare function advanceSpanEnd(actionKey: string): number | null;
|
|
131
|
+
/**
|
|
132
|
+
* One dangling execution, and what this checkout's git refs can prove about it.
|
|
133
|
+
*
|
|
134
|
+
* `provenBy` is the whole point: a ref name, or `null`. A sweep may close only
|
|
135
|
+
* the first kind, and every surface that reports the second kind reports it as
|
|
136
|
+
* a human's to establish rather than as a failure — an `execution.failed`
|
|
137
|
+
* written over an advance that actually published would be worse than the
|
|
138
|
+
* dangling record it replaced.
|
|
139
|
+
*/
|
|
140
|
+
export interface DanglingAdvance {
|
|
141
|
+
actionKey: string;
|
|
142
|
+
task: string | null;
|
|
143
|
+
/** The `execution.started` record's position. */
|
|
144
|
+
seq: number;
|
|
145
|
+
/** The seq the key names, or `null` when the key names no span. */
|
|
146
|
+
toSeq: number | null;
|
|
147
|
+
/** The ref that carries `toSeq`, or `null` when nothing in this checkout does. */
|
|
148
|
+
provenBy: string | null;
|
|
149
|
+
}
|
|
150
|
+
/** Every dangling execution whose key is one this daemon mints, in log order. */
|
|
151
|
+
export declare function danglingAdvances(records: readonly EventRecord[]): DanglingAdvance[];
|
|
152
|
+
/**
|
|
153
|
+
* The same list, with each entry's proof filled in from a published state.
|
|
154
|
+
*
|
|
155
|
+
* PURE, and the published state is an argument rather than something read here:
|
|
156
|
+
* `publishedState` lives in `cli/log-advance.ts` because it reads git, a CLI
|
|
157
|
+
* module may not import the daemon, and the daemon, the doctor row and
|
|
158
|
+
* `execution resolve --dangling` must not disagree about which key counts as
|
|
159
|
+
* proved. So the git read happens once in each caller and the RULE lives here.
|
|
160
|
+
*
|
|
161
|
+
* The rule is one comparison: the ref that carries the highest published seq
|
|
162
|
+
* carries every seq below it, because `publishedState` only ever counts a copy
|
|
163
|
+
* of this chain that is a PREFIX of the working log. So a span ending at or
|
|
164
|
+
* below `publishedSeq` is on that ref, and a span above it is on nothing this
|
|
165
|
+
* checkout can see.
|
|
166
|
+
*/
|
|
167
|
+
export declare function proveDanglingAdvances(records: readonly EventRecord[], published: {
|
|
168
|
+
publishedSeq: number;
|
|
169
|
+
publishedRev: string | null;
|
|
170
|
+
}): DanglingAdvance[];
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGENTS.md permissions import (SPEC.md §2, §12) — turn permissions PROSE into
|
|
3
|
+
* a DRAFT policy block a human can read, correct, and confirm.
|
|
4
|
+
*
|
|
5
|
+
* SPEC.md §2 names the gap this closes: AGENTS.md files "routinely contain
|
|
6
|
+
* permissions sections splitting actions into 'allowed without prompting' and
|
|
7
|
+
* 'require approval first'", and "nothing checks". SPEC.md §12 gives the verb:
|
|
8
|
+
* `approval import agents-md` "parses ... permissions sections into draft
|
|
9
|
+
* policy classes for human confirmation".
|
|
10
|
+
*
|
|
11
|
+
* ## This module is deterministic, and there is no model in it
|
|
12
|
+
*
|
|
13
|
+
* Per CLAUDE.md's engineering invariants, routing and policy are pure
|
|
14
|
+
* deterministic code; LLMs are confined to language tasks. Reading a bullet
|
|
15
|
+
* list is a language task in appearance only — its OUTPUT is a permission
|
|
16
|
+
* document, so the mapping is a fixed, ordered, documented keyword table
|
|
17
|
+
* ({@link CLASS_TABLE}), not a judgement. The same bytes always produce the
|
|
18
|
+
* same draft, on any machine, with no network and no clock. An LLM-assisted
|
|
19
|
+
* `--suggest` (proposing classes for bullets this table cannot place) is a
|
|
20
|
+
* separate, opt-in, out-of-scope idea: it would be allowed to propose, never
|
|
21
|
+
* to decide.
|
|
22
|
+
*
|
|
23
|
+
* ## The grammar
|
|
24
|
+
*
|
|
25
|
+
* Input is CommonMark-ish markdown. The scanner is line-based:
|
|
26
|
+
*
|
|
27
|
+
* - **Fenced code blocks** (``` or ~~~, 3+ markers, indented at most 3 spaces)
|
|
28
|
+
* are skipped entirely. A bullet inside an example block is an example.
|
|
29
|
+
* - **Headings** are ATX only: `^ {0,3}#{1,6}\s+text`, trailing `#`s stripped.
|
|
30
|
+
* Setext headings are not recognised; the convention in the wild is ATX.
|
|
31
|
+
* - A heading whose text contains `permissions` (case-insensitive, at any
|
|
32
|
+
* level) opens the **permissions region**. The region closes at the next
|
|
33
|
+
* non-canonical heading whose level is less than or equal to the permissions
|
|
34
|
+
* heading's level.
|
|
35
|
+
* - Three **canonical sub-headings** open a section, matched case-insensitively
|
|
36
|
+
* against {@link SECTION_PHRASES} after normalisation (lowercased, markdown
|
|
37
|
+
* emphasis and backticks stripped, whitespace collapsed, trailing `:`
|
|
38
|
+
* dropped). They are recognised at ANY level and, deliberately, whether or
|
|
39
|
+
* not a parent `Permissions` heading exists: the bare three-heading layout is
|
|
40
|
+
* common in AGENTS.md files.
|
|
41
|
+
* - **Bullets** are `-` or `*` list items (`^\s*[-*]\s+`) appearing while a
|
|
42
|
+
* section is open. A following line that is not blank, not a heading, not a
|
|
43
|
+
* list item and not a fence is a **continuation** and is joined to the
|
|
44
|
+
* previous bullet with a single space. Any heading closes the open section.
|
|
45
|
+
* - Every other line is ignored.
|
|
46
|
+
*
|
|
47
|
+
* Non-canonical headings seen inside the permissions region, and non-canonical
|
|
48
|
+
* headings that interrupt an open section, are reported in `ignored` rather
|
|
49
|
+
* than silently dropped: a heading the importer did not understand may be
|
|
50
|
+
* carrying permissions prose, and the human confirming the draft is the one who
|
|
51
|
+
* should decide.
|
|
52
|
+
*
|
|
53
|
+
* ## Fail closed
|
|
54
|
+
*
|
|
55
|
+
* A bullet the table cannot place is NOT guessed at and NOT dropped. It becomes
|
|
56
|
+
* an `unmapped` entry, is preserved verbatim as a comment in the draft, and is
|
|
57
|
+
* covered by `defaults.autonomy: manual` — the strictest outcome available.
|
|
58
|
+
* Likewise a source with no permissions section produces an empty draft (all
|
|
59
|
+
* classes manual by default) plus a warning, never a permissive one.
|
|
60
|
+
*
|
|
61
|
+
* ## The values draft
|
|
62
|
+
*
|
|
63
|
+
* Since APRV-240 the same file is scanned a second time for four optional
|
|
64
|
+
* headings ("what I value", "what good looks like", "how I like to work",
|
|
65
|
+
* "what I want from you") and their bullets are drafted into a
|
|
66
|
+
* ` ```yaml approval-values ` block (SPEC.md §5.3). Every bullet lands in
|
|
67
|
+
* `wants:`, and nothing is ever placed in `love:`, `like:` or `dislike:`.
|
|
68
|
+
* Grading is the human's act. This importer can see that a line was written
|
|
69
|
+
* down; it cannot see how much its author meant it, and a guessed grade would
|
|
70
|
+
* put words in their mouth inside the one block of `APPROVAL.md` that exists to
|
|
71
|
+
* carry their own.
|
|
72
|
+
*
|
|
73
|
+
* ## Namespaces
|
|
74
|
+
*
|
|
75
|
+
* SPEC.md §7 reserves top-level namespaces to the spec and lets implementations
|
|
76
|
+
* add sub-classes freely. The developer-workstation vocabulary this table emits
|
|
77
|
+
* (`vcs.*`, `deps.*`, `release.*`, `exec.*`, `network.*`, `policy.edit`) is not
|
|
78
|
+
* in the §7 table, which was written for life-admin side effects. That is a
|
|
79
|
+
* deliberate, visible property of a DRAFT: the classes are proposals a human
|
|
80
|
+
* renames or upstreams before confirming, and the draft says so in its header.
|
|
81
|
+
*/
|
|
82
|
+
/** Which prose section a bullet came from. */
|
|
83
|
+
export type AgentsMdSection = "allowed" | "approval-first" | "never";
|
|
84
|
+
/** SPEC.md §5.2 autonomy levels, strictest first. */
|
|
85
|
+
export type Autonomy = "manual" | "supervised" | "autonomous";
|
|
86
|
+
/** One list item of a permissions section. */
|
|
87
|
+
export interface Bullet {
|
|
88
|
+
/** The bullet's text, continuation lines joined, marker stripped. */
|
|
89
|
+
text: string;
|
|
90
|
+
/** 1-based line number of the bullet's first line, for diagnostics. */
|
|
91
|
+
line: number;
|
|
92
|
+
}
|
|
93
|
+
/** The three recognised sections, each in source order. */
|
|
94
|
+
export interface AgentsMdSections {
|
|
95
|
+
allowed: Bullet[];
|
|
96
|
+
approvalFirst: Bullet[];
|
|
97
|
+
never: Bullet[];
|
|
98
|
+
}
|
|
99
|
+
/** Result of {@link parseAgentsMd}. Pure function of the input bytes. */
|
|
100
|
+
export interface AgentsMdParse {
|
|
101
|
+
sections: AgentsMdSections;
|
|
102
|
+
/** Headings the scanner met inside the permissions area and did not use. */
|
|
103
|
+
ignored: string[];
|
|
104
|
+
/** Human-facing notes; never a reason to relax anything. */
|
|
105
|
+
warnings: string[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The class heuristic: an ORDERED, STABLE table. First match wins, and the
|
|
109
|
+
* order of this array IS the precedence. Each entry lists alternative keyword
|
|
110
|
+
* conjunctions; an alternative matches when every one of its keywords appears
|
|
111
|
+
* as a substring of the normalised bullet text.
|
|
112
|
+
*
|
|
113
|
+
* Ordering rules, so future edits stay principled:
|
|
114
|
+
*
|
|
115
|
+
* 1. The most consequential and most specific classes come first, because a
|
|
116
|
+
* bullet naming several actions ("git push, merges to main, tag creation")
|
|
117
|
+
* is placed by its first match and the safer placement is the broader,
|
|
118
|
+
* more consequential class.
|
|
119
|
+
* 2. `network.call` precedes `deps.add` on purpose: "any network call beyond
|
|
120
|
+
* package installs" contains "install" and is not a dependency bullet.
|
|
121
|
+
* 3. `vcs.push` precedes `vcs.push.main`: a bullet naming pushes generally
|
|
122
|
+
* should govern all pushes, not only pushes to the default branch.
|
|
123
|
+
* 4. Generic verbs (`edit`, `read`) come last, since almost every bullet
|
|
124
|
+
* contains one.
|
|
125
|
+
*
|
|
126
|
+
* Adding a keyword here changes what a draft proposes for existing files, so
|
|
127
|
+
* the table is pinned byte-for-byte by `tests/agents-md.test.ts` fixtures.
|
|
128
|
+
*/
|
|
129
|
+
export declare const CLASS_TABLE: ReadonlyArray<{
|
|
130
|
+
readonly cls: string;
|
|
131
|
+
readonly any: ReadonlyArray<readonly string[]>;
|
|
132
|
+
}>;
|
|
133
|
+
/** Text used for keyword matching: lowercased with whitespace collapsed. */
|
|
134
|
+
export declare function normaliseBullet(text: string): string;
|
|
135
|
+
/**
|
|
136
|
+
* The class this bullet proposes, or `null` when the table cannot place it.
|
|
137
|
+
*
|
|
138
|
+
* Deterministic, total, and side-effect free: the first entry of
|
|
139
|
+
* {@link CLASS_TABLE} with a satisfied keyword conjunction wins.
|
|
140
|
+
*/
|
|
141
|
+
export declare function classifyBullet(text: string): string | null;
|
|
142
|
+
/**
|
|
143
|
+
* Parse the permissions region of an AGENTS.md-style document.
|
|
144
|
+
*
|
|
145
|
+
* Never throws. Pure function of `markdown`; see the module header for the
|
|
146
|
+
* grammar it accepts.
|
|
147
|
+
*/
|
|
148
|
+
export declare function parseAgentsMd(markdown: string): AgentsMdParse;
|
|
149
|
+
/** Result of {@link parseValuesHeadings}. Pure function of the input bytes. */
|
|
150
|
+
export interface ValuesDraft {
|
|
151
|
+
/** The recognised headings, normalised, in source order. */
|
|
152
|
+
headings: string[];
|
|
153
|
+
/** Bullets destined for `wants:`: truncated, deduped, capped. */
|
|
154
|
+
wants: string[];
|
|
155
|
+
/** Bullets past {@link VALUES_MAX_ITEMS}, kept so none is dropped silently. */
|
|
156
|
+
overflow: string[];
|
|
157
|
+
/** Human-facing notes; never a reason to relax anything. */
|
|
158
|
+
warnings: string[];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Collect the bullets under the optional values headings of an AGENTS.md-style
|
|
162
|
+
* document (SPEC.md §5.3).
|
|
163
|
+
*
|
|
164
|
+
* ## Everything goes to `wants`, and nothing is graded
|
|
165
|
+
*
|
|
166
|
+
* The values block has three standing grades (`love`, `like`, `dislike`) and
|
|
167
|
+
* one behavioural list (`wants`). This function fills `wants` and leaves the
|
|
168
|
+
* three grades empty, always. A grade is a statement of taste, and it is the
|
|
169
|
+
* human's to make: the source shows that a line was written under a heading, it
|
|
170
|
+
* does not show how strongly it was meant, and an importer that inferred
|
|
171
|
+
* "love" from an exclamation mark or a heading's wording would be putting words
|
|
172
|
+
* in its reader's mouth in the one block of `APPROVAL.md` that exists to carry
|
|
173
|
+
* theirs. `wants` is the honest destination for a bullet whose grade is
|
|
174
|
+
* unknown: it says the operator asked for something, which is exactly what a
|
|
175
|
+
* bullet under "what I want from you" demonstrates.
|
|
176
|
+
*
|
|
177
|
+
* ## The scan
|
|
178
|
+
*
|
|
179
|
+
* A second line-based pass over the same primitives {@link parseAgentsMd} uses,
|
|
180
|
+
* with the same rules: fenced code blocks are skipped whole (a bullet in an
|
|
181
|
+
* example block is an example), headings are ATX at any level, a values heading
|
|
182
|
+
* opens a section, any other heading closes one, and a non-blank line that is
|
|
183
|
+
* not a bullet, heading or fence is a continuation joined to the previous
|
|
184
|
+
* bullet with a single space.
|
|
185
|
+
*
|
|
186
|
+
* Never throws. No clock, no filesystem, no network.
|
|
187
|
+
*/
|
|
188
|
+
export declare function parseValuesHeadings(markdown: string): ValuesDraft;
|
|
189
|
+
/** One proposed class rule, with the bullets that produced it. */
|
|
190
|
+
export interface DraftClass {
|
|
191
|
+
cls: string;
|
|
192
|
+
autonomy: Autonomy;
|
|
193
|
+
/** Every bullet that mapped here, in source order. */
|
|
194
|
+
bullets: Array<{
|
|
195
|
+
text: string;
|
|
196
|
+
section: AgentsMdSection;
|
|
197
|
+
}>;
|
|
198
|
+
/** The bullet that decided the autonomy (strictest, earliest on ties). */
|
|
199
|
+
from: {
|
|
200
|
+
text: string;
|
|
201
|
+
section: AgentsMdSection;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/** A bullet the table could not place. Covered by `defaults.autonomy`. */
|
|
205
|
+
export interface UnmappedBullet {
|
|
206
|
+
text: string;
|
|
207
|
+
section: AgentsMdSection;
|
|
208
|
+
}
|
|
209
|
+
/** Result of {@link importAgentsMd}: the draft, and everything it could not use. */
|
|
210
|
+
export interface AgentsMdImport {
|
|
211
|
+
classes: DraftClass[];
|
|
212
|
+
unmapped: UnmappedBullet[];
|
|
213
|
+
ignored: string[];
|
|
214
|
+
warnings: string[];
|
|
215
|
+
/**
|
|
216
|
+
* The values headings the same source declared (APRV-240). Empty `headings`
|
|
217
|
+
* means the source declared none, and no values fence is rendered at all: an
|
|
218
|
+
* absent values block is a declaration in its own right (SPEC.md §5.3), and a
|
|
219
|
+
* draft of one would be this importer inventing the declaration.
|
|
220
|
+
*/
|
|
221
|
+
values: ValuesDraft;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Parse and classify: the whole deterministic half of `approval import
|
|
225
|
+
* agents-md`. Emitting bytes is {@link renderDraftPolicy}'s job.
|
|
226
|
+
*
|
|
227
|
+
* Conflicts follow SPEC.md §5.2 "deny beats allow": when bullets from different
|
|
228
|
+
* sections claim the same class, the strictest autonomy wins and a warning
|
|
229
|
+
* names both bullets, because a class that appears in both an allow list and an
|
|
230
|
+
* approval list is a contradiction in the SOURCE that a human must resolve.
|
|
231
|
+
*/
|
|
232
|
+
export declare function importAgentsMd(markdown: string): AgentsMdImport;
|
|
233
|
+
/** Info string of the machine-readable policy block (SPEC.md §5). */
|
|
234
|
+
export declare const POLICY_INFO_STRING = "yaml approval-policy";
|
|
235
|
+
/**
|
|
236
|
+
* Render the values draft, fenced (APRV-240).
|
|
237
|
+
*
|
|
238
|
+
* The argument is the whole {@link ValuesDraft} rather than its bullets alone,
|
|
239
|
+
* because the entries past the cap have to appear in the output as comments:
|
|
240
|
+
* the renderer needs to see what was left out in order to say so.
|
|
241
|
+
*
|
|
242
|
+
* Deterministic, like everything else here. Entries are emitted as
|
|
243
|
+
* double-quoted scalars via `JSON.stringify`, whose escapes are all valid YAML
|
|
244
|
+
* double-quoted escapes, so a bullet full of backticks, colons and `#` survives
|
|
245
|
+
* the round trip without the renderer having to reason about YAML quoting.
|
|
246
|
+
*/
|
|
247
|
+
export declare function renderFencedValuesDraft(draft: ValuesDraft, source: string): string;
|
|
248
|
+
/**
|
|
249
|
+
* The fenced values draft for an import, or `null` when the source declared no
|
|
250
|
+
* values headings. The `--json` surface's `values_draft` field, verbatim.
|
|
251
|
+
*/
|
|
252
|
+
export declare function valuesDraftOf(result: AgentsMdImport, source: string): string | null;
|
|
253
|
+
/**
|
|
254
|
+
* Render the draft policy YAML. Deterministic: no clock, no cwd, no
|
|
255
|
+
* randomness — the only inputs are the import result and the source label, so
|
|
256
|
+
* the same file always produces the same bytes.
|
|
257
|
+
*
|
|
258
|
+
* With `values` omitted (or from a source that declared no values headings) the
|
|
259
|
+
* output is bare YAML with no fence, a valid policy under
|
|
260
|
+
* `schema/policy.schema.json` that loads through `loadPolicy` once wrapped in a
|
|
261
|
+
* ` ```yaml approval-policy ` fence.
|
|
262
|
+
*
|
|
263
|
+
* With a values draft the shape changes, and it has to. A values fence appended
|
|
264
|
+
* to bare YAML could not be pasted into a policy fence, because the values
|
|
265
|
+
* block's own closing fence would close the policy block and leave a file that
|
|
266
|
+
* loads as neither. So a two-block draft is emitted already fenced: the policy
|
|
267
|
+
* inside its ` ```yaml approval-policy ` fence, then the values fence after it,
|
|
268
|
+
* which is the shape `APPROVAL.md` itself has and which the policy loader and
|
|
269
|
+
* the values reader each read straight off disk.
|
|
270
|
+
*/
|
|
271
|
+
export declare function renderDraftPolicy(result: AgentsMdImport, source: string, values?: ValuesDraft | null): string;
|
|
272
|
+
/**
|
|
273
|
+
* The draft wrapped in its ` ```yaml approval-policy ` fence, for stdout, with
|
|
274
|
+
* the values fence printed after it when the source declared values headings.
|
|
275
|
+
*/
|
|
276
|
+
export declare function renderFencedDraft(result: AgentsMdImport, source: string, values?: ValuesDraft | null): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Strict parsing and path classification for Codex `apply_patch` (APRV-312). */
|
|
2
|
+
import { type ProtectedPathEntry } from "./command-class.js";
|
|
3
|
+
export type ApplyPatchOperation = {
|
|
4
|
+
kind: "add";
|
|
5
|
+
path: string;
|
|
6
|
+
lines: string[];
|
|
7
|
+
} | {
|
|
8
|
+
kind: "delete";
|
|
9
|
+
path: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "update";
|
|
12
|
+
path: string;
|
|
13
|
+
moveTo?: string;
|
|
14
|
+
hunks: ApplyPatchHunk[];
|
|
15
|
+
eof: boolean;
|
|
16
|
+
};
|
|
17
|
+
export interface ApplyPatchHunk {
|
|
18
|
+
header: string;
|
|
19
|
+
lines: string[];
|
|
20
|
+
}
|
|
21
|
+
export type ApplyPatchParseResult = {
|
|
22
|
+
ok: true;
|
|
23
|
+
operations: ApplyPatchOperation[];
|
|
24
|
+
} | {
|
|
25
|
+
ok: false;
|
|
26
|
+
detail: string;
|
|
27
|
+
};
|
|
28
|
+
/** Parse one exact apply_patch envelope. No filesystem reads occur here. */
|
|
29
|
+
export declare function parseApplyPatch(raw: string): ApplyPatchParseResult;
|
|
30
|
+
export interface ApplyPatchTarget {
|
|
31
|
+
role: "add" | "delete" | "update" | "move-destination";
|
|
32
|
+
path: string;
|
|
33
|
+
absolute: string;
|
|
34
|
+
resolved: string;
|
|
35
|
+
classes: string[];
|
|
36
|
+
}
|
|
37
|
+
export type ApplyPatchClassification = {
|
|
38
|
+
ok: true;
|
|
39
|
+
operations: ApplyPatchOperation[];
|
|
40
|
+
targets: ApplyPatchTarget[];
|
|
41
|
+
classes: string[];
|
|
42
|
+
} | {
|
|
43
|
+
ok: false;
|
|
44
|
+
detail: string;
|
|
45
|
+
};
|
|
46
|
+
/** Resolve and classify every source and destination of a parsed patch. */
|
|
47
|
+
export declare function classifyApplyPatch(parsed: {
|
|
48
|
+
operations: ApplyPatchOperation[];
|
|
49
|
+
}, cwd: string, protectedPaths?: readonly ProtectedPathEntry[]): ApplyPatchClassification;
|