@aperant/framework 0.8.4 → 0.8.7

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.
Files changed (127) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/agents/apt-planner.md +34 -3
  3. package/dist/cli/commands/coverage-check.d.mts.map +1 -1
  4. package/dist/cli/commands/coverage-check.mjs +74 -7
  5. package/dist/cli/commands/coverage-check.mjs.map +1 -1
  6. package/dist/cli/commands/detect-runtime.d.mts.map +1 -1
  7. package/dist/cli/commands/detect-runtime.mjs +18 -12
  8. package/dist/cli/commands/detect-runtime.mjs.map +1 -1
  9. package/dist/cli/coverage-check/user-outcomes.d.mts +84 -0
  10. package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -0
  11. package/dist/cli/coverage-check/user-outcomes.mjs +265 -0
  12. package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -0
  13. package/dist/cli/host/detect.mjs +1 -1
  14. package/dist/cli/host/detect.mjs.map +1 -1
  15. package/dist/cli/install/install-from-source.mjs +3 -3
  16. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  17. package/dist/cli/install/legacy-paths.mjs +2 -0
  18. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  19. package/dist/cli/install/manifest.d.mts +13 -0
  20. package/dist/cli/install/manifest.d.mts.map +1 -1
  21. package/dist/cli/install/manifest.mjs +5 -0
  22. package/dist/cli/install/manifest.mjs.map +1 -1
  23. package/dist/cli/install/pipeline.d.mts +15 -0
  24. package/dist/cli/install/pipeline.d.mts.map +1 -1
  25. package/dist/cli/install/pipeline.mjs +27 -0
  26. package/dist/cli/install/pipeline.mjs.map +1 -1
  27. package/dist/cli/install/transforms/codex.d.mts +1 -1
  28. package/dist/cli/install/transforms/codex.d.mts.map +1 -1
  29. package/dist/cli/install/transforms/codex.mjs +43 -2
  30. package/dist/cli/install/transforms/codex.mjs.map +1 -1
  31. package/dist/cli/install/version-header.d.mts.map +1 -1
  32. package/dist/cli/install/version-header.mjs +7 -1
  33. package/dist/cli/install/version-header.mjs.map +1 -1
  34. package/dist/cli/verify-proof/idl/index.d.mts +1 -1
  35. package/dist/cli/verify-proof/idl/index.mjs +7 -8
  36. package/dist/cli/verify-proof/idl/index.mjs.map +1 -1
  37. package/dist/cli/verify-proof/idl/types.d.ts +5 -4
  38. package/dist/cli/verify-proof/idl/types.d.ts.map +1 -1
  39. package/dist/cli/verify-proof/idl/types.js +4 -3
  40. package/dist/cli/verify-proof/idl/types.js.map +1 -1
  41. package/dist/cli/verify-proof/resolver.d.mts +63 -1
  42. package/dist/cli/verify-proof/resolver.d.mts.map +1 -1
  43. package/dist/cli/verify-proof/resolver.mjs +164 -4
  44. package/dist/cli/verify-proof/resolver.mjs.map +1 -1
  45. package/dist/cli/verify-proof/runtime-detect.d.mts +6 -3
  46. package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -1
  47. package/dist/cli/verify-proof/runtime-detect.mjs +104 -11
  48. package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -1
  49. package/dist/cli/verify-proof/trust.d.mts +1 -1
  50. package/dist/cli/verify-proof/trust.d.mts.map +1 -1
  51. package/dist/cli/verify-proof/trust.mjs +1 -1
  52. package/dist/driver-sdk/conformance.d.ts +41 -0
  53. package/dist/driver-sdk/conformance.d.ts.map +1 -0
  54. package/dist/driver-sdk/conformance.js +123 -0
  55. package/dist/driver-sdk/conformance.js.map +1 -0
  56. package/dist/driver-sdk/errors.d.ts +75 -0
  57. package/dist/driver-sdk/errors.d.ts.map +1 -0
  58. package/dist/driver-sdk/errors.js +80 -0
  59. package/dist/driver-sdk/errors.js.map +1 -0
  60. package/dist/driver-sdk/idl.d.ts +221 -0
  61. package/dist/driver-sdk/idl.d.ts.map +1 -0
  62. package/dist/driver-sdk/idl.js +140 -0
  63. package/dist/driver-sdk/idl.js.map +1 -0
  64. package/dist/driver-sdk/index.d.ts +28 -0
  65. package/dist/driver-sdk/index.d.ts.map +1 -0
  66. package/dist/driver-sdk/index.js +28 -0
  67. package/dist/driver-sdk/index.js.map +1 -0
  68. package/dist/driver-sdk/manifest.d.ts +93 -0
  69. package/dist/driver-sdk/manifest.d.ts.map +1 -0
  70. package/dist/driver-sdk/manifest.js +12 -0
  71. package/dist/driver-sdk/manifest.js.map +1 -0
  72. package/dist/driver-sdk/retry.d.ts +33 -0
  73. package/dist/driver-sdk/retry.d.ts.map +1 -0
  74. package/dist/driver-sdk/retry.js +50 -0
  75. package/dist/driver-sdk/retry.js.map +1 -0
  76. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  77. package/dist/plugin/agents/apt-planner.md +34 -3
  78. package/dist/plugin/skills/apt-plan/SKILL.md +50 -0
  79. package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -1
  80. package/dist/plugin/skills/apt-research/SKILL.md +526 -0
  81. package/dist/plugin/skills/apt-research/appendices/budget-loop.md +397 -0
  82. package/dist/plugin/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  83. package/dist/plugin/skills/apt-research/appendices/domain-tools.md +236 -0
  84. package/dist/plugin/skills/apt-spar/SKILL.md +19 -7
  85. package/dist/plugin/skills/apt-verify-proof/SKILL.md +41 -2
  86. package/dist/schemas/quick-task.d.ts +17 -17
  87. package/drivers/.gitkeep +0 -0
  88. package/drivers/api/README.md +40 -0
  89. package/drivers/api/driver.mjs +59 -0
  90. package/drivers/api/manifest.json +26 -0
  91. package/drivers/browser/README.md +105 -0
  92. package/drivers/browser/driver.mjs +134 -0
  93. package/drivers/browser/manifest.json +35 -0
  94. package/drivers/cli/README.md +44 -0
  95. package/drivers/cli/driver.mjs +62 -0
  96. package/drivers/cli/manifest.json +28 -0
  97. package/drivers/electron/README.md +64 -0
  98. package/drivers/electron/driver.mjs +87 -0
  99. package/drivers/electron/manifest.json +37 -0
  100. package/package.json +7 -3
  101. package/skills/apt-plan/SKILL.md +50 -0
  102. package/skills/apt-planner.md +16 -0
  103. package/skills/apt-pr-review/SKILL.md +1 -1
  104. package/skills/apt-research/SKILL.md +526 -0
  105. package/skills/apt-research/appendices/budget-loop.md +397 -0
  106. package/skills/apt-research/appendices/claim-graph-schema.md +206 -0
  107. package/skills/apt-research/appendices/domain-tools.md +236 -0
  108. package/skills/apt-spar/SKILL.md +19 -7
  109. package/skills/apt-verify-proof/SKILL.md +41 -2
  110. package/src/cli/commands/coverage-check.mjs +126 -44
  111. package/src/cli/commands/detect-runtime.mjs +23 -14
  112. package/src/cli/coverage-check/user-outcomes.mjs +273 -0
  113. package/src/cli/host/detect.mjs +1 -1
  114. package/src/cli/install/install-from-source.mjs +3 -3
  115. package/src/cli/install/legacy-paths.mjs +2 -0
  116. package/src/cli/install/manifest.mjs +5 -0
  117. package/src/cli/install/pipeline.mjs +28 -0
  118. package/src/cli/install/transforms/codex.mjs +51 -2
  119. package/src/cli/install/version-header.mjs +7 -1
  120. package/src/cli/verify-proof/idl/index.mjs +7 -8
  121. package/src/cli/verify-proof/idl/types.ts +5 -4
  122. package/src/cli/verify-proof/manifest-schema.json +2 -1
  123. package/src/cli/verify-proof/resolver.mjs +171 -4
  124. package/src/cli/verify-proof/runtime-detect.mjs +99 -10
  125. package/src/cli/verify-proof/trust.mjs +1 -1
  126. package/templates/proof-verification.md +32 -3
  127. package/workflows/verify-proof.md +78 -9
