@aperant/framework 0.8.1 → 0.8.4
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/CHANGELOG.md +287 -1
- package/agents/apt-planner.md +5 -4
- package/dist/cli/commands/adr.mjs +1 -1
- package/dist/cli/commands/audit-branch-current.d.mts +25 -0
- package/dist/cli/commands/audit-branch-current.d.mts.map +1 -0
- package/dist/cli/commands/audit-branch-current.mjs +252 -0
- package/dist/cli/commands/audit-branch-current.mjs.map +1 -0
- package/dist/cli/commands/audit.d.mts.map +1 -1
- package/dist/cli/commands/audit.mjs +13 -2
- package/dist/cli/commands/audit.mjs.map +1 -1
- package/dist/cli/commands/check-version.d.mts.map +1 -1
- package/dist/cli/commands/check-version.mjs +19 -1
- package/dist/cli/commands/check-version.mjs.map +1 -1
- package/dist/cli/commands/context.mjs +3 -3
- package/dist/cli/commands/context.mjs.map +1 -1
- package/dist/cli/commands/detect-runtime.d.mts +6 -0
- package/dist/cli/commands/detect-runtime.d.mts.map +1 -0
- package/dist/cli/commands/detect-runtime.mjs +136 -0
- package/dist/cli/commands/detect-runtime.mjs.map +1 -0
- package/dist/cli/commands/driver-doctor.d.mts +29 -0
- package/dist/cli/commands/driver-doctor.d.mts.map +1 -0
- package/dist/cli/commands/driver-doctor.mjs +291 -0
- package/dist/cli/commands/driver-doctor.mjs.map +1 -0
- package/dist/cli/commands/gate.mjs +14 -0
- package/dist/cli/commands/gate.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +9 -6
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/install-from-source.d.mts +2 -0
- package/dist/cli/commands/install-from-source.d.mts.map +1 -0
- package/dist/cli/commands/install-from-source.mjs +2 -0
- package/dist/cli/commands/install-from-source.mjs.map +1 -0
- package/dist/cli/commands/modes.mjs +1 -1
- package/dist/cli/commands/route.d.mts.map +1 -1
- package/dist/cli/commands/route.mjs +38 -1
- package/dist/cli/commands/route.mjs.map +1 -1
- package/dist/cli/commands/triage.mjs +1 -1
- package/dist/cli/config/load.d.mts.map +1 -1
- package/dist/cli/config/load.mjs +9 -3
- package/dist/cli/config/load.mjs.map +1 -1
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
- package/dist/cli/coordination/event-schema.d.mts.map +1 -1
- package/dist/cli/coordination/event-schema.mjs +18 -0
- package/dist/cli/coordination/event-schema.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +18 -1
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/gate/context.d.mts +1 -0
- package/dist/cli/gate/context.d.mts.map +1 -1
- package/dist/cli/gate/context.mjs +14 -1
- package/dist/cli/gate/context.mjs.map +1 -1
- package/dist/cli/gate/gates/consistency-check.d.mts +1 -0
- package/dist/cli/gate/gates/consistency-check.d.mts.map +1 -1
- package/dist/cli/gate/gates/consistency-check.mjs +1 -0
- package/dist/cli/gate/gates/consistency-check.mjs.map +1 -1
- package/dist/cli/gate/gates/review-clean.d.mts +1 -0
- package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
- package/dist/cli/gate/gates/review-clean.mjs +1 -0
- package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
- package/dist/cli/gate/gates/verify-approved.d.mts +1 -0
- package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
- package/dist/cli/gate/gates/verify-approved.mjs +1 -0
- package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
- package/dist/cli/gate/registry.d.mts.map +1 -1
- package/dist/cli/gate/registry.mjs +9 -1
- package/dist/cli/gate/registry.mjs.map +1 -1
- package/dist/cli/install/find-workspace-framework-version.d.mts +16 -0
- package/dist/cli/install/find-workspace-framework-version.d.mts.map +1 -0
- package/dist/cli/install/find-workspace-framework-version.mjs +85 -0
- package/dist/cli/install/find-workspace-framework-version.mjs.map +1 -0
- package/dist/cli/install/install-from-source.d.mts +10 -0
- package/dist/cli/install/install-from-source.d.mts.map +1 -0
- package/dist/cli/install/install-from-source.mjs +271 -0
- package/dist/cli/install/install-from-source.mjs.map +1 -0
- package/dist/cli/install/install-kernel.d.mts.map +1 -1
- package/dist/cli/install/install-kernel.mjs +24 -0
- package/dist/cli/install/install-kernel.mjs.map +1 -1
- package/dist/cli/install/legacy-paths.d.mts +33 -11
- package/dist/cli/install/legacy-paths.d.mts.map +1 -1
- package/dist/cli/install/legacy-paths.mjs +161 -39
- package/dist/cli/install/legacy-paths.mjs.map +1 -1
- package/dist/cli/install/runtime-detect.mjs +1 -1
- package/dist/cli/install/runtime-detect.mjs.map +1 -1
- package/dist/cli/install/runtime-migrate.d.mts +34 -14
- package/dist/cli/install/runtime-migrate.d.mts.map +1 -1
- package/dist/cli/install/runtime-migrate.mjs +63 -30
- package/dist/cli/install/runtime-migrate.mjs.map +1 -1
- package/dist/cli/install/transforms/codex.d.mts.map +1 -1
- package/dist/cli/install/transforms/codex.mjs +28 -17
- package/dist/cli/install/transforms/codex.mjs.map +1 -1
- package/dist/cli/skill-author/contract.mjs +2 -2
- package/dist/cli/skill-author/contract.mjs.map +1 -1
- package/dist/cli/verify-proof/audit.d.mts +34 -0
- package/dist/cli/verify-proof/audit.d.mts.map +1 -0
- package/dist/cli/verify-proof/audit.mjs +53 -0
- package/dist/cli/verify-proof/audit.mjs.map +1 -0
- package/dist/cli/verify-proof/exec.d.mts +20 -0
- package/dist/cli/verify-proof/exec.d.mts.map +1 -0
- package/dist/cli/verify-proof/exec.mjs +74 -0
- package/dist/cli/verify-proof/exec.mjs.map +1 -0
- package/dist/cli/verify-proof/idl/index.d.mts +2 -0
- package/dist/cli/verify-proof/idl/index.d.mts.map +1 -0
- package/dist/cli/verify-proof/idl/index.mjs +14 -0
- package/dist/cli/verify-proof/idl/index.mjs.map +1 -0
- package/dist/cli/verify-proof/idl/types.d.ts +9 -0
- package/dist/cli/verify-proof/idl/types.d.ts.map +1 -0
- package/dist/cli/verify-proof/idl/types.js +9 -0
- package/dist/cli/verify-proof/idl/types.js.map +1 -0
- package/dist/cli/verify-proof/manifest-validator.d.mts +19 -0
- package/dist/cli/verify-proof/manifest-validator.d.mts.map +1 -0
- package/dist/cli/verify-proof/manifest-validator.mjs +182 -0
- package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -0
- package/dist/cli/verify-proof/resolver.d.mts +43 -0
- package/dist/cli/verify-proof/resolver.d.mts.map +1 -0
- package/dist/cli/verify-proof/resolver.mjs +160 -0
- package/dist/cli/verify-proof/resolver.mjs.map +1 -0
- package/dist/cli/verify-proof/runtime-detect.d.mts +27 -0
- package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -0
- package/dist/cli/verify-proof/runtime-detect.mjs +116 -0
- package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -0
- package/dist/cli/verify-proof/trust.d.mts +70 -0
- package/dist/cli/verify-proof/trust.d.mts.map +1 -0
- package/dist/cli/verify-proof/trust.mjs +174 -0
- package/dist/cli/verify-proof/trust.mjs.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +3 -1
- package/dist/plugin/agents/apt-planner.md +5 -4
- package/dist/plugin/skills/apt/SKILL.md +75 -6
- package/dist/plugin/skills/apt-caveman/SKILL.md +64 -0
- package/dist/plugin/skills/apt-debug/SKILL.md +36 -4
- package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
- package/dist/plugin/skills/apt-discuss/SKILL.md +4 -4
- package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +2 -11
- package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
- package/dist/plugin/skills/apt-execute/SKILL.md +27 -4
- package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +2 -10
- package/dist/plugin/skills/apt-fan-out/SKILL.md +36 -1
- package/dist/plugin/skills/apt-handoff/SKILL.md +228 -0
- package/dist/plugin/skills/apt-improve/DEEPENING.md +0 -8
- package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
- package/dist/plugin/skills/apt-improve/LANGUAGE.md +0 -8
- package/dist/plugin/skills/apt-improve/SKILL.md +4 -4
- package/dist/plugin/skills/apt-plan/SKILL.md +23 -5
- package/dist/plugin/skills/apt-plan/adapters/conductor.md +1 -1
- package/dist/plugin/skills/apt-prototype/LOGIC.md +0 -8
- package/dist/plugin/skills/apt-prototype/SKILL.md +4 -4
- package/dist/plugin/skills/apt-prototype/UI.md +0 -8
- package/dist/plugin/skills/apt-quick/SKILL.md +31 -1
- package/dist/plugin/skills/apt-resume/SKILL.md +54 -31
- package/dist/plugin/skills/apt-run/SKILL.md +114 -16
- package/dist/plugin/skills/apt-setup/SKILL.md +97 -8
- package/dist/plugin/skills/apt-ship/SKILL.md +165 -9
- package/dist/plugin/skills/apt-spar/SKILL.md +65 -16
- package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +0 -8
- package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
- package/dist/plugin/skills/apt-triage/SKILL.md +2 -2
- package/dist/plugin/skills/apt-update/SKILL.md +74 -17
- package/dist/plugin/skills/apt-verify-proof/SKILL.md +48 -1
- package/dist/plugin/skills/apt-zoom-out/SKILL.md +3 -3
- package/dist/schemas/quick-task.d.ts.map +1 -1
- package/dist/schemas/quick-task.js +2 -2
- package/dist/schemas/quick-task.js.map +1 -1
- package/dist/types/config.d.ts +33 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/state.d.ts +1 -1
- package/dist/types/state.d.ts.map +1 -1
- package/dist/types/task-record.d.ts +1 -1
- package/dist/types/task-record.d.ts.map +1 -1
- package/package.json +134 -133
- package/prompts/planner.md +1 -1
- package/prompts/spec_writer.md +1 -1
- package/skills/apt/SKILL.md +75 -6
- package/skills/apt-caveman/SKILL.md +64 -0
- package/skills/apt-debug/SKILL.md +36 -4
- package/skills/apt-debug/appendices/diagnose-discipline.md +2 -11
- package/skills/apt-discuss/SKILL.md +4 -4
- package/skills/apt-discuss/appendices/grill-discipline.md +2 -11
- package/skills/apt-discuss/appendices/zoom-out-helper.md +0 -8
- package/skills/apt-execute/SKILL.md +27 -4
- package/skills/apt-execute/appendices/tdd-mode.md +2 -10
- package/skills/apt-fan-out/SKILL.md +36 -1
- package/skills/apt-handoff/SKILL.md +228 -0
- package/skills/apt-improve/DEEPENING.md +0 -8
- package/skills/apt-improve/INTERFACE-DESIGN.md +0 -8
- package/skills/apt-improve/LANGUAGE.md +0 -8
- package/skills/apt-improve/SKILL.md +4 -4
- package/skills/apt-plan/SKILL.md +23 -5
- package/skills/apt-plan/adapters/conductor.md +1 -1
- package/skills/apt-planner.md +1 -1
- package/skills/apt-prototype/LOGIC.md +0 -8
- package/skills/apt-prototype/SKILL.md +4 -4
- package/skills/apt-prototype/UI.md +0 -8
- package/skills/apt-quick/SKILL.md +31 -1
- package/skills/apt-resume/SKILL.md +54 -31
- package/skills/apt-run/SKILL.md +114 -16
- package/skills/apt-setup/SKILL.md +97 -8
- package/skills/apt-ship/SKILL.md +165 -9
- package/skills/apt-spar/SKILL.md +65 -16
- package/skills/apt-triage/AGENT-BRIEF.md +0 -8
- package/skills/apt-triage/OUT-OF-SCOPE.md +0 -7
- package/skills/apt-triage/SKILL.md +2 -2
- package/skills/apt-update/SKILL.md +74 -17
- package/skills/apt-verify-proof/SKILL.md +48 -1
- package/skills/apt-zoom-out/SKILL.md +3 -3
- package/src/cli/commands/adr.mjs +1 -1
- package/src/cli/commands/audit-branch-current.mjs +250 -0
- package/src/cli/commands/audit.mjs +13 -2
- package/src/cli/commands/check-version.mjs +20 -1
- package/src/cli/commands/context.mjs +3 -3
- package/src/cli/commands/detect-runtime.mjs +134 -0
- package/src/cli/commands/driver-doctor.mjs +294 -0
- package/src/cli/commands/gate.mjs +15 -0
- package/src/cli/commands/init.mjs +9 -6
- package/src/cli/commands/install-from-source.mjs +1 -0
- package/src/cli/commands/modes.mjs +1 -1
- package/src/cli/commands/route.mjs +42 -1
- package/src/cli/commands/triage.mjs +1 -1
- package/src/cli/config/load.mjs +9 -3
- package/src/cli/coordination/auto-emit-artifact-ready.mjs +1 -1
- package/src/cli/coordination/event-schema.mjs +19 -0
- package/src/cli/dispatch.mjs +17 -1
- package/src/cli/gate/context.mjs +20 -1
- package/src/cli/gate/gates/consistency-check.mjs +1 -0
- package/src/cli/gate/gates/review-clean.mjs +1 -0
- package/src/cli/gate/gates/verify-approved.mjs +1 -0
- package/src/cli/gate/registry.mjs +11 -1
- package/src/cli/install/find-workspace-framework-version.mjs +88 -0
- package/src/cli/install/install-from-source.mjs +276 -0
- package/src/cli/install/install-kernel.mjs +24 -0
- package/src/cli/install/legacy-paths.mjs +162 -39
- package/src/cli/install/runtime-detect.mjs +1 -1
- package/src/cli/install/runtime-migrate.mjs +63 -30
- package/src/cli/install/transforms/codex.mjs +26 -17
- package/src/cli/skill-author/contract.mjs +2 -2
- package/src/cli/verify-proof/.gitkeep +0 -0
- package/src/cli/verify-proof/audit.mjs +55 -0
- package/src/cli/verify-proof/exec.mjs +74 -0
- package/src/cli/verify-proof/idl/index.mjs +24 -0
- package/src/cli/verify-proof/idl/types.ts +41 -0
- package/src/cli/verify-proof/manifest-schema.json +211 -0
- package/src/cli/verify-proof/manifest-validator.mjs +184 -0
- package/src/cli/verify-proof/resolver.mjs +163 -0
- package/src/cli/verify-proof/runtime-detect.mjs +122 -0
- package/src/cli/verify-proof/trust.mjs +187 -0
- package/templates/adr-format.md +0 -8
- package/templates/aperant-claude-md-appendix.md +1 -1
- package/templates/context-format.md +1 -9
- package/templates/proof-verification.md +19 -5
- package/workflows/verify-proof.md +56 -14
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/detect-runtime.mjs — `apt-tools detect-runtime <projectDir>`.
|
|
3
|
+
*
|
|
4
|
+
* Exposes runtime-detect.mjs at the CLI surface so adopters can preview
|
|
5
|
+
* which driver Aperant will pick for /apt:verify-proof.
|
|
6
|
+
*
|
|
7
|
+
* Returns:
|
|
8
|
+
* { status: 'ok', command: 'detect-runtime', projectDir,
|
|
9
|
+
* runtime: { is_electron, is_tauri, is_nextjs, is_web_frontend, ... },
|
|
10
|
+
* selected_driver_id: string | null,
|
|
11
|
+
* fallback_attempts: [{ driver_id, reason }] }
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
15
|
+
import { join, resolve } from 'node:path'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
import { err, ok } from '../util/result.mjs'
|
|
18
|
+
import { resolveDriver } from '../verify-proof/resolver.mjs'
|
|
19
|
+
import { loadRuntimeCapabilities } from '../verify-proof/runtime-detect.mjs'
|
|
20
|
+
|
|
21
|
+
/** Default required capabilities — the smallest set that proves a driver is usable. */
|
|
22
|
+
const DEFAULT_REQUIRED_CAPABILITIES = ['screenshot']
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Discover every driver manifest bundled with @aperant/framework.
|
|
26
|
+
* @param {string} frameworkRoot — absolute path to packages/framework or .aperant/deps/.../@aperant/framework
|
|
27
|
+
* @returns {Array<object>}
|
|
28
|
+
*/
|
|
29
|
+
function discoverBundledDrivers(frameworkRoot) {
|
|
30
|
+
const dir = join(frameworkRoot, 'drivers')
|
|
31
|
+
if (!existsSync(dir)) return []
|
|
32
|
+
const manifests = []
|
|
33
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
34
|
+
if (!entry.isDirectory()) continue
|
|
35
|
+
const manifestPath = join(dir, entry.name, 'manifest.json')
|
|
36
|
+
if (!existsSync(manifestPath)) continue
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'))
|
|
39
|
+
manifests.push({ ...parsed, locality: 'bundled' })
|
|
40
|
+
} catch {
|
|
41
|
+
/* skip malformed manifests */
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return manifests
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Discover every user-installed driver manifest at .aperant/drivers/.
|
|
49
|
+
* @param {string} projectDir
|
|
50
|
+
* @returns {Array<object>}
|
|
51
|
+
*/
|
|
52
|
+
function discoverUserDrivers(projectDir) {
|
|
53
|
+
const dir = join(projectDir, '.aperant/drivers')
|
|
54
|
+
if (!existsSync(dir)) return []
|
|
55
|
+
const manifests = []
|
|
56
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
57
|
+
if (!entry.isDirectory()) continue
|
|
58
|
+
const manifestPath = join(dir, entry.name, 'manifest.json')
|
|
59
|
+
if (!existsSync(manifestPath)) continue
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'))
|
|
62
|
+
manifests.push({ ...parsed, locality: 'user' })
|
|
63
|
+
} catch {
|
|
64
|
+
/* skip malformed manifests */
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return manifests
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} projectDir
|
|
72
|
+
* @param {string[]} [_extra]
|
|
73
|
+
*/
|
|
74
|
+
export function cmdDetectRuntime(projectDir, _extra = []) {
|
|
75
|
+
if (!projectDir) return err('Usage: apt-tools detect-runtime <project-dir>')
|
|
76
|
+
const target = resolve(projectDir)
|
|
77
|
+
|
|
78
|
+
// Detect runtime capabilities from project's package.json.
|
|
79
|
+
const runtime = loadRuntimeCapabilities(target)
|
|
80
|
+
|
|
81
|
+
// Discover bundled + user drivers.
|
|
82
|
+
// __dirname-equivalent under ESM: walk up from this file to the framework
|
|
83
|
+
// package root. fileURLToPath is portable across POSIX + Windows;
|
|
84
|
+
// new URL(...).pathname returns '/C:/...' on Windows which resolve()
|
|
85
|
+
// does not handle correctly.
|
|
86
|
+
const frameworkRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
|
87
|
+
const bundled = discoverBundledDrivers(frameworkRoot)
|
|
88
|
+
const user = discoverUserDrivers(target)
|
|
89
|
+
const drivers = [...bundled, ...user]
|
|
90
|
+
|
|
91
|
+
// Read user override from .aperant/config.json:verification.runtimes[].
|
|
92
|
+
let override = []
|
|
93
|
+
const configPath = join(target, '.aperant/config.json')
|
|
94
|
+
if (existsSync(configPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const cfg = JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
97
|
+
if (Array.isArray(cfg?.verification?.runtimes)) {
|
|
98
|
+
override = cfg.verification.runtimes.filter((x) => typeof x === 'string')
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
/* malformed config — ignore */
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Try resolving with the default minimum required capability.
|
|
106
|
+
let selected = null
|
|
107
|
+
let fallback_attempts = []
|
|
108
|
+
try {
|
|
109
|
+
const result = resolveDriver({
|
|
110
|
+
drivers,
|
|
111
|
+
runtime,
|
|
112
|
+
required_capabilities: DEFAULT_REQUIRED_CAPABILITIES,
|
|
113
|
+
override,
|
|
114
|
+
})
|
|
115
|
+
selected = result.selected.driverId
|
|
116
|
+
fallback_attempts = result.fallback_attempts
|
|
117
|
+
} catch (resolveErr) {
|
|
118
|
+
fallback_attempts = resolveErr && Array.isArray(resolveErr.attempts) ? resolveErr.attempts : []
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return ok({
|
|
122
|
+
status: 'ok',
|
|
123
|
+
command: 'detect-runtime',
|
|
124
|
+
projectDir: target,
|
|
125
|
+
runtime,
|
|
126
|
+
discovered_drivers: drivers.map((d) => ({
|
|
127
|
+
driverId: d.driverId,
|
|
128
|
+
version: d.version,
|
|
129
|
+
locality: d.locality,
|
|
130
|
+
})),
|
|
131
|
+
selected_driver_id: selected,
|
|
132
|
+
fallback_attempts,
|
|
133
|
+
})
|
|
134
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/driver-doctor.mjs — `apt-tools driver doctor <projectDir>`.
|
|
3
|
+
*
|
|
4
|
+
* Validates every discovered driver against:
|
|
5
|
+
* 1. The manifest JSON-Schema (manifest-validator.mjs).
|
|
6
|
+
* 2. IDL conformance — every declared capability has a function export.
|
|
7
|
+
* NOTE: this step necessarily imports driver.mjs, which RUNS the
|
|
8
|
+
* driver's top-level module code. For that reason we trust-gate the
|
|
9
|
+
* import: bundled drivers (first-party signed, sitting under
|
|
10
|
+
* packages/framework/drivers/) are always imported; user drivers
|
|
11
|
+
* under .aperant/drivers/ are imported only when policy + TOFU say
|
|
12
|
+
* it is safe. A user driver that fails the gate is reported with
|
|
13
|
+
* `idl_conformant: false` and `idl_skipped_reason: <decision>`
|
|
14
|
+
* rather than executed.
|
|
15
|
+
* 3. Preflight commands (declared as structured local_command per
|
|
16
|
+
* ID-03). For each preflight whose declared transport is the
|
|
17
|
+
* structured `{ binary, args[] }` shape, we run it via execFile
|
|
18
|
+
* (exec.mjs — no shell) and record pass/fail + missing_deps.
|
|
19
|
+
* Preflights without a structured `command` (declaration-only) are
|
|
20
|
+
* marked `pending` — there is nothing to execute.
|
|
21
|
+
*
|
|
22
|
+
* Emits a JSON envelope listing pass/fail per driver. Third-party driver
|
|
23
|
+
* authors invoke this before publishing.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
27
|
+
import { join, resolve } from 'node:path'
|
|
28
|
+
import { fileURLToPath } from 'node:url'
|
|
29
|
+
import { err, ok } from '../util/result.mjs'
|
|
30
|
+
import { execLocalCommand } from '../verify-proof/exec.mjs'
|
|
31
|
+
import { IDL_VERB_NAMES } from '../verify-proof/idl/index.mjs'
|
|
32
|
+
import { validateManifest } from '../verify-proof/manifest-validator.mjs'
|
|
33
|
+
import { decide, readPolicy, readTofuLock } from '../verify-proof/trust.mjs'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {Object} DriverDoctorReport
|
|
37
|
+
* @property {string} driverId
|
|
38
|
+
* @property {string} version
|
|
39
|
+
* @property {string} locality
|
|
40
|
+
* @property {boolean} manifest_valid
|
|
41
|
+
* @property {Array<{path: string, message: string}>} manifest_errors
|
|
42
|
+
* @property {boolean} idl_conformant
|
|
43
|
+
* @property {string[]} missing_exports
|
|
44
|
+
* @property {string[]} unknown_capabilities
|
|
45
|
+
* @property {string} [idl_skipped_reason] — present when conformance was skipped because the trust gate blocked driver.mjs import
|
|
46
|
+
* @property {'pending' | 'pass' | 'fail' | 'skipped'} preflight_status
|
|
47
|
+
* @property {string[]} missing_deps
|
|
48
|
+
* @property {string} [publisher_warning] — surfaced when a non-first-party manifest claims first_party_signed (v1 has no signature verification)
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Scan a drivers directory for manifest.json files. Returns
|
|
53
|
+
* (manifest, dir, locality) tuples.
|
|
54
|
+
* @param {string} rootDir
|
|
55
|
+
* @param {'bundled' | 'user'} locality
|
|
56
|
+
*/
|
|
57
|
+
function scanDriversDir(rootDir, locality) {
|
|
58
|
+
const out = []
|
|
59
|
+
if (!existsSync(rootDir)) return out
|
|
60
|
+
for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
|
|
61
|
+
if (!entry.isDirectory()) continue
|
|
62
|
+
const driverDir = join(rootDir, entry.name)
|
|
63
|
+
const manifestPath = join(driverDir, 'manifest.json')
|
|
64
|
+
if (!existsSync(manifestPath)) continue
|
|
65
|
+
let manifest
|
|
66
|
+
try {
|
|
67
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))
|
|
68
|
+
} catch (e) {
|
|
69
|
+
out.push({ malformed: true, dir: driverDir, locality, parse_error: String(e) })
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
out.push({ manifest, dir: driverDir, locality })
|
|
73
|
+
}
|
|
74
|
+
return out
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {object} manifest
|
|
79
|
+
* @param {string} driverDir
|
|
80
|
+
* @returns {Promise<{idl_conformant: boolean, missing_exports: string[], unknown_capabilities: string[]}>}
|
|
81
|
+
*/
|
|
82
|
+
async function checkIdlConformance(manifest, driverDir) {
|
|
83
|
+
const driverPath = join(driverDir, 'driver.mjs')
|
|
84
|
+
if (!existsSync(driverPath)) {
|
|
85
|
+
return {
|
|
86
|
+
idl_conformant: false,
|
|
87
|
+
missing_exports: ['driver.mjs not found'],
|
|
88
|
+
unknown_capabilities: [],
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const missing_exports = []
|
|
92
|
+
const unknown_capabilities = []
|
|
93
|
+
const idlSet = new Set(IDL_VERB_NAMES)
|
|
94
|
+
try {
|
|
95
|
+
const mod = await import(driverPath)
|
|
96
|
+
for (const cap of manifest.capabilities ?? []) {
|
|
97
|
+
if (!idlSet.has(cap)) {
|
|
98
|
+
unknown_capabilities.push(cap)
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
if (typeof mod[cap] !== 'function') {
|
|
102
|
+
missing_exports.push(cap)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return {
|
|
107
|
+
idl_conformant: false,
|
|
108
|
+
missing_exports: [`driver.mjs import failed: ${String(e)}`],
|
|
109
|
+
unknown_capabilities: [],
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
idl_conformant: missing_exports.length === 0 && unknown_capabilities.length === 0,
|
|
114
|
+
missing_exports,
|
|
115
|
+
unknown_capabilities,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Run a driver's preflight checks. Each declared preflight with a
|
|
121
|
+
* structured `command: { binary, args[] }` is executed via execFile
|
|
122
|
+
* (no shell). Preflights without a structured command are reported as
|
|
123
|
+
* `pending` — there is nothing to execute. Empty preflight list is
|
|
124
|
+
* `skipped`.
|
|
125
|
+
*
|
|
126
|
+
* @param {object} manifest
|
|
127
|
+
* @returns {Promise<{preflight_status: 'pending'|'pass'|'fail'|'skipped', missing_deps: string[]}>}
|
|
128
|
+
*/
|
|
129
|
+
async function runPreflight(manifest) {
|
|
130
|
+
const preflight = Array.isArray(manifest.preflight) ? manifest.preflight : []
|
|
131
|
+
if (preflight.length === 0) return { preflight_status: 'skipped', missing_deps: [] }
|
|
132
|
+
|
|
133
|
+
const missing_deps = []
|
|
134
|
+
let executed = 0
|
|
135
|
+
let failed = false
|
|
136
|
+
|
|
137
|
+
for (const check of preflight) {
|
|
138
|
+
// Only execute when the preflight declares a structured local-command
|
|
139
|
+
// shape. Anything else (no command, or non-structured) is left as
|
|
140
|
+
// declaration-only — caller can drive it explicitly.
|
|
141
|
+
const cmd = check?.command
|
|
142
|
+
if (
|
|
143
|
+
cmd === undefined ||
|
|
144
|
+
cmd === null ||
|
|
145
|
+
typeof cmd !== 'object' ||
|
|
146
|
+
typeof cmd.binary !== 'string' ||
|
|
147
|
+
!Array.isArray(cmd.args) ||
|
|
148
|
+
cmd.args.some((a) => typeof a !== 'string')
|
|
149
|
+
) {
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
executed++
|
|
153
|
+
try {
|
|
154
|
+
await execLocalCommand({ transport: { local_command: cmd } }, [], { timeout: 5000 })
|
|
155
|
+
} catch {
|
|
156
|
+
failed = true
|
|
157
|
+
if (check.required !== false) {
|
|
158
|
+
missing_deps.push(check.name ?? cmd.binary)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (executed === 0) return { preflight_status: 'pending', missing_deps: [] }
|
|
164
|
+
return { preflight_status: failed ? 'fail' : 'pass', missing_deps }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Trust-gate the driver.mjs import. Bundled drivers (locality='bundled')
|
|
169
|
+
* always pass — they live in packages/framework/drivers/ and are first-
|
|
170
|
+
* party. User drivers go through the policy + TOFU decision; only
|
|
171
|
+
* `allow` permits the dynamic import.
|
|
172
|
+
*
|
|
173
|
+
* @param {object} manifest
|
|
174
|
+
* @param {'bundled'|'user'} locality
|
|
175
|
+
* @param {string} projectDir
|
|
176
|
+
* @returns {{ok: true} | {ok: false, reason: string}}
|
|
177
|
+
*/
|
|
178
|
+
function trustGate(manifest, locality, projectDir) {
|
|
179
|
+
if (locality === 'bundled') return { ok: true }
|
|
180
|
+
const policy = readPolicy(projectDir)
|
|
181
|
+
const tofu = readTofuLock(projectDir)
|
|
182
|
+
const decision = decide(manifest, tofu, policy)
|
|
183
|
+
if (decision.decision === 'allow') return { ok: true }
|
|
184
|
+
return { ok: false, reason: decision.reason }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* v1 has no signature verification (spec §Out of Scope). A non-Aperant
|
|
189
|
+
* publisher claiming `first_party_signed` is suspicious — surface a
|
|
190
|
+
* warning so the operator notices.
|
|
191
|
+
*
|
|
192
|
+
* @param {object} manifest
|
|
193
|
+
* @returns {string | undefined}
|
|
194
|
+
*/
|
|
195
|
+
function publisherWarning(manifest) {
|
|
196
|
+
const tier = manifest?.trust?.tier
|
|
197
|
+
if (tier !== 'first_party_signed') return undefined
|
|
198
|
+
const npmScope = manifest?.trust?.publisher?.npm_scope
|
|
199
|
+
const githubOrg = manifest?.trust?.publisher?.github_org
|
|
200
|
+
const looksLikeAperant = npmScope === '@aperant' || githubOrg === 'Mikalsen-AI'
|
|
201
|
+
if (looksLikeAperant) return undefined
|
|
202
|
+
return `trust.tier=first_party_signed but publisher is not @aperant — v1 does not verify signatures; treat tier as advisory`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @param {string} projectDir
|
|
207
|
+
* @param {string[]} [_extra]
|
|
208
|
+
*/
|
|
209
|
+
export async function cmdDriverDoctor(projectDir, _extra = []) {
|
|
210
|
+
if (!projectDir) return err('Usage: apt-tools driver doctor <project-dir>')
|
|
211
|
+
const target = resolve(projectDir)
|
|
212
|
+
|
|
213
|
+
// fileURLToPath is portable across POSIX + Windows; new URL(...).pathname
|
|
214
|
+
// returns '/C:/...' on Windows which resolve() does not handle correctly.
|
|
215
|
+
const frameworkRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
|
216
|
+
const bundled = scanDriversDir(join(frameworkRoot, 'drivers'), 'bundled')
|
|
217
|
+
const user = scanDriversDir(join(target, '.aperant/drivers'), 'user')
|
|
218
|
+
const discovered = [...bundled, ...user]
|
|
219
|
+
|
|
220
|
+
const drivers = []
|
|
221
|
+
for (const d of discovered) {
|
|
222
|
+
if (d.malformed) {
|
|
223
|
+
drivers.push({
|
|
224
|
+
driverId: '(malformed)',
|
|
225
|
+
version: '?',
|
|
226
|
+
locality: d.locality,
|
|
227
|
+
manifest_valid: false,
|
|
228
|
+
manifest_errors: [{ path: '/', message: d.parse_error }],
|
|
229
|
+
idl_conformant: false,
|
|
230
|
+
missing_exports: [],
|
|
231
|
+
unknown_capabilities: [],
|
|
232
|
+
preflight_status: 'skipped',
|
|
233
|
+
missing_deps: [],
|
|
234
|
+
})
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const manifest = d.manifest
|
|
239
|
+
const schemaResult = validateManifest(manifest)
|
|
240
|
+
const manifest_valid = schemaResult.valid === true
|
|
241
|
+
const manifest_errors = schemaResult.valid === false ? schemaResult.errors : []
|
|
242
|
+
|
|
243
|
+
let idlResult = { idl_conformant: false, missing_exports: [], unknown_capabilities: [] }
|
|
244
|
+
let idl_skipped_reason
|
|
245
|
+
if (manifest_valid) {
|
|
246
|
+
const gate = trustGate(manifest, d.locality, target)
|
|
247
|
+
if (gate.ok) {
|
|
248
|
+
idlResult = await checkIdlConformance(manifest, d.dir)
|
|
249
|
+
} else {
|
|
250
|
+
// Trust gate denied. Do NOT dynamic-import the driver — that
|
|
251
|
+
// would execute arbitrary third-party code under the audit
|
|
252
|
+
// command. Report idl_conformant=false with the gate reason
|
|
253
|
+
// so the operator sees exactly why conformance was not run.
|
|
254
|
+
idl_skipped_reason = `trust gate denied: ${gate.reason}`
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let preflightResult = { preflight_status: 'skipped', missing_deps: [] }
|
|
259
|
+
if (manifest_valid) {
|
|
260
|
+
preflightResult = await runPreflight(manifest)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const entry = {
|
|
264
|
+
driverId: manifest.driverId ?? '(unknown)',
|
|
265
|
+
version: manifest.version ?? '?',
|
|
266
|
+
locality: d.locality,
|
|
267
|
+
manifest_valid,
|
|
268
|
+
manifest_errors,
|
|
269
|
+
idl_conformant: idlResult.idl_conformant,
|
|
270
|
+
missing_exports: idlResult.missing_exports,
|
|
271
|
+
unknown_capabilities: idlResult.unknown_capabilities,
|
|
272
|
+
preflight_status: preflightResult.preflight_status,
|
|
273
|
+
missing_deps: preflightResult.missing_deps,
|
|
274
|
+
}
|
|
275
|
+
if (idl_skipped_reason !== undefined) entry.idl_skipped_reason = idl_skipped_reason
|
|
276
|
+
const warning = publisherWarning(manifest)
|
|
277
|
+
if (warning !== undefined) entry.publisher_warning = warning
|
|
278
|
+
drivers.push(entry)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const all_pass = drivers.every((d) => d.manifest_valid && d.idl_conformant)
|
|
282
|
+
return ok({
|
|
283
|
+
status: 'ok',
|
|
284
|
+
command: 'driver-doctor',
|
|
285
|
+
projectDir: target,
|
|
286
|
+
drivers,
|
|
287
|
+
summary: {
|
|
288
|
+
total: drivers.length,
|
|
289
|
+
pass: drivers.filter((d) => d.manifest_valid && d.idl_conformant).length,
|
|
290
|
+
fail: drivers.filter((d) => !d.manifest_valid || !d.idl_conformant).length,
|
|
291
|
+
overall: all_pass ? 'pass' : 'fail',
|
|
292
|
+
},
|
|
293
|
+
})
|
|
294
|
+
}
|
|
@@ -135,6 +135,20 @@ async function cmdGateCheck(projectDir, extraArgs) {
|
|
|
135
135
|
const cache = cacheMod ? cacheMod.readCache(ctx.taskDir) : {}
|
|
136
136
|
|
|
137
137
|
for (const gate of gates) {
|
|
138
|
+
// Applicability check fires BEFORE cache lookup or check() execution.
|
|
139
|
+
// Gates that don't apply to the active profile (lite vs full-pipeline)
|
|
140
|
+
// short-circuit with a skip verdict. Skip results are intentionally
|
|
141
|
+
// NOT cached (see status: 'skip' branch below), so a track change on
|
|
142
|
+
// the same task immediately reflects in the next gate-check.
|
|
143
|
+
if (typeof gate.applies_to_profile === 'function' && gate.applies_to_profile(ctx) === false) {
|
|
144
|
+
skipped.push({
|
|
145
|
+
id: gate.id,
|
|
146
|
+
severity: gate.severity,
|
|
147
|
+
reason: `profile-not-applicable: ${ctx.profile}`,
|
|
148
|
+
})
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
|
|
138
152
|
let verdict
|
|
139
153
|
let cached = false
|
|
140
154
|
let inputHash = null
|
|
@@ -244,6 +258,7 @@ async function cmdGateCheck(projectDir, extraArgs) {
|
|
|
244
258
|
command: 'gate-check',
|
|
245
259
|
skill,
|
|
246
260
|
task_id: taskId,
|
|
261
|
+
profile: ctx.profile,
|
|
247
262
|
passed: passed.map((p) => p.id),
|
|
248
263
|
warned: warned.map((w) => ({ id: w.id, reason: w.reason })),
|
|
249
264
|
skipped: skipped.map((s) => ({ id: s.id, reason: s.reason })),
|
|
@@ -1250,12 +1250,15 @@ export async function cmdInit(projectDir, extraArgs = []) {
|
|
|
1250
1250
|
}
|
|
1251
1251
|
}
|
|
1252
1252
|
|
|
1253
|
-
// FRAMEWORK-BUG-021 — sweep the legacy `.
|
|
1253
|
+
// FRAMEWORK-BUG-021 — sweep the legacy `.codex/` codex tree
|
|
1254
1254
|
// BEFORE the install pipeline runs so the install can re-stamp
|
|
1255
|
-
// `.
|
|
1256
|
-
//
|
|
1257
|
-
//
|
|
1258
|
-
//
|
|
1255
|
+
// `.agents/skills/<folder>/SKILL.md` (Codex CLI's documented
|
|
1256
|
+
// surface, per https://developers.openai.com/codex/skills) against
|
|
1257
|
+
// a clean slate. Only fires when codex is in the requested runtime
|
|
1258
|
+
// set and `--keep-legacy` was not passed; the helper itself is
|
|
1259
|
+
// conservative (no-op on managed installs, no-op when no legacy
|
|
1260
|
+
// framework files are present). The v0.8.2 inversion is
|
|
1261
|
+
// documented in `runtime-migrate.mjs:7-12`.
|
|
1259
1262
|
let legacyCleanup = null
|
|
1260
1263
|
if (effectiveAnswers.runtimes.includes('codex')) {
|
|
1261
1264
|
if (parsedFlags.keepLegacy) {
|
|
@@ -1265,7 +1268,7 @@ export async function cmdInit(projectDir, extraArgs = []) {
|
|
|
1265
1268
|
legacyCleanup = { runtime: 'codex', ...cleanup }
|
|
1266
1269
|
if (cleanup.ran && cleanup.retained.length > 0) {
|
|
1267
1270
|
process.stderr.write(
|
|
1268
|
-
`[apt-tools] legacy_cleanup retained ${cleanup.retained.length} user-added path(s) under .
|
|
1271
|
+
`[apt-tools] legacy_cleanup retained ${cleanup.retained.length} user-added path(s) under .codex/: ${cleanup.retained.join(', ')}\n`,
|
|
1269
1272
|
)
|
|
1270
1273
|
}
|
|
1271
1274
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { cmdInstallFromSource } from '../install/install-from-source.mjs'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* commands/modes.mjs — `apt-tools modes list
|
|
2
|
+
* commands/modes.mjs — `apt-tools modes list`.
|
|
3
3
|
*
|
|
4
4
|
* Power-user discoverability tool. Returns a JSON envelope with one
|
|
5
5
|
* record per discovered skill, exposing modes / flags / content-formats /
|
|
@@ -28,7 +28,11 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
28
28
|
import { homedir } from 'node:os'
|
|
29
29
|
import { dirname, join, resolve } from 'node:path'
|
|
30
30
|
import { fileURLToPath } from 'node:url'
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
getTaskIsolationConfig,
|
|
33
|
+
getTrustedSkillSources,
|
|
34
|
+
loadMergedProjectConfig,
|
|
35
|
+
} from '../config/load.mjs'
|
|
32
36
|
import '../gate/gates/index.mjs'
|
|
33
37
|
import * as gateRegistry from '../gate/registry.mjs'
|
|
34
38
|
import { detectHost, SUPPORTED_HOSTS } from '../host/detect.mjs'
|
|
@@ -264,6 +268,19 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
264
268
|
else if (f === 'debug') track_override = 'DEBUG'
|
|
265
269
|
}
|
|
266
270
|
|
|
271
|
+
// FRAMEWORK-RFC-001 — consent signal for spar pipeline auto-inject.
|
|
272
|
+
// Reads merged config (config.json ∪ config.local.json) via the same
|
|
273
|
+
// helper apt-setup writes through. AUDIT-001 — spar fields live in
|
|
274
|
+
// .aperant/config.local.json, not shared config. Resolved once and
|
|
275
|
+
// folded into the envelope by both classify branches below.
|
|
276
|
+
// `loadMergedProjectConfig` returns null when no shared config exists
|
|
277
|
+
// (first-time init path) — treat as consent-off + no partner.
|
|
278
|
+
const merged = loadMergedProjectConfig(targetDir) ?? {}
|
|
279
|
+
const sparEnabled = merged.spar?.enabled === true
|
|
280
|
+
const sparPartner = merged.spar?.partner ?? null
|
|
281
|
+
const userPassedExplicitSparGates =
|
|
282
|
+
rawInput.includes('--spar-gates') || flags.some((f) => f.startsWith('spar-gates'))
|
|
283
|
+
|
|
267
284
|
const taskDescription = normalizeAptInvocation(rawInput.replace(/--\w+/g, '').trim())
|
|
268
285
|
|
|
269
286
|
const configPath = join(targetDir, '.aperant', 'config.json')
|
|
@@ -399,6 +416,17 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
399
416
|
const agentName = spawnAgent ? index.agentOf(primarySkill) || 'apt-planner' : null
|
|
400
417
|
const options = buildTrackOptions(track)
|
|
401
418
|
const gate_preview = previewGatesFor(primarySkill)
|
|
419
|
+
// FRAMEWORK-RFC-001 — flag-override path: track is known deterministically,
|
|
420
|
+
// so we can resolve the spar gate string inline and emit it directly.
|
|
421
|
+
// QUICK/DEBUG are Fast-Path-exempt — no inject. User-passed --spar-gates
|
|
422
|
+
// wins over consent (no double-inject). Field omitted entirely when null
|
|
423
|
+
// so the envelope stays byte-equivalent for users who never opted in.
|
|
424
|
+
let spar_inject_gates = null
|
|
425
|
+
if (sparEnabled && !userPassedExplicitSparGates) {
|
|
426
|
+
if (track === 'STANDARD') spar_inject_gates = 'plan'
|
|
427
|
+
else if (track === 'DEEP') spar_inject_gates = 'plan,execute'
|
|
428
|
+
// QUICK / DEBUG → stays null
|
|
429
|
+
}
|
|
402
430
|
return ok({
|
|
403
431
|
status: 'ok',
|
|
404
432
|
command: 'route',
|
|
@@ -417,6 +445,7 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
417
445
|
task_isolation,
|
|
418
446
|
update_check,
|
|
419
447
|
...(gate_preview ? { gate_preview } : {}),
|
|
448
|
+
...(spar_inject_gates ? { spar_inject_gates } : {}),
|
|
420
449
|
classification: {
|
|
421
450
|
source: 'flag-override',
|
|
422
451
|
reason: `Explicit --${track_override.toLowerCase()} flag`,
|
|
@@ -467,6 +496,18 @@ export function cmdRoute(projectDir, extraArgs) {
|
|
|
467
496
|
reason:
|
|
468
497
|
'Track and skill classification deferred to the host LLM; see classification_meta.trust_notice.',
|
|
469
498
|
},
|
|
499
|
+
// FRAMEWORK-RFC-001 — host model composes --spar-gates from these
|
|
500
|
+
// signals + its track classification per apt/SKILL.md MODE: classify
|
|
501
|
+
// Step 4. Field is absent when consent is off (envelope stays slim).
|
|
502
|
+
...(sparEnabled
|
|
503
|
+
? {
|
|
504
|
+
spar_consent: {
|
|
505
|
+
enabled: true,
|
|
506
|
+
partner: sparPartner,
|
|
507
|
+
user_passed_explicit_gates: userPassedExplicitSparGates,
|
|
508
|
+
},
|
|
509
|
+
}
|
|
510
|
+
: {}),
|
|
470
511
|
},
|
|
471
512
|
})
|
|
472
513
|
} catch (e) {
|
package/src/cli/config/load.mjs
CHANGED
|
@@ -361,11 +361,17 @@ export function getTrustedSkillSources(projectDir, cli) {
|
|
|
361
361
|
)
|
|
362
362
|
if (existsSync(consumerSkills)) defaults.push(consumerSkills)
|
|
363
363
|
// 4. Host CLI's installed runtime skills — only when the router knows
|
|
364
|
-
// which runtime it's in. Some hosts (
|
|
365
|
-
// .
|
|
364
|
+
// which runtime it's in. Some hosts (claude-code, gemini, …) install into
|
|
365
|
+
// .claude/skills / .gemini/skills etc; the router picks skills from
|
|
366
366
|
// the host-local directory so per-runtime transforms are honored.
|
|
367
|
+
//
|
|
368
|
+
// Codex is excluded: its transform strips frontmatter to {name, description}
|
|
369
|
+
// for codex validator compliance, and that stripped form fails the
|
|
370
|
+
// router's SkillFrontmatterSchema (contract.mjs). The canonical + kernel
|
|
371
|
+
// roots above still serve routing on Codex hosts (they carry the fat
|
|
372
|
+
// frontmatter the schema requires).
|
|
367
373
|
const installRoot = cli ? cliToInstallRoot(cli) : null
|
|
368
|
-
if (installRoot) {
|
|
374
|
+
if (installRoot && cli !== 'codex') {
|
|
369
375
|
const runtimeSkills = join(targetDir, installRoot, 'skills')
|
|
370
376
|
if (existsSync(runtimeSkills)) defaults.push(runtimeSkills)
|
|
371
377
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* postcondition of `apt-tools commit`. This closes the Conductor v2 gap
|
|
8
8
|
* exposed during live verification 2026-05-15: the planner's QUICK /
|
|
9
9
|
* SIMPLE track is hard-exempt from "new interaction discipline"
|
|
10
|
-
* (
|
|
10
|
+
* (Aperant Fast Path Guarantee ID-05), so the markdown emit step was
|
|
11
11
|
* never executed for tasks router-classified as simple. The fleet
|
|
12
12
|
* never saw artifact.ready, the PlanReadyCard never auto-promoted.
|
|
13
13
|
*
|
|
@@ -42,6 +42,10 @@ const CONDUCTOR_OPS = new Set([
|
|
|
42
42
|
'conductor.action.executed',
|
|
43
43
|
'conductor.action.gate_blocked',
|
|
44
44
|
'conductor.boot_reconciled',
|
|
45
|
+
// AC21 — MCP-server consent decisions audited to .aperant/events/*.jsonl
|
|
46
|
+
'mcp-consent.approved',
|
|
47
|
+
'mcp-consent.denied',
|
|
48
|
+
'mcp-consent.tofu-reprompt',
|
|
45
49
|
])
|
|
46
50
|
|
|
47
51
|
/** Allowed values for `phase.changed.to` — PTY busy/idle only. */
|
|
@@ -133,6 +137,21 @@ export function validateConductorEvent(op, data) {
|
|
|
133
137
|
'blocked_reason',
|
|
134
138
|
])
|
|
135
139
|
|
|
140
|
+
// AC21 — MCP server consent audit. Three ops share the same
|
|
141
|
+
// payload shape: who approved/denied which server, with which
|
|
142
|
+
// configHash, at what time. session_id is a session-scoped string
|
|
143
|
+
// (chat-session row id).
|
|
144
|
+
case 'mcp-consent.approved':
|
|
145
|
+
case 'mcp-consent.denied':
|
|
146
|
+
case 'mcp-consent.tofu-reprompt':
|
|
147
|
+
return requireFields(op, d, [
|
|
148
|
+
'server_id',
|
|
149
|
+
'config_sha256',
|
|
150
|
+
'decided_at',
|
|
151
|
+
'decided_by',
|
|
152
|
+
'session_id',
|
|
153
|
+
])
|
|
154
|
+
|
|
136
155
|
case 'conductor.boot_reconciled': {
|
|
137
156
|
// Fleet-wide event — numeric fields only. All optional but if
|
|
138
157
|
// present must be finite numbers (zero allowed).
|