@deftai/directive-core 0.99.0 → 0.101.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/dist/authz/actions.d.ts +6 -0
- package/dist/authz/actions.js +3 -0
- package/dist/authz/classify.js +241 -30
- package/dist/authz/decompose-apply.d.ts +81 -0
- package/dist/authz/decompose-apply.js +377 -0
- package/dist/authz/index.d.ts +3 -1
- package/dist/authz/index.js +3 -1
- package/dist/authz/store.d.ts +53 -0
- package/dist/authz/store.js +219 -1
- package/dist/authz/types.d.ts +19 -2
- package/dist/authz/types.js +5 -1
- package/dist/check/cached-orchestrator.d.ts +26 -5
- package/dist/check/cached-orchestrator.js +228 -6
- package/dist/check/gate-lists.d.ts +10 -4
- package/dist/check/gate-lists.js +19 -5
- package/dist/check/index.d.ts +3 -2
- package/dist/check/index.js +2 -1
- package/dist/check/named-cause.d.ts +45 -0
- package/dist/check/named-cause.js +121 -0
- package/dist/check/orchestrator.js +6 -1
- package/dist/doctor/checks.d.ts +44 -0
- package/dist/doctor/checks.js +428 -2
- package/dist/doctor/help.d.ts +5 -0
- package/dist/doctor/help.js +31 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/main.d.ts +6 -7
- package/dist/doctor/main.js +130 -79
- package/dist/doctor/session-coda.d.ts +82 -0
- package/dist/doctor/session-coda.js +151 -0
- package/dist/doctor/types.d.ts +16 -0
- package/dist/doctor/which.d.ts +20 -1
- package/dist/doctor/which.js +67 -1
- package/dist/finish-loop/pr-finish-loop.d.ts +13 -2
- package/dist/finish-loop/pr-finish-loop.js +116 -5
- package/dist/finish-loop/types.d.ts +1 -1
- package/dist/hooks/dispatcher.d.ts +9 -5
- package/dist/hooks/dispatcher.js +23 -15
- package/dist/hooks/readonly.d.ts +6 -3
- package/dist/hooks/readonly.js +44 -25
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/agent-hooks.d.ts +20 -0
- package/dist/init-deposit/agent-hooks.js +51 -31
- package/dist/init-deposit/gitignore.js +2 -0
- package/dist/intake/issue-ingest.js +36 -0
- package/dist/lifecycle/completed-consistency.d.ts +60 -0
- package/dist/lifecycle/completed-consistency.js +443 -0
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +47 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +415 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +52 -0
- package/dist/literal-acceptance/capture.js +658 -0
- package/dist/literal-acceptance/evaluate.d.ts +47 -0
- package/dist/literal-acceptance/evaluate.js +193 -0
- package/dist/literal-acceptance/index.d.ts +12 -0
- package/dist/literal-acceptance/index.js +12 -0
- package/dist/literal-acceptance/run.d.ts +38 -0
- package/dist/literal-acceptance/run.js +216 -0
- package/dist/literal-acceptance/safety.d.ts +24 -0
- package/dist/literal-acceptance/safety.js +251 -0
- package/dist/literal-acceptance/types.d.ts +82 -0
- package/dist/literal-acceptance/types.js +26 -0
- package/dist/policy/ac-pass-banking.d.ts +58 -0
- package/dist/policy/ac-pass-banking.js +150 -0
- package/dist/policy/ceremony-dial.d.ts +96 -11
- package/dist/policy/ceremony-dial.js +250 -23
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +16 -1
- package/dist/policy/merge-approval-head.d.ts +129 -0
- package/dist/policy/merge-approval-head.js +461 -0
- package/dist/policy/require-human-merge.d.ts +5 -0
- package/dist/policy/require-human-merge.js +5 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +20 -3
- package/dist/pr-merge-readiness/ci-gate.js +65 -5
- package/dist/pr-merge-readiness/compute.d.ts +13 -0
- package/dist/pr-merge-readiness/compute.js +99 -14
- package/dist/pr-merge-readiness/gh.d.ts +49 -0
- package/dist/pr-merge-readiness/gh.js +268 -0
- package/dist/pr-merge-readiness/index.d.ts +2 -2
- package/dist/pr-merge-readiness/index.js +2 -2
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +11 -0
- package/dist/pr-wait-mergeable/cascade.js +115 -1
- package/dist/pr-wait-mergeable/main.d.ts +4 -0
- package/dist/pr-wait-mergeable/main.js +4 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -1
- package/dist/pr-wait-mergeable/wrappers.js +7 -1
- package/dist/preflight/evaluate.d.ts +15 -1
- package/dist/preflight/evaluate.js +33 -2
- package/dist/preflight-cache/evaluate.d.ts +3 -0
- package/dist/preflight-cache/evaluate.js +3 -0
- package/dist/product-first-done-gate/acceptance.d.ts +25 -0
- package/dist/product-first-done-gate/acceptance.js +247 -0
- package/dist/product-first-done-gate/check-mode.d.ts +45 -0
- package/dist/product-first-done-gate/check-mode.js +148 -0
- package/dist/product-first-done-gate/evaluate.d.ts +50 -0
- package/dist/product-first-done-gate/evaluate.js +255 -0
- package/dist/product-first-done-gate/index.d.ts +11 -0
- package/dist/product-first-done-gate/index.js +11 -0
- package/dist/product-first-done-gate/types.d.ts +64 -0
- package/dist/product-first-done-gate/types.js +61 -0
- package/dist/render/framework-commands.js +4 -0
- package/dist/run-summary/emit.d.ts +56 -0
- package/dist/run-summary/emit.js +171 -0
- package/dist/run-summary/index.d.ts +4 -0
- package/dist/run-summary/index.js +4 -0
- package/dist/run-summary/path.d.ts +25 -0
- package/dist/run-summary/path.js +78 -0
- package/dist/run-summary/types.d.ts +76 -0
- package/dist/run-summary/types.js +21 -0
- package/dist/scope/acceptance-evidence.d.ts +110 -0
- package/dist/scope/acceptance-evidence.js +422 -0
- package/dist/scope/coverage-map.d.ts +98 -0
- package/dist/scope/coverage-map.js +558 -0
- package/dist/scope/decompose.d.ts +5 -0
- package/dist/scope/decompose.js +134 -11
- package/dist/scope/index.d.ts +3 -0
- package/dist/scope/index.js +3 -0
- package/dist/scope/parent-lineage.d.ts +125 -0
- package/dist/scope/parent-lineage.js +626 -0
- package/dist/scope/transition.d.ts +8 -0
- package/dist/scope/transition.js +61 -2
- package/dist/session/ac-pass-banking.d.ts +221 -0
- package/dist/session/ac-pass-banking.js +761 -0
- package/dist/session/active-cli.d.ts +79 -0
- package/dist/session/active-cli.js +382 -0
- package/dist/session/deposit-sha.d.ts +49 -0
- package/dist/session/deposit-sha.js +121 -0
- package/dist/session/effort-budget.d.ts +136 -0
- package/dist/session/effort-budget.js +352 -0
- package/dist/session/index.d.ts +7 -0
- package/dist/session/index.js +7 -0
- package/dist/session/orientation-compression.d.ts +127 -0
- package/dist/session/orientation-compression.js +425 -0
- package/dist/session/orientation-state.d.ts +29 -0
- package/dist/session/orientation-state.js +85 -0
- package/dist/session/session-start.d.ts +31 -0
- package/dist/session/session-start.js +228 -2
- package/dist/session/toolchain-preflight.d.ts +64 -0
- package/dist/session/toolchain-preflight.js +151 -0
- package/dist/session/verify-session-ritual.d.ts +14 -0
- package/dist/session/verify-session-ritual.js +33 -0
- package/dist/story-ready/evaluate.d.ts +11 -0
- package/dist/story-ready/evaluate.js +41 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -2
- package/dist/triage/actions/candidates-log.js +37 -6
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +8 -2
- package/dist/triage/bootstrap/index.js +15 -3
- package/dist/triage/classify/index.d.ts +1 -0
- package/dist/triage/classify/index.js +2 -0
- package/dist/triage/classify/label-mirror.js +16 -0
- package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
- package/dist/triage/classify/mirror-discovery-tip.js +234 -0
- package/dist/triage/reconcile/reconcile.js +28 -6
- package/dist/triage/summary/index.js +12 -4
- package/dist/triage/welcome/default-mode.js +16 -0
- package/dist/ts-check-lane/run-lane.js +21 -4
- package/dist/vbrief-validate/conformance.js +7 -0
- package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
- package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
- package/dist/xbrief-migrate/migrate-project.js +92 -3
- package/package.json +15 -3
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety filters for literal acceptance commands (#3267 Greptile P1).
|
|
3
|
+
*
|
|
4
|
+
* Stored commands may originate from issue text. Before shell execution they
|
|
5
|
+
* MUST pass an allowlist of argv-shaped CLI invocations without shell metacharacters.
|
|
6
|
+
* source=task_statement is capture-only until agent promotes to verify_commands.
|
|
7
|
+
*
|
|
8
|
+
* Wrapper verbs (task/deft/directive) and package managers are further restricted
|
|
9
|
+
* to read-only / verification subcommands so ambient credentials and lifecycle
|
|
10
|
+
* mutations cannot ride an acceptance command (#3267 residual).
|
|
11
|
+
*/
|
|
12
|
+
import { EXECUTABLE_LITERAL_SOURCES } from "./types.js";
|
|
13
|
+
/** Characters that imply shell composition / expansion (refuse closed). */
|
|
14
|
+
const SHELL_META_CHARS = new Set([
|
|
15
|
+
";",
|
|
16
|
+
"|",
|
|
17
|
+
"&",
|
|
18
|
+
"`",
|
|
19
|
+
"\n",
|
|
20
|
+
"\r",
|
|
21
|
+
"<",
|
|
22
|
+
">",
|
|
23
|
+
"(",
|
|
24
|
+
")",
|
|
25
|
+
"{",
|
|
26
|
+
"}",
|
|
27
|
+
"$",
|
|
28
|
+
"\0",
|
|
29
|
+
]);
|
|
30
|
+
/**
|
|
31
|
+
* First-token allowlist for **execution** (#3267 Greptile P1 ambient-authority).
|
|
32
|
+
* Network/SCM tools (curl/gh/git/docker) are intentionally excluded — they retain
|
|
33
|
+
* ambient credentials. Capture may still record broader CLI shape; only these
|
|
34
|
+
* tokens may spawn, and wrappers/PMs require subcommand allowlists below.
|
|
35
|
+
*/
|
|
36
|
+
const ALLOWED_FIRST_TOKENS = new Set([
|
|
37
|
+
"task",
|
|
38
|
+
"deft",
|
|
39
|
+
"directive",
|
|
40
|
+
"pnpm",
|
|
41
|
+
"npm",
|
|
42
|
+
"npx",
|
|
43
|
+
"yarn",
|
|
44
|
+
"bun",
|
|
45
|
+
"vitest",
|
|
46
|
+
"true",
|
|
47
|
+
"false",
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
50
|
+
* Exact wrapper subcommands that are verification-shaped (no scope/policy/scm/swarm
|
|
51
|
+
* mutations). Additional args after these tokens are allowed (e.g. `task check --json`).
|
|
52
|
+
*/
|
|
53
|
+
const ALLOWED_WRAPPER_SUBCOMMANDS = new Set([
|
|
54
|
+
"check",
|
|
55
|
+
"doctor",
|
|
56
|
+
"help",
|
|
57
|
+
"--help",
|
|
58
|
+
"-h",
|
|
59
|
+
"--version",
|
|
60
|
+
"-v",
|
|
61
|
+
"test",
|
|
62
|
+
]);
|
|
63
|
+
/**
|
|
64
|
+
* Prefixes for wrapper verbs that stay on the verify/read surface.
|
|
65
|
+
* `verify:` / `verify-` cover task verify:* family; bare `verify ` is the space form.
|
|
66
|
+
*/
|
|
67
|
+
const ALLOWED_WRAPPER_PREFIXES = ["verify:", "verify-", "verify "];
|
|
68
|
+
/** Whether this provenance is allowed to spawn a shell (#3267). */
|
|
69
|
+
export function isExecutableLiteralSource(source) {
|
|
70
|
+
return EXECUTABLE_LITERAL_SOURCES.includes(source);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Linear-time scan: refuse shell metacharacters and non-allowlisted first tokens.
|
|
74
|
+
* Intentionally does not parse full shell grammar — fail closed on ambiguity.
|
|
75
|
+
*/
|
|
76
|
+
export function evaluateCommandSafety(command) {
|
|
77
|
+
if (typeof command !== "string" || command.trim().length === 0) {
|
|
78
|
+
return { ok: false, reason: "empty command" };
|
|
79
|
+
}
|
|
80
|
+
const trimmed = command.trim();
|
|
81
|
+
if (trimmed.length > 500) {
|
|
82
|
+
return { ok: false, reason: "command exceeds 500 characters" };
|
|
83
|
+
}
|
|
84
|
+
for (let i = 0; i < trimmed.length; i += 1) {
|
|
85
|
+
const ch = trimmed[i];
|
|
86
|
+
if (SHELL_META_CHARS.has(ch)) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
reason: `shell metacharacter ${JSON.stringify(ch)} is not allowed in literal AC commands`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
let end = 0;
|
|
94
|
+
while (end < trimmed.length && trimmed[end] !== " " && trimmed[end] !== "\t") {
|
|
95
|
+
end += 1;
|
|
96
|
+
}
|
|
97
|
+
const first = trimmed.slice(0, end).toLowerCase();
|
|
98
|
+
if (first.includes("/") || first.includes("\\") || first.includes(":")) {
|
|
99
|
+
return { ok: false, reason: "path-like first token is not allowlisted" };
|
|
100
|
+
}
|
|
101
|
+
if (!ALLOWED_FIRST_TOKENS.has(first)) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
reason: `first token ${JSON.stringify(first)} is not in the literal-AC allowlist`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const rest = trimmed.slice(end).trim();
|
|
108
|
+
// Framework wrappers: only verification / help / version subcommands (no scope/policy/merge).
|
|
109
|
+
if (first === "task" || first === "deft" || first === "directive") {
|
|
110
|
+
return evaluateWrapperSubcommand(rest);
|
|
111
|
+
}
|
|
112
|
+
// Package managers: only test/exec vitest/run test|check|--version (no install/publish/net).
|
|
113
|
+
// npx is stricter (no run/exec registry forms) — see evaluatePackageManagerArgs.
|
|
114
|
+
if (first === "pnpm" ||
|
|
115
|
+
first === "npm" ||
|
|
116
|
+
first === "npx" ||
|
|
117
|
+
first === "yarn" ||
|
|
118
|
+
first === "bun") {
|
|
119
|
+
return evaluatePackageManagerArgs(rest, first);
|
|
120
|
+
}
|
|
121
|
+
// vitest first-token: allow run / related test args only (no watch/ui).
|
|
122
|
+
if (first === "vitest") {
|
|
123
|
+
return evaluateVitestArgs(rest);
|
|
124
|
+
}
|
|
125
|
+
return { ok: true, reason: null };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Restrict task/deft/directive to verification-shaped subcommands.
|
|
129
|
+
* Fail closed on scope/policy/swarm/pr/scm/lifecycle mutations.
|
|
130
|
+
*/
|
|
131
|
+
function evaluateWrapperSubcommand(rest) {
|
|
132
|
+
if (rest.length === 0) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
reason: "wrapper verb (task/deft/directive) requires a restricted verification subcommand",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const low = rest.toLowerCase();
|
|
139
|
+
// First subcommand token (before flags/args).
|
|
140
|
+
let subEnd = 0;
|
|
141
|
+
while (subEnd < low.length && low[subEnd] !== " " && low[subEnd] !== "\t") {
|
|
142
|
+
subEnd += 1;
|
|
143
|
+
}
|
|
144
|
+
const sub = low.slice(0, subEnd);
|
|
145
|
+
if (ALLOWED_WRAPPER_SUBCOMMANDS.has(sub)) {
|
|
146
|
+
return { ok: true, reason: null };
|
|
147
|
+
}
|
|
148
|
+
for (const prefix of ALLOWED_WRAPPER_PREFIXES) {
|
|
149
|
+
if (low.startsWith(prefix)) {
|
|
150
|
+
return { ok: true, reason: null };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
reason: "wrapper subcommand must be check|doctor|verify:*|help|--version " +
|
|
156
|
+
"(scope/policy/swarm/scm/merge and other ambient-authority verbs denied)",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Package-manager subcommand allowlist (no install/publish/exec of arbitrary bins).
|
|
161
|
+
* `npx` is stricter than npm/pnpm/yarn/bun: it must not accept `run`/`exec` forms that
|
|
162
|
+
* resolve arbitrary registry packages with inherited env (Greptile conf residual).
|
|
163
|
+
*/
|
|
164
|
+
function evaluatePackageManagerArgs(rest, firstToken) {
|
|
165
|
+
if (rest.length === 0) {
|
|
166
|
+
return { ok: false, reason: "package manager requires a restricted subcommand" };
|
|
167
|
+
}
|
|
168
|
+
const low = rest.toLowerCase();
|
|
169
|
+
// npx: only vitest (direct) or version/help — no `npx run` / `npx exec <pkg>` registry path.
|
|
170
|
+
if (firstToken === "npx") {
|
|
171
|
+
if (low === "--version" || low === "-v" || low === "--help" || low === "-h") {
|
|
172
|
+
return { ok: true, reason: null };
|
|
173
|
+
}
|
|
174
|
+
if (low === "vitest" || low.startsWith("vitest ")) {
|
|
175
|
+
const after = low === "vitest" ? "" : low.slice("vitest".length).trim();
|
|
176
|
+
return evaluateVitestArgs(after);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
reason: "npx is limited to vitest|--version|--help " +
|
|
181
|
+
"(npx run/exec/registry packages denied for ambient-authority)",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// exec: only vitest (not deft/task — those re-open wrapper ambient authority).
|
|
185
|
+
if (low.startsWith("exec ")) {
|
|
186
|
+
const afterExec = low.slice("exec ".length).trim();
|
|
187
|
+
if (afterExec === "vitest" || afterExec.startsWith("vitest ")) {
|
|
188
|
+
const after = afterExec === "vitest" ? "" : afterExec.slice("vitest".length).trim();
|
|
189
|
+
return evaluateVitestArgs(after);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
reason: "package-manager exec is limited to vitest " +
|
|
194
|
+
"(exec of deft/task/other bins denied for ambient-authority)",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (low.startsWith("run vitest")) {
|
|
198
|
+
const after = low.slice("run vitest".length).trim();
|
|
199
|
+
return evaluateVitestArgs(after.length > 0 ? `run ${after}` : "run");
|
|
200
|
+
}
|
|
201
|
+
const allowedPm = low === "test" ||
|
|
202
|
+
low === "--version" ||
|
|
203
|
+
low === "-v" ||
|
|
204
|
+
low.startsWith("test ") ||
|
|
205
|
+
low.startsWith("run test") ||
|
|
206
|
+
low.startsWith("run check");
|
|
207
|
+
if (!allowedPm) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
reason: "package-manager args must be test|exec vitest|run test|run check|--version " +
|
|
211
|
+
"(arbitrary scripts/network install denied for ambient-authority)",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { ok: true, reason: null };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Vitest args: only non-interactive run paths. Deny watch/ui/browser hang modes.
|
|
218
|
+
*/
|
|
219
|
+
function evaluateVitestArgs(rest) {
|
|
220
|
+
const low = rest.trim().toLowerCase();
|
|
221
|
+
if (low.length === 0) {
|
|
222
|
+
// bare `vitest` defaults to watch in many setups — refuse closed.
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
reason: "vitest requires explicit run (bare vitest defaults to watch; denied)",
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
for (const bad of ["watch", "ui", "browser", "--watch", "--ui", "--browser", "dev"]) {
|
|
229
|
+
if (low === bad ||
|
|
230
|
+
low.startsWith(`${bad} `) ||
|
|
231
|
+
low.endsWith(` ${bad}`) ||
|
|
232
|
+
low.includes(` ${bad} `)) {
|
|
233
|
+
return {
|
|
234
|
+
ok: false,
|
|
235
|
+
reason: `vitest ${bad} is denied (hang/interactive mode; use run)`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (low === "run" ||
|
|
240
|
+
low.startsWith("run ") ||
|
|
241
|
+
low === "--version" ||
|
|
242
|
+
low === "-v" ||
|
|
243
|
+
low.startsWith("--run")) {
|
|
244
|
+
return { ok: true, reason: null };
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
reason: "vitest args must be run|--version (watch/ui/network denied for ambient-authority)",
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal acceptance-command verification (#3267).
|
|
3
|
+
*
|
|
4
|
+
* Capture exact stated shell commands from the task statement at intake;
|
|
5
|
+
* run them verbatim before done. Self-chosen verification is supplementary.
|
|
6
|
+
* Extends #973 machine-verifiable-spec. Survives ceremony dial rapid/minimal
|
|
7
|
+
* (#3214) — verification depth is constant (#3156).
|
|
8
|
+
*/
|
|
9
|
+
/** Where a literal command was recovered from. */
|
|
10
|
+
export type LiteralAcceptanceSource = "task_statement" | "verify_commands" | "plan_item" | "explicit" | "metadata";
|
|
11
|
+
/**
|
|
12
|
+
* Sources trusted for automatic shell execution (#3267 Greptile P1).
|
|
13
|
+
* `task_statement` (raw issue/task text) is capture-only until an agent promotes
|
|
14
|
+
* the exact command into swarm.verify_commands / plan item / explicit metadata.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EXECUTABLE_LITERAL_SOURCES: readonly LiteralAcceptanceSource[];
|
|
17
|
+
/**
|
|
18
|
+
* One executable acceptance command exactly as stated (no paraphrase).
|
|
19
|
+
* `command` is the shell string to run with the same flags/cwd as written.
|
|
20
|
+
*/
|
|
21
|
+
export interface LiteralAcceptanceCommand {
|
|
22
|
+
/** Exact command string from the task statement (not reworded). */
|
|
23
|
+
readonly command: string;
|
|
24
|
+
/**
|
|
25
|
+
* Working directory relative to project root when stated; null/omit = project root.
|
|
26
|
+
* Absolute paths are allowed when the statement uses them.
|
|
27
|
+
*/
|
|
28
|
+
readonly cwd?: string | null;
|
|
29
|
+
/** Expected stdout substring when the statement names expected output. */
|
|
30
|
+
readonly expectedStdout?: string | null;
|
|
31
|
+
/** Expected process exit code (default 0). */
|
|
32
|
+
readonly expectedExitCode?: number;
|
|
33
|
+
readonly source: LiteralAcceptanceSource;
|
|
34
|
+
/** Short provenance (heading, field path, or line context). */
|
|
35
|
+
readonly sourceSpan?: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Result of running one stored command. */
|
|
38
|
+
export interface LiteralAcceptanceRunResult {
|
|
39
|
+
readonly command: string;
|
|
40
|
+
readonly cwd: string;
|
|
41
|
+
readonly exitCode: number;
|
|
42
|
+
readonly stdout: string;
|
|
43
|
+
readonly stderr: string;
|
|
44
|
+
readonly ok: boolean;
|
|
45
|
+
readonly detail: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Shell-shaped command that was refused by the safety allowlist (#3267 residual).
|
|
49
|
+
* Operators must see these — silent drop hides ambient-authority / shape mistakes.
|
|
50
|
+
*/
|
|
51
|
+
export interface RejectedLiteralCommand {
|
|
52
|
+
readonly command: string;
|
|
53
|
+
readonly reason: string;
|
|
54
|
+
readonly sourceSpan?: string | null;
|
|
55
|
+
}
|
|
56
|
+
/** Aggregate evaluate / done-gate result. */
|
|
57
|
+
export interface LiteralAcceptanceGateResult {
|
|
58
|
+
readonly ok: boolean;
|
|
59
|
+
/** 0 = pass or no commands; 1 = command failed; 2 = config / input error. */
|
|
60
|
+
readonly code: 0 | 1 | 2;
|
|
61
|
+
readonly message: string;
|
|
62
|
+
readonly commands: readonly LiteralAcceptanceCommand[];
|
|
63
|
+
readonly runs: readonly LiteralAcceptanceRunResult[];
|
|
64
|
+
/** Safety-rejected shell-shaped lines (fail-loud ledger; may be empty). */
|
|
65
|
+
readonly rejected?: readonly RejectedLiteralCommand[];
|
|
66
|
+
}
|
|
67
|
+
/** Metadata key for operator-visible rejected command ledger (#3267). */
|
|
68
|
+
export declare const LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY: "literal_acceptance_rejected";
|
|
69
|
+
/** Injectable shell runner for tests (default: spawnSync shell). */
|
|
70
|
+
export type LiteralAcceptanceRunner = (input: {
|
|
71
|
+
readonly command: string;
|
|
72
|
+
readonly cwd: string;
|
|
73
|
+
}) => {
|
|
74
|
+
readonly exitCode: number;
|
|
75
|
+
readonly stdout: string;
|
|
76
|
+
readonly stderr: string;
|
|
77
|
+
};
|
|
78
|
+
/** Metadata key on plan.metadata for captured literal commands (#3267). */
|
|
79
|
+
export declare const LITERAL_ACCEPTANCE_METADATA_KEY: "literal_acceptance_commands";
|
|
80
|
+
/** Alternate camelCase key accepted on read. */
|
|
81
|
+
export declare const LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL: "literalAcceptanceCommands";
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal acceptance-command verification (#3267).
|
|
3
|
+
*
|
|
4
|
+
* Capture exact stated shell commands from the task statement at intake;
|
|
5
|
+
* run them verbatim before done. Self-chosen verification is supplementary.
|
|
6
|
+
* Extends #973 machine-verifiable-spec. Survives ceremony dial rapid/minimal
|
|
7
|
+
* (#3214) — verification depth is constant (#3156).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Sources trusted for automatic shell execution (#3267 Greptile P1).
|
|
11
|
+
* `task_statement` (raw issue/task text) is capture-only until an agent promotes
|
|
12
|
+
* the exact command into swarm.verify_commands / plan item / explicit metadata.
|
|
13
|
+
*/
|
|
14
|
+
export const EXECUTABLE_LITERAL_SOURCES = [
|
|
15
|
+
"verify_commands",
|
|
16
|
+
"plan_item",
|
|
17
|
+
"explicit",
|
|
18
|
+
"metadata",
|
|
19
|
+
];
|
|
20
|
+
/** Metadata key for operator-visible rejected command ledger (#3267). */
|
|
21
|
+
export const LITERAL_ACCEPTANCE_REJECTED_METADATA_KEY = "literal_acceptance_rejected";
|
|
22
|
+
/** Metadata key on plan.metadata for captured literal commands (#3267). */
|
|
23
|
+
export const LITERAL_ACCEPTANCE_METADATA_KEY = "literal_acceptance_commands";
|
|
24
|
+
/** Alternate camelCase key accepted on read. */
|
|
25
|
+
export const LITERAL_ACCEPTANCE_METADATA_KEY_CAMEL = "literalAcceptanceCommands";
|
|
26
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed plan.policy.acPassBanking (#3285).
|
|
3
|
+
*
|
|
4
|
+
* Surplus-threshold policy for post-AC-pass deepening: after the first
|
|
5
|
+
* stated-AC bank, agents may deepen only when remaining budget is at least
|
|
6
|
+
* this fraction of the hard max (default 0.2 = 20%).
|
|
7
|
+
*
|
|
8
|
+
* Composes #3266 bank-the-pass and product-first done-gate (#3284).
|
|
9
|
+
*/
|
|
10
|
+
/** Canonical dotted path for policy:show / PROJECT-DEFINITION. */
|
|
11
|
+
export declare const FIELD_AC_PASS_BANKING = "plan.policy.acPassBanking";
|
|
12
|
+
/** CLI alias for `task policy:show --field=acPassBanking`. */
|
|
13
|
+
export declare const FIELD_AC_PASS_BANKING_CLI_ALIAS = "acPassBanking";
|
|
14
|
+
/**
|
|
15
|
+
* Default surplus fraction of max turns/cost that must remain after bank
|
|
16
|
+
* before self-imposed deepening is allowed (#3285).
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_SURPLUS_THRESHOLD = 0.2;
|
|
19
|
+
/** Floor / ceiling for surplusThreshold (inclusive). */
|
|
20
|
+
export declare const SURPLUS_THRESHOLD_MIN = 0;
|
|
21
|
+
export declare const SURPLUS_THRESHOLD_MAX = 1;
|
|
22
|
+
/** Optional env override (fraction 0–1 or percent 0–100). */
|
|
23
|
+
export declare const ENV_SURPLUS_THRESHOLD = "DEFT_AC_PASS_SURPLUS_THRESHOLD";
|
|
24
|
+
export interface AcPassBankingConfig {
|
|
25
|
+
/**
|
|
26
|
+
* When false, surplus gate is inactive (unbounded / optional discipline only).
|
|
27
|
+
* Default true — the gate still only binds under a hard effort budget.
|
|
28
|
+
*/
|
|
29
|
+
readonly enabled: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Minimum remaining fraction of max turns/cost required to deepen after bank.
|
|
32
|
+
* Default {@link DEFAULT_SURPLUS_THRESHOLD} (0.2).
|
|
33
|
+
*/
|
|
34
|
+
readonly surplusThreshold: number;
|
|
35
|
+
}
|
|
36
|
+
export type AcPassBankingSource = "typed" | "env" | "default" | "default-on-error";
|
|
37
|
+
export interface AcPassBankingResolved extends AcPassBankingConfig {
|
|
38
|
+
readonly source: AcPassBankingSource;
|
|
39
|
+
readonly error: string | null;
|
|
40
|
+
}
|
|
41
|
+
/** Parse a 0–1 fraction or a 0–100 percent string/number into a fraction. */
|
|
42
|
+
export declare function parseSurplusThreshold(raw: unknown): number | null;
|
|
43
|
+
/** Validate a `plan.policy.acPassBanking` payload. */
|
|
44
|
+
export declare function validateAcPassBanking(value: unknown): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Resolve surplus policy from typed plan.policy.acPassBanking, then env,
|
|
47
|
+
* then defaults (#3285).
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveAcPassBanking(projectRoot?: string | null, environ?: Readonly<Record<string, string | undefined>>): AcPassBankingResolved;
|
|
50
|
+
export interface AcPassBankingPolicyField {
|
|
51
|
+
readonly name: typeof FIELD_AC_PASS_BANKING;
|
|
52
|
+
readonly current: AcPassBankingConfig;
|
|
53
|
+
readonly default: AcPassBankingConfig;
|
|
54
|
+
readonly source: string;
|
|
55
|
+
}
|
|
56
|
+
/** Inspector row for `task policy:show --field=acPassBanking` (#3285). */
|
|
57
|
+
export declare function inspectAcPassBanking(_data: Record<string, unknown> | null, projectRoot?: string): AcPassBankingPolicyField;
|
|
58
|
+
//# sourceMappingURL=ac-pass-banking.d.ts.map
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed plan.policy.acPassBanking (#3285).
|
|
3
|
+
*
|
|
4
|
+
* Surplus-threshold policy for post-AC-pass deepening: after the first
|
|
5
|
+
* stated-AC bank, agents may deepen only when remaining budget is at least
|
|
6
|
+
* this fraction of the hard max (default 0.2 = 20%).
|
|
7
|
+
*
|
|
8
|
+
* Composes #3266 bank-the-pass and product-first done-gate (#3284).
|
|
9
|
+
*/
|
|
10
|
+
import { readPlanPolicy } from "./plan-extensions.js";
|
|
11
|
+
import { loadProjectDefinition } from "./resolve.js";
|
|
12
|
+
/** Canonical dotted path for policy:show / PROJECT-DEFINITION. */
|
|
13
|
+
export const FIELD_AC_PASS_BANKING = "plan.policy.acPassBanking";
|
|
14
|
+
/** CLI alias for `task policy:show --field=acPassBanking`. */
|
|
15
|
+
export const FIELD_AC_PASS_BANKING_CLI_ALIAS = "acPassBanking";
|
|
16
|
+
/**
|
|
17
|
+
* Default surplus fraction of max turns/cost that must remain after bank
|
|
18
|
+
* before self-imposed deepening is allowed (#3285).
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_SURPLUS_THRESHOLD = 0.2;
|
|
21
|
+
/** Floor / ceiling for surplusThreshold (inclusive). */
|
|
22
|
+
export const SURPLUS_THRESHOLD_MIN = 0;
|
|
23
|
+
export const SURPLUS_THRESHOLD_MAX = 1;
|
|
24
|
+
/** Optional env override (fraction 0–1 or percent 0–100). */
|
|
25
|
+
export const ENV_SURPLUS_THRESHOLD = "DEFT_AC_PASS_SURPLUS_THRESHOLD";
|
|
26
|
+
function defaultResolved(source, error = null, override) {
|
|
27
|
+
return {
|
|
28
|
+
enabled: override?.enabled ?? true,
|
|
29
|
+
surplusThreshold: override?.surplusThreshold ?? DEFAULT_SURPLUS_THRESHOLD,
|
|
30
|
+
source,
|
|
31
|
+
error,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Parse a 0–1 fraction or a 0–100 percent string/number into a fraction. */
|
|
35
|
+
export function parseSurplusThreshold(raw) {
|
|
36
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
37
|
+
if (raw >= 0 && raw <= 1)
|
|
38
|
+
return raw;
|
|
39
|
+
// Allow 20 meaning 20% when clearly a percent.
|
|
40
|
+
if (raw > 1 && raw <= 100)
|
|
41
|
+
return raw / 100;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (typeof raw === "string") {
|
|
45
|
+
const text = raw.trim();
|
|
46
|
+
if (!text)
|
|
47
|
+
return null;
|
|
48
|
+
const percent = text.endsWith("%");
|
|
49
|
+
const n = Number(percent ? text.slice(0, -1).trim() : text);
|
|
50
|
+
if (!Number.isFinite(n))
|
|
51
|
+
return null;
|
|
52
|
+
if (percent) {
|
|
53
|
+
if (n < 0 || n > 100)
|
|
54
|
+
return null;
|
|
55
|
+
return n / 100;
|
|
56
|
+
}
|
|
57
|
+
if (n >= 0 && n <= 1)
|
|
58
|
+
return n;
|
|
59
|
+
if (n > 1 && n <= 100)
|
|
60
|
+
return n / 100;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/** Validate a `plan.policy.acPassBanking` payload. */
|
|
66
|
+
export function validateAcPassBanking(value) {
|
|
67
|
+
if (value === null || value === undefined) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
71
|
+
return [`${FIELD_AC_PASS_BANKING} must be an object; got ${typeof value}`];
|
|
72
|
+
}
|
|
73
|
+
const rec = value;
|
|
74
|
+
const errors = [];
|
|
75
|
+
if ("enabled" in rec && typeof rec.enabled !== "boolean") {
|
|
76
|
+
errors.push(`${FIELD_AC_PASS_BANKING}.enabled must be a boolean`);
|
|
77
|
+
}
|
|
78
|
+
if ("surplusThreshold" in rec) {
|
|
79
|
+
const parsed = parseSurplusThreshold(rec.surplusThreshold);
|
|
80
|
+
if (parsed === null) {
|
|
81
|
+
errors.push(`${FIELD_AC_PASS_BANKING}.surplusThreshold must be a fraction 0–1 ` +
|
|
82
|
+
`(or percent 0–100); got ${JSON.stringify(rec.surplusThreshold)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return errors;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve surplus policy from typed plan.policy.acPassBanking, then env,
|
|
89
|
+
* then defaults (#3285).
|
|
90
|
+
*/
|
|
91
|
+
export function resolveAcPassBanking(projectRoot, environ = process.env) {
|
|
92
|
+
if (projectRoot !== undefined && projectRoot !== null && projectRoot.length > 0) {
|
|
93
|
+
const [data, err] = loadProjectDefinition(projectRoot);
|
|
94
|
+
if (data !== null) {
|
|
95
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
96
|
+
if (typeof policyBlock === "object" &&
|
|
97
|
+
policyBlock !== null &&
|
|
98
|
+
!Array.isArray(policyBlock) &&
|
|
99
|
+
"acPassBanking" in policyBlock) {
|
|
100
|
+
const raw = policyBlock.acPassBanking;
|
|
101
|
+
const errors = validateAcPassBanking(raw);
|
|
102
|
+
if (errors.length > 0) {
|
|
103
|
+
return defaultResolved("default-on-error", errors[0] ?? "invalid acPassBanking");
|
|
104
|
+
}
|
|
105
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
106
|
+
const rec = raw;
|
|
107
|
+
const enabled = typeof rec.enabled === "boolean" ? rec.enabled : true;
|
|
108
|
+
const threshold = "surplusThreshold" in rec
|
|
109
|
+
? (parseSurplusThreshold(rec.surplusThreshold) ?? DEFAULT_SURPLUS_THRESHOLD)
|
|
110
|
+
: DEFAULT_SURPLUS_THRESHOLD;
|
|
111
|
+
return {
|
|
112
|
+
enabled,
|
|
113
|
+
surplusThreshold: threshold,
|
|
114
|
+
source: "typed",
|
|
115
|
+
error: null,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else if (err !== null && err.length > 0) {
|
|
121
|
+
// Fall through to env/default; record nothing as fatal.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const envRaw = environ[ENV_SURPLUS_THRESHOLD];
|
|
125
|
+
if (envRaw !== undefined && envRaw.trim() !== "") {
|
|
126
|
+
const parsed = parseSurplusThreshold(envRaw);
|
|
127
|
+
if (parsed !== null) {
|
|
128
|
+
return defaultResolved("env", null, { surplusThreshold: parsed });
|
|
129
|
+
}
|
|
130
|
+
return defaultResolved("default-on-error", `${ENV_SURPLUS_THRESHOLD} is not a valid fraction/percent: ${envRaw}`);
|
|
131
|
+
}
|
|
132
|
+
return defaultResolved("default");
|
|
133
|
+
}
|
|
134
|
+
/** Inspector row for `task policy:show --field=acPassBanking` (#3285). */
|
|
135
|
+
export function inspectAcPassBanking(_data, projectRoot) {
|
|
136
|
+
const resolved = resolveAcPassBanking(projectRoot);
|
|
137
|
+
return {
|
|
138
|
+
name: FIELD_AC_PASS_BANKING,
|
|
139
|
+
current: {
|
|
140
|
+
enabled: resolved.enabled,
|
|
141
|
+
surplusThreshold: resolved.surplusThreshold,
|
|
142
|
+
},
|
|
143
|
+
default: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
surplusThreshold: DEFAULT_SURPLUS_THRESHOLD,
|
|
146
|
+
},
|
|
147
|
+
source: resolved.source,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=ac-pass-banking.js.map
|