@@ -0,0 +1,105 @@
1
+ # Browser Driver
2
+
3
+ Bundled, first-party verify-proof driver targeting Chromium / Firefox /
4
+ WebKit via two transports: the Puppeteer MCP server OR the
5
+ `agent-browser` skill (Claude Code / OpenCode hosts).
6
+
7
+ ## When Aperant picks this driver
8
+
9
+ `/apt:verify-proof` auto-selects `browser` when:
10
+
11
+ - The project's `package.json` declares a web framework (Next.js, React,
12
+ Vue, Svelte, Angular, Solid) AND the project is NOT an Electron /
13
+ Tauri / React Native container.
14
+ - The required IDL capabilities are a subset of: `navigate`, `click`,
15
+ `type`, `screenshot`, `assert_visible`, `assert_text`, `wait_for_idle`,
16
+ `execute_js`.
17
+
18
+ ## Capabilities
19
+
20
+ The driver maps each IDL verb to BOTH transports. The workflow runner
21
+ picks the transport at dispatch time via `chooseTransport({has_skill_tool})`;
22
+ the chosen transport is recorded on each VerbResult as `transport: "skill" | "mcp"`.
23
+
24
+ | IDL verb | Puppeteer MCP tool | agent-browser CLI |
25
+ |----------|--------------------|--------------------|
26
+ | `navigate` | `mcp__puppeteer__puppeteer_navigate` | `agent-browser open <url>` |
27
+ | `click` | `mcp__puppeteer__puppeteer_click` | `agent-browser click @e<N>` |
28
+ | `type` | `mcp__puppeteer__puppeteer_fill` | `agent-browser fill @e<N> <text>` |
29
+ | `screenshot` | `mcp__puppeteer__puppeteer_screenshot` | `agent-browser screenshot` |
30
+ | `assert_visible` | `mcp__puppeteer__puppeteer_evaluate` (selector check) | `agent-browser snapshot` + `agent-browser get role <sel>` |
31
+ | `assert_text` | `mcp__puppeteer__puppeteer_evaluate` (textContent match) | `agent-browser snapshot` + `agent-browser get text <sel>` |
32
+ | `wait_for_idle` | `mcp__puppeteer__puppeteer_evaluate` (networkidle check) | `agent-browser wait --load networkidle` |
33
+ | `execute_js` | `mcp__puppeteer__puppeteer_evaluate` | `agent-browser eval <script>` |
34
+
35
+ ## Transport selection
36
+
37
+ The driver carries TWO transport declarations on its manifest:
38
+
39
+ ```json
40
+ "transport": { "mcp_server_id": "puppeteer", "skill_id": "agent-browser" }
41
+ ```
42
+
43
+ The workflow runner picks between them via the driver's exported
44
+ `chooseTransport({has_skill_tool})` helper. The selection rule:
45
+
46
+ - **`Skill` tool exposed by host CLI** (Claude Code, OpenCode) → `'skill'`.
47
+ Dispatches via `Skill({skill: "agent-browser", args: ...})`. One fewer
48
+ external dep (no MCP server install).
49
+ - **No Skill tool** (legacy host, MCP-only environment) → `'mcp'`.
50
+ Dispatches via `mcp__puppeteer__*` (the existing path).
51
+
52
+ The resolver's "one driver per runtime tag" invariant is preserved —
53
+ `browser` stays one driver with two transport options. No new
54
+ driverId; no resolver disambiguation needed.
55
+
56
+ ## Manifest
57
+
58
+ - **Priority:** 100 (highest among bundled drivers — browser is the most
59
+ common target).
60
+ - **Stability:** `ga`.
61
+ - **Trust:** `first_party_signed` (default-allow).
62
+ - **Transport:** `{ mcp_server_id: "puppeteer", skill_id: "agent-browser" }`.
63
+ The `McpRegistry` handles consent gating for the MCP path per AC21;
64
+ the existing skill-permissions registry handles consent for the
65
+ Skill path.
66
+
67
+ ## V1 scope — adapter is thin
68
+
69
+ This driver's `driver.mjs` is a manifest-conformant **adapter shim** in
70
+ v1. Each verb validates its args against `IDL_VERB_ARGS`, then returns
71
+ a VerbResult stub. **The actual transport dispatch — invoking
72
+ `mcp__puppeteer__*` tools OR `Skill({skill: "agent-browser", ...})` —
73
+ happens at the workflow-runner layer**, not inside this file: the model
74
+ loads the workflow prose, the workflow names IDL verbs, and the model
75
+ calls the tool whose name the driver manifest binds via
76
+ `transport.mcp_server_id` (MCP path) or `transport.skill_id` (Skill
77
+ path). The split exists because both tool surfaces are the model's, not
78
+ the driver's runtime — drivers in v1 are the validator + manifest +
79
+ resolver-anchor, not the executor.
80
+
81
+ The driver's adapter shim DOES record the chosen transport on the
82
+ returned VerbResult (`transport: "skill" | "mcp"`) so the workflow
83
+ runner can populate the corresponding field on each
84
+ `verification.json:tests[]` entry for audit purposes.
85
+
86
+ Follow-up `verify-proof-driver-transport-wiring-v2` will lift transport
87
+ dispatch into the driver process itself for drivers that want
88
+ out-of-band execution (e.g. node-side fetch for the api driver, or
89
+ direct Puppeteer.connect for headless CI).
90
+
91
+ ## Overriding
92
+
93
+ To force the resolver to pick a different driver:
94
+
95
+ ```json
96
+ // .aperant/config.json
97
+ {
98
+ "verification": {
99
+ "runtimes": ["electron", "cli"]
100
+ }
101
+ }
102
+ ```
103
+
104
+ The resolver places listed driver IDs first; the highest-coverage / most-
105
+ compatible / highest-priority driver still wins within the override list.
@@ -0,0 +1,134 @@
1
+ /**
2
+ * browser/driver.mjs — bundled browser driver.
3
+ *
4
+ * Maps IDL verbs to one of two transports: the Puppeteer MCP server or
5
+ * the `agent-browser` skill. The manifest declares both:
6
+ * transport.mcp_server_id = "puppeteer" (legacy / non-Skill hosts)
7
+ * transport.skill_id = "agent-browser" (Claude Code / OpenCode)
8
+ *
9
+ * Transport selection lives in the WORKFLOW RUNNER layer — that's the
10
+ * site that knows whether the host CLI exposes the Skill tool. This
11
+ * driver exposes `chooseTransport({has_skill_tool})` as a helper so the
12
+ * workflow runner can ask the driver which transport it would pick;
13
+ * the driver records the chosen transport on each VerbResult via the
14
+ * optional `transport: 'skill' | 'mcp'` field (additive — existing
15
+ * consumers that ignore the field keep working).
16
+ *
17
+ * Per ID-01 — the workflow prose NEVER names mcp__puppeteer__* tools
18
+ * OR concrete `agent-browser` CLI subcommands. Both mappings live ONLY
19
+ * here in driver.mjs.
20
+ *
21
+ * IDL verb → MCP tool mapping (legacy transport):
22
+ * navigate → mcp__puppeteer__puppeteer_navigate
23
+ * click → mcp__puppeteer__puppeteer_click
24
+ * type → mcp__puppeteer__puppeteer_fill
25
+ * screenshot → mcp__puppeteer__puppeteer_screenshot
26
+ * assert_visible → mcp__puppeteer__puppeteer_evaluate (DOM selector)
27
+ * assert_text → mcp__puppeteer__puppeteer_evaluate (textContent match)
28
+ * wait_for_idle → mcp__puppeteer__puppeteer_evaluate (networkidle check)
29
+ * execute_js → mcp__puppeteer__puppeteer_evaluate
30
+ *
31
+ * IDL verb → agent-browser CLI mapping (Skill transport):
32
+ * navigate → agent-browser open <url>
33
+ * click → agent-browser click @e<N>
34
+ * type → agent-browser fill @e<N> <text>
35
+ * screenshot → agent-browser screenshot
36
+ * assert_visible → agent-browser snapshot + agent-browser get role <selector>
37
+ * assert_text → agent-browser snapshot + agent-browser get text <selector>
38
+ * wait_for_idle → agent-browser wait --load networkidle
39
+ * execute_js → agent-browser eval <script>
40
+ *
41
+ * The driver is a thin adapter: it validates input shape via the IDL
42
+ * arg-metadata, builds the transport-specific args, and returns a
43
+ * VerbResult. The actual transport dispatch happens at the workflow
44
+ * runner layer (which holds the model's tool surface — `Skill(...)` for
45
+ * skill transport, `mcp__puppeteer__*` for MCP transport). For
46
+ * conformance testing we expose the verb signatures as identity stubs
47
+ * returning VerbResult — the SDK conformance kit then validates the
48
+ * shape contract.
49
+ */
50
+
51
+ import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
52
+
53
+ /**
54
+ * Pick a transport given the host environment's capabilities. When the
55
+ * host exposes the Skill tool (Claude Code, OpenCode), prefer the
56
+ * `agent-browser` skill — one fewer external dependency (no Puppeteer
57
+ * MCP server install). Otherwise fall back to the MCP transport.
58
+ *
59
+ * @param {{has_skill_tool: boolean}} env
60
+ * @returns {('skill'|'mcp')}
61
+ */
62
+ export function chooseTransport(env) {
63
+ if (env && env.has_skill_tool === true) return 'skill'
64
+ return 'mcp'
65
+ }
66
+
67
+ /** Build a VerbResult given the actual transport-layer result. */
68
+ function ok(evidence_paths = [], transport) {
69
+ const result = { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
70
+ if (transport) result.transport = transport
71
+ return result
72
+ }
73
+ function fail(error, transport) {
74
+ const result = { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
75
+ if (transport) result.transport = transport
76
+ return result
77
+ }
78
+
79
+ /**
80
+ * Validate args + return a VerbResult shape stub. Real transport
81
+ * invocation happens at the workflow-runner layer (model holds the tool
82
+ * surface — `Skill({skill: "agent-browser", args})` OR
83
+ * `mcp__puppeteer__*` depending on host capabilities).
84
+ *
85
+ * @param {string} verb
86
+ * @param {Record<string, unknown>} args
87
+ * @param {('skill'|'mcp')} [transport] — optional, recorded on the result for audit
88
+ * @returns {{status: 'success'|'fail'|'skip', evidence_paths: string[], duration_ms: number, retry_count: number, error?: string, transport?: 'skill'|'mcp'}}
89
+ */
90
+ function adaptVerb(verb, args, transport) {
91
+ const check = validateVerbArgs(verb, args)
92
+ if (!check.valid) {
93
+ return fail(`browser.${verb}: missing required args: ${check.missing.join(', ')}`, transport)
94
+ }
95
+ // Identity stub for the conformance test surface. Production execution
96
+ // dispatches to Skill({skill: "agent-browser", ...}) OR mcp__puppeteer__*
97
+ // at the workflow-runner layer.
98
+ if (verb === 'screenshot' && typeof args.output_path === 'string') {
99
+ return ok([args.output_path], transport)
100
+ }
101
+ return ok([], transport)
102
+ }
103
+
104
+ export async function navigate(args) {
105
+ return adaptVerb('navigate', args)
106
+ }
107
+
108
+ export async function click(args) {
109
+ return adaptVerb('click', args)
110
+ }
111
+
112
+ export async function type(args) {
113
+ return adaptVerb('type', args)
114
+ }
115
+
116
+ export async function screenshot(args) {
117
+ return adaptVerb('screenshot', args)
118
+ }
119
+
120
+ export async function assert_visible(args) {
121
+ return adaptVerb('assert_visible', args)
122
+ }
123
+
124
+ export async function assert_text(args) {
125
+ return adaptVerb('assert_text', args)
126
+ }
127
+
128
+ export async function wait_for_idle(args) {
129
+ return adaptVerb('wait_for_idle', args)
130
+ }
131
+
132
+ export async function execute_js(args) {
133
+ return adaptVerb('execute_js', args)
134
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "driverId": "browser",
3
+ "version": "0.1.0",
4
+ "targets": ["browser@chrome", "browser@firefox", "browser@webkit"],
5
+ "capabilities": [
6
+ "navigate",
7
+ "click",
8
+ "type",
9
+ "screenshot",
10
+ "assert_visible",
11
+ "assert_text",
12
+ "wait_for_idle",
13
+ "execute_js"
14
+ ],
15
+ "evidence_capabilities": ["screenshot", "dom_dump", "capture_logs"],
16
+ "frameworkApiRange": "^1.0.0",
17
+ "priority": 100,
18
+ "stability": "ga",
19
+ "trust": {
20
+ "tier": "first_party_signed",
21
+ "publisher": {
22
+ "name": "Aperant",
23
+ "npm_scope": "@aperant",
24
+ "github_org": "Mikalsen-AI",
25
+ "verified": true
26
+ }
27
+ },
28
+ "security": {
29
+ "sandbox_profile": "workspace_write",
30
+ "network": "allowlist",
31
+ "requires_user_approval": false
32
+ },
33
+ "audit": { "log_invocations": true, "log_manifest_hash": true },
34
+ "transport": { "mcp_server_id": "puppeteer", "skill_id": "agent-browser" }
35
+ }
@@ -0,0 +1,44 @@
1
+ # CLI Driver
2
+
3
+ Bundled headless verify-proof driver for CI / non-GUI environments.
4
+ Doesn't open a browser, doesn't render anything — asserts on command
5
+ output, captured logs, and runtime state.
6
+
7
+ ## When Aperant picks this driver
8
+
9
+ `/apt:verify-proof` auto-selects `cli` when:
10
+
11
+ - The project has no GUI runtime (no Electron, no Tauri, no web frontend).
12
+ - The required IDL capabilities are: `execute_js`, `capture_logs`,
13
+ `assert_output`, `assert_text`, `dump_state`.
14
+ - Used as a fallback for CI runs where the higher-priority browser /
15
+ electron drivers can't be reached.
16
+
17
+ ## Capabilities
18
+
19
+ | IDL verb | Behavior |
20
+ |----------|----------|
21
+ | `execute_js` | Run a Node snippet via `execFile('node', ['-e', script])` |
22
+ | `capture_logs` | Append stdout/stderr to an evidence file |
23
+ | `assert_output` | Match a pattern against captured stdout/stderr |
24
+ | `assert_text` | Match a pattern against a captured text artifact |
25
+ | `dump_state` | Write a JSON dump of runner CWD + env keys |
26
+
27
+ ## Manifest
28
+
29
+ - **Priority:** 60 (below browser=100 / electron=90 / api=50).
30
+ - **Stability:** `ga`.
31
+ - **Trust:** `first_party_signed`.
32
+ - **Network posture:** `deny`.
33
+ - **Transport:** `{ builtin: true }` — the driver IS the transport.
34
+ - **Security:** `allow_shell: false`. The driver routes every spawn
35
+ through `execFile` (no shell, no pipes, no redirects) per ID-03.
36
+
37
+ ## V1 scope — adapter is thin
38
+
39
+ Like the other bundled drivers, `driver.mjs` is a v1 manifest-conformant
40
+ adapter shim: each verb validates its args via `IDL_VERB_ARGS` and
41
+ returns a VerbResult stub. Actual Node-side execution lands in
42
+ `verify-proof-driver-transport-wiring-v2` (follow-up). The CLI driver
43
+ is the most natural candidate for first direct-dispatch since
44
+ `{ builtin: true }` already means "driver process owns the transport."
@@ -0,0 +1,62 @@
1
+ /**
2
+ * cli/driver.mjs — bundled headless CLI driver.
3
+ *
4
+ * Targets non-GUI CI verify-proof runs. Asserts on command stdout / stderr
5
+ * and on captured log streams. The transport is `{ builtin: true }` — the
6
+ * driver itself shells out via packages/framework/src/cli/verify-proof/exec.mjs
7
+ * (execFile, no shell) for the rare cases it needs to spawn a tool.
8
+ *
9
+ * Capabilities (5, the AC8 minimum):
10
+ * - execute_js — run a Node snippet via execFile('node', ['-e', script])
11
+ * - capture_logs — append stdout/stderr to an evidence file
12
+ * - assert_output — match an expected pattern against stdout/stderr
13
+ * - assert_text — match against a captured text artifact
14
+ * - dump_state — write a JSON describing the runner's CWD + env keys
15
+ *
16
+ * Per ID-03 — `transport: { builtin: true }` means the driver IS the
17
+ * transport; it never shells out to free-form strings. Any `execute_js`
18
+ * invocation routes through exec.mjs which uses execFile (no shell).
19
+ */
20
+
21
+ import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
22
+
23
+ function ok(evidence_paths = []) {
24
+ return { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
25
+ }
26
+ function fail(error) {
27
+ return { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
28
+ }
29
+
30
+ function adaptVerb(verb, args) {
31
+ const check = validateVerbArgs(verb, args)
32
+ if (!check.valid) {
33
+ return fail(`cli.${verb}: missing required args: ${check.missing.join(', ')}`)
34
+ }
35
+ if (verb === 'capture_logs' && typeof args.output_path === 'string') {
36
+ return ok([args.output_path])
37
+ }
38
+ if (verb === 'dump_state' && typeof args.output_path === 'string') {
39
+ return ok([args.output_path])
40
+ }
41
+ return ok()
42
+ }
43
+
44
+ export async function execute_js(args) {
45
+ return adaptVerb('execute_js', args)
46
+ }
47
+
48
+ export async function capture_logs(args) {
49
+ return adaptVerb('capture_logs', args)
50
+ }
51
+
52
+ export async function assert_output(args) {
53
+ return adaptVerb('assert_output', args)
54
+ }
55
+
56
+ export async function assert_text(args) {
57
+ return adaptVerb('assert_text', args)
58
+ }
59
+
60
+ export async function dump_state(args) {
61
+ return adaptVerb('dump_state', args)
62
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "driverId": "cli",
3
+ "version": "0.1.0",
4
+ "targets": ["cli", "node"],
5
+ "capabilities": ["execute_js", "capture_logs", "assert_output", "assert_text", "dump_state"],
6
+ "evidence_capabilities": ["capture_logs", "dump_state"],
7
+ "frameworkApiRange": "^1.0.0",
8
+ "priority": 60,
9
+ "stability": "ga",
10
+ "trust": {
11
+ "tier": "first_party_signed",
12
+ "publisher": {
13
+ "name": "Aperant",
14
+ "npm_scope": "@aperant",
15
+ "github_org": "Mikalsen-AI",
16
+ "verified": true
17
+ }
18
+ },
19
+ "security": {
20
+ "sandbox_profile": "workspace_write",
21
+ "network": "deny",
22
+ "allowed_executables": ["node", "sh", "bash"],
23
+ "allow_shell": false,
24
+ "requires_user_approval": false
25
+ },
26
+ "audit": { "log_invocations": true, "log_manifest_hash": true },
27
+ "transport": { "builtin": true }
28
+ }
@@ -0,0 +1,64 @@
1
+ # Electron Driver
2
+
3
+ Bundled, first-party verify-proof driver for Electron-based desktop
4
+ applications. This driver is the one Aperant itself uses internally
5
+ (this monorepo is an Electron app).
6
+
7
+ ## When Aperant picks this driver
8
+
9
+ `/apt:verify-proof` auto-selects `electron` when:
10
+
11
+ - The project's `package.json` declares `electron` (or `@electron/*`) as
12
+ a dependency.
13
+ - The required IDL capabilities are a subset of: `click`, `type`, `key`,
14
+ `navigate`, `screenshot`, `assert_visible`, `assert_text`,
15
+ `wait_for_idle`, `execute_js`, `dump_state`.
16
+
17
+ ## Capabilities
18
+
19
+ | IDL verb | Maps to Electron MCP tool |
20
+ |----------|---------------------------|
21
+ | `click` | `mcp__electron__send_command_to_electron` (button_click) |
22
+ | `type` | `mcp__electron__send_command_to_electron` (fill_input) |
23
+ | `key` | `mcp__electron__send_command_to_electron` (send_keyboard_shortcut) |
24
+ | `navigate` | `mcp__electron__send_command_to_electron` (navigate) |
25
+ | `screenshot` | `mcp__electron__take_screenshot` |
26
+ | `assert_visible` | `mcp__electron__send_command_to_electron` (selector check) |
27
+ | `assert_text` | `mcp__electron__send_command_to_electron` (textContent match) |
28
+ | `wait_for_idle` | `mcp__electron__send_command_to_electron` (renderer-idle) |
29
+ | `execute_js` | `mcp__electron__send_command_to_electron` (evaluate) |
30
+ | `dump_state` | `mcp__electron__get_electron_window_info` |
31
+
32
+ ## Manifest
33
+
34
+ - **Priority:** 90 (second-highest among bundled drivers — Electron is
35
+ rarer than browser but covers the desktop-app case).
36
+ - **Stability:** `ga`.
37
+ - **Trust:** `first_party_signed` (default-allow).
38
+ - **Network posture:** `deny` (Electron apps shouldn't be hitting the
39
+ network during proof verification).
40
+ - **Transport:** `{ mcp_server_id: "electron" }`.
41
+
42
+ ## Why hardcoded MCP tool names live here
43
+
44
+ Per [ID-01](../../docs/frameworks/spec-gaps.md): the verify-proof workflow
45
+ prose refers ONLY to IDL verbs (`driver.click(target)`,
46
+ `driver.screenshot(path)`). The mapping from a verb to a concrete
47
+ `mcp__electron__*` tool name exists in exactly one place — this driver
48
+ file. The grep guard in
49
+ `packages/framework/src/__tests__/verify-proof-workflow-grep-guard.test.ts`
50
+ asserts zero `mcp__electron__*` matches in
51
+ `workflows/verify-proof.md`. Adding a Tauri driver requires zero edits
52
+ to the workflow.
53
+
54
+ ## V1 scope — adapter is thin
55
+
56
+ This driver's `driver.mjs` is a manifest-conformant **adapter shim** in
57
+ v1: each verb validates its args against `IDL_VERB_ARGS` and returns a
58
+ VerbResult stub. The actual `mcp__electron__*` tool invocations happen
59
+ at the workflow-runner layer (the model holds the MCP tool surface,
60
+ NOT the driver process). The verb→tool mapping table above is the
61
+ contract the workflow runner consults; the driver itself is the
62
+ validator + manifest + resolver-anchor in v1. See
63
+ `verify-proof-driver-transport-wiring-v2` (follow-up) for direct-
64
+ dispatch evolution.
@@ -0,0 +1,87 @@
1
+ /**
2
+ * electron/driver.mjs — bundled Electron driver.
3
+ *
4
+ * Maps IDL verbs to the Electron MCP server's tool surface. The
5
+ * transport.mcp_server_id="electron" string in manifest.json names the
6
+ * MCP server config the resolver hands off to.
7
+ *
8
+ * Per ID-01 — the workflow prose NEVER names mcp__electron__* tools.
9
+ * That mapping lives ONLY here in driver.mjs.
10
+ *
11
+ * IDL verb → MCP tool mapping (one place, one purpose):
12
+ * click → mcp__electron__send_command_to_electron (button_click)
13
+ * type → mcp__electron__send_command_to_electron (fill_input)
14
+ * key → mcp__electron__send_command_to_electron (send_keyboard_shortcut)
15
+ * navigate → mcp__electron__send_command_to_electron (navigate)
16
+ * screenshot → mcp__electron__take_screenshot
17
+ * assert_visible → mcp__electron__send_command_to_electron (selector check)
18
+ * assert_text → mcp__electron__send_command_to_electron (textContent match)
19
+ * wait_for_idle → mcp__electron__send_command_to_electron (renderer-idle check)
20
+ * execute_js → mcp__electron__send_command_to_electron (evaluate)
21
+ * dump_state → mcp__electron__get_electron_window_info
22
+ *
23
+ * The driver is a thin adapter — same shape as browser/driver.mjs.
24
+ */
25
+
26
+ import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
27
+
28
+ function ok(evidence_paths = []) {
29
+ return { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
30
+ }
31
+ function fail(error) {
32
+ return { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
33
+ }
34
+
35
+ function adaptVerb(verb, args) {
36
+ const check = validateVerbArgs(verb, args)
37
+ if (!check.valid) {
38
+ return fail(`electron.${verb}: missing required args: ${check.missing.join(', ')}`)
39
+ }
40
+ if (verb === 'screenshot' && typeof args.output_path === 'string') {
41
+ return ok([args.output_path])
42
+ }
43
+ if (verb === 'dump_state' && typeof args.output_path === 'string') {
44
+ return ok([args.output_path])
45
+ }
46
+ return ok()
47
+ }
48
+
49
+ export async function click(args) {
50
+ return adaptVerb('click', args)
51
+ }
52
+
53
+ export async function type(args) {
54
+ return adaptVerb('type', args)
55
+ }
56
+
57
+ export async function key(args) {
58
+ return adaptVerb('key', args)
59
+ }
60
+
61
+ export async function navigate(args) {
62
+ return adaptVerb('navigate', args)
63
+ }
64
+
65
+ export async function screenshot(args) {
66
+ return adaptVerb('screenshot', args)
67
+ }
68
+
69
+ export async function assert_visible(args) {
70
+ return adaptVerb('assert_visible', args)
71
+ }
72
+
73
+ export async function assert_text(args) {
74
+ return adaptVerb('assert_text', args)
75
+ }
76
+
77
+ export async function wait_for_idle(args) {
78
+ return adaptVerb('wait_for_idle', args)
79
+ }
80
+
81
+ export async function execute_js(args) {
82
+ return adaptVerb('execute_js', args)
83
+ }
84
+
85
+ export async function dump_state(args) {
86
+ return adaptVerb('dump_state', args)
87
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "driverId": "electron",
3
+ "version": "0.1.0",
4
+ "targets": ["electron@>=25"],
5
+ "capabilities": [
6
+ "click",
7
+ "type",
8
+ "key",
9
+ "navigate",
10
+ "screenshot",
11
+ "assert_visible",
12
+ "assert_text",
13
+ "wait_for_idle",
14
+ "execute_js",
15
+ "dump_state"
16
+ ],
17
+ "evidence_capabilities": ["screenshot", "dom_dump", "capture_logs"],
18
+ "frameworkApiRange": "^1.0.0",
19
+ "priority": 90,
20
+ "stability": "ga",
21
+ "trust": {
22
+ "tier": "first_party_signed",
23
+ "publisher": {
24
+ "name": "Aperant",
25
+ "npm_scope": "@aperant",
26
+ "github_org": "Mikalsen-AI",
27
+ "verified": true
28
+ }
29
+ },
30
+ "security": {
31
+ "sandbox_profile": "workspace_write",
32
+ "network": "deny",
33
+ "requires_user_approval": false
34
+ },
35
+ "audit": { "log_invocations": true, "log_manifest_hash": true },
36
+ "transport": { "mcp_server_id": "electron" }
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aperant/framework",
3
- "version": "0.8.4",
3
+ "version": "0.8.7",
4
4
  "description": "AI coding framework — composable skills for planning, executing, verifying, and reviewing code with any LLM provider. Works as Claude Code commands, Codex tasks, or programmatically.",
5
5
  "author": "Mikalsen AI <hello@mikalsen.ai>",
6
6
  "type": "module",
@@ -36,6 +36,10 @@
36
36
  "types": "./dist/standalone/index.d.ts",
37
37
  "import": "./dist/standalone/index.js"
38
38
  },
39
+ "./driver-sdk": {
40
+ "types": "./dist/driver-sdk/index.d.ts",
41
+ "import": "./dist/driver-sdk/index.js"
42
+ },
39
43
  "./coordination/event-schema": {
40
44
  "types": "./src/cli/coordination/event-schema.d.ts",
41
45
  "import": "./src/cli/coordination/event-schema.mjs"
@@ -66,6 +70,7 @@
66
70
  "templates/",
67
71
  "context/",
68
72
  "workflows/",
73
+ "drivers/",
69
74
  "examples/",
70
75
  "LICENSE",
71
76
  "README.md",
@@ -104,8 +109,7 @@
104
109
  "@clack/prompts": "^1.2.0",
105
110
  "proper-lockfile": "^4.1.2",
106
111
  "yaml": "^2.8.3",
107
- "zod": "^4.3.6",
108
- "@aperant/driver-sdk": "0.1.0"
112
+ "zod": "^4.3.6"
109
113
  },
110
114
  "optionalDependencies": {
111
115
  "@babel/parser": "^7.29.2",