@deftai/directive-core 0.98.1 → 0.100.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 +401 -75
- package/dist/authz/decompose-apply.d.ts +67 -0
- package/dist/authz/decompose-apply.js +302 -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/consumer-check-contract/evaluate.d.ts +40 -0
- package/dist/consumer-check-contract/evaluate.js +188 -3
- package/dist/consumer-check-contract/index.d.ts +1 -1
- package/dist/consumer-check-contract/index.js +1 -1
- package/dist/content-contracts/skills/greptile-detector.d.ts +42 -0
- package/dist/content-contracts/skills/greptile-detector.js +202 -4
- package/dist/decision/index.d.ts +17 -0
- package/dist/decision/index.js +35 -0
- package/dist/decision/list.d.ts +47 -0
- package/dist/decision/list.js +250 -0
- package/dist/decision/schema.d.ts +88 -0
- package/dist/decision/schema.js +293 -0
- package/dist/decision/write.d.ts +82 -0
- package/dist/decision/write.js +427 -0
- package/dist/doctor/checks.d.ts +44 -0
- package/dist/doctor/checks.js +428 -2
- package/dist/doctor/main.d.ts +6 -7
- package/dist/doctor/main.js +72 -79
- package/dist/doctor/which.d.ts +20 -1
- package/dist/doctor/which.js +67 -1
- package/dist/eval/report.d.ts +29 -0
- package/dist/eval/report.js +69 -0
- package/dist/eval/run.d.ts +9 -0
- package/dist/eval/run.js +40 -4
- package/dist/eval/version-pin.d.ts +99 -0
- package/dist/eval/version-pin.js +181 -0
- 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 +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/agent-hooks.d.ts +20 -0
- package/dist/init-deposit/agent-hooks.js +51 -31
- package/dist/intake/issue-ingest.js +29 -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/platform/host-content-surface.d.ts +74 -0
- package/dist/platform/host-content-surface.js +214 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/policy/ceremony-dial.d.ts +289 -0
- package/dist/policy/ceremony-dial.js +980 -0
- package/dist/policy/deft-directive-disable.js +12 -2
- 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/evaluate.js +10 -0
- 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/mergeability.js +5 -0
- package/dist/pr-merge-readiness/output.js +2 -0
- package/dist/pr-merge-readiness/parse.js +4 -0
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
- package/dist/pr-merge-readiness/types.d.ts +6 -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/render/framework-commands.js +4 -0
- package/dist/scope/acceptance-evidence.d.ts +76 -0
- package/dist/scope/acceptance-evidence.js +348 -0
- package/dist/scope/coverage-map.d.ts +98 -0
- package/dist/scope/coverage-map.js +558 -0
- package/dist/scope/decompose.js +125 -11
- package/dist/scope/effort-activate-gate.d.ts +28 -0
- package/dist/scope/effort-activate-gate.js +64 -0
- package/dist/scope/index.d.ts +4 -0
- package/dist/scope/index.js +4 -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 +69 -2
- package/dist/session/active-cli.d.ts +79 -0
- package/dist/session/active-cli.js +382 -0
- package/dist/session/effort-budget.d.ts +130 -0
- package/dist/session/effort-budget.js +334 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/session-start.d.ts +30 -1
- package/dist/session/session-start.js +234 -26
- 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/swarm/index.d.ts +2 -0
- package/dist/swarm/index.js +2 -0
- package/dist/swarm/pre-dispatch-cli.d.ts +19 -0
- package/dist/swarm/pre-dispatch-cli.js +143 -0
- package/dist/swarm/pre-dispatch.d.ts +87 -0
- package/dist/swarm/pre-dispatch.js +373 -0
- package/dist/triage/actions/candidates-log.d.ts +1 -2
- package/dist/triage/actions/candidates-log.js +37 -6
- package/dist/vbrief-activate/activate.js +6 -0
- package/dist/vbrief-validate/constants.d.ts +2 -0
- package/dist/vbrief-validate/constants.js +2 -0
- package/dist/vbrief-validate/schema.js +4 -1
- package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
- package/dist/xbrief-migrate/migrate-project.js +92 -3
- package/package.json +19 -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,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host content-surface class + managed-section drift (#3162).
|
|
3
|
+
*
|
|
4
|
+
* Directive file gates and #830/#2508 pins assume filesystem-visible work product
|
|
5
|
+
* and a host that does not rewrite constitution mid-run. REPL-first and
|
|
6
|
+
* self-mutating hosts break those assumptions. This module classifies the host
|
|
7
|
+
* surface (capability pointer #1461/#1357) and reports managed AGENTS section
|
|
8
|
+
* drift without inventing a full pin product.
|
|
9
|
+
*
|
|
10
|
+
* Never blocks session-start — advisory only.
|
|
11
|
+
*/
|
|
12
|
+
import { type AgentsMdSeams } from "./agents-md.js";
|
|
13
|
+
/** Host content-surface classes for gate / pin honesty (#3162). */
|
|
14
|
+
export declare const HOST_CONTENT_SURFACE_CLASSES: readonly ["file-first", "repl-first", "self-mutating", "unknown"];
|
|
15
|
+
export type HostContentSurfaceClass = (typeof HOST_CONTENT_SURFACE_CLASSES)[number];
|
|
16
|
+
/** Env key for explicit class override (capability descriptor pointer). */
|
|
17
|
+
export declare const ENV_HOST_CONTENT_SURFACE = "DEFT_HOST_CONTENT_SURFACE";
|
|
18
|
+
/** Env opt-in: host holds executable work product before files exist. */
|
|
19
|
+
export declare const ENV_HOST_REPL_FIRST = "DEFT_HOST_REPL_FIRST";
|
|
20
|
+
/** Env opt-in: host refine/kernel may CRUD skills/prompts mid-run. */
|
|
21
|
+
export declare const ENV_HOST_SELF_MUTATE = "DEFT_HOST_SELF_MUTATE";
|
|
22
|
+
export interface HostContentSurfaceClassResult {
|
|
23
|
+
readonly contentClass: HostContentSurfaceClass;
|
|
24
|
+
readonly source: string;
|
|
25
|
+
/** Signals observed (env keys present). */
|
|
26
|
+
readonly signals: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export type ManagedSectionDriftState = "current" | "stale" | "missing" | "absent" | "unreadable" | "template-missing" | "template-malformed" | "unknown";
|
|
29
|
+
export interface ManagedSectionDriftReport {
|
|
30
|
+
readonly state: ManagedSectionDriftState;
|
|
31
|
+
readonly embeddedSha: string | null;
|
|
32
|
+
/** SHA-256 (12 hex) of stripped managed-section body when readable. */
|
|
33
|
+
readonly bodyHash: string | null;
|
|
34
|
+
readonly path: string;
|
|
35
|
+
}
|
|
36
|
+
export interface HostContentSurfaceReport {
|
|
37
|
+
readonly contentClass: HostContentSurfaceClass;
|
|
38
|
+
readonly classSource: string;
|
|
39
|
+
readonly signals: readonly string[];
|
|
40
|
+
readonly managedSection: ManagedSectionDriftReport;
|
|
41
|
+
/** Runtime mode from platform capability descriptor when provided. */
|
|
42
|
+
readonly runtimeMode: string | null;
|
|
43
|
+
}
|
|
44
|
+
export interface HostContentSurfaceSeams {
|
|
45
|
+
readonly environ?: Readonly<Record<string, string | undefined>>;
|
|
46
|
+
readonly agentsMdSeams?: AgentsMdSeams;
|
|
47
|
+
readonly runtimeMode?: string | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Classify host content surface from explicit env / capability signals (#1461/#1357 pointer).
|
|
51
|
+
* Defaults to file-first (historical Directive assumption) when no signal is set.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyHostContentSurface(environ?: Readonly<Record<string, string | undefined>>): HostContentSurfaceClassResult;
|
|
54
|
+
/**
|
|
55
|
+
* Probe managed AGENTS.md section drift (tamper-evident boundary, not full #830 pins).
|
|
56
|
+
* Uses agents:refresh plan state + embedded marker sha + body hash.
|
|
57
|
+
*/
|
|
58
|
+
export declare function probeManagedSectionDrift(projectRoot: string, seams?: HostContentSurfaceSeams): ManagedSectionDriftReport;
|
|
59
|
+
/** Full host-surface report for session-start payload + lines. */
|
|
60
|
+
export declare function probeHostContentSurface(projectRoot: string, seams?: HostContentSurfaceSeams): HostContentSurfaceReport;
|
|
61
|
+
export declare function hostContentSurfaceToDict(report: HostContentSurfaceReport): Record<string, unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* Format operator-facing host-surface lines (#3162).
|
|
64
|
+
* Always emits one summary line; adds honesty note for non-file-first or drift.
|
|
65
|
+
*/
|
|
66
|
+
export declare function formatHostContentSurfaceLines(report: HostContentSurfaceReport): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Probe + format; fail-open for session-start (never throws to callers that wrap).
|
|
69
|
+
*/
|
|
70
|
+
export declare function maybeFormatHostContentSurfaceLines(projectRoot: string, seams?: HostContentSurfaceSeams): {
|
|
71
|
+
report: HostContentSurfaceReport;
|
|
72
|
+
lines: string[];
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=host-content-surface.d.ts.map
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host content-surface class + managed-section drift (#3162).
|
|
3
|
+
*
|
|
4
|
+
* Directive file gates and #830/#2508 pins assume filesystem-visible work product
|
|
5
|
+
* and a host that does not rewrite constitution mid-run. REPL-first and
|
|
6
|
+
* self-mutating hosts break those assumptions. This module classifies the host
|
|
7
|
+
* surface (capability pointer #1461/#1357) and reports managed AGENTS section
|
|
8
|
+
* drift without inventing a full pin product.
|
|
9
|
+
*
|
|
10
|
+
* Never blocks session-start — advisory only.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { agentsRefreshPlan, extractManagedSection, parseManagedSectionAttrs, stripManagedSectionAttrs, } from "./agents-md.js";
|
|
16
|
+
/** Host content-surface classes for gate / pin honesty (#3162). */
|
|
17
|
+
export const HOST_CONTENT_SURFACE_CLASSES = [
|
|
18
|
+
"file-first",
|
|
19
|
+
"repl-first",
|
|
20
|
+
"self-mutating",
|
|
21
|
+
"unknown",
|
|
22
|
+
];
|
|
23
|
+
/** Env key for explicit class override (capability descriptor pointer). */
|
|
24
|
+
export const ENV_HOST_CONTENT_SURFACE = "DEFT_HOST_CONTENT_SURFACE";
|
|
25
|
+
/** Env opt-in: host holds executable work product before files exist. */
|
|
26
|
+
export const ENV_HOST_REPL_FIRST = "DEFT_HOST_REPL_FIRST";
|
|
27
|
+
/** Env opt-in: host refine/kernel may CRUD skills/prompts mid-run. */
|
|
28
|
+
export const ENV_HOST_SELF_MUTATE = "DEFT_HOST_SELF_MUTATE";
|
|
29
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
30
|
+
function envTruthy(value) {
|
|
31
|
+
return TRUTHY.has((value ?? "").trim().toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
function normalizeClassToken(raw) {
|
|
34
|
+
const token = raw.trim().toLowerCase().replace(/_/g, "-");
|
|
35
|
+
if (HOST_CONTENT_SURFACE_CLASSES.includes(token)) {
|
|
36
|
+
return token;
|
|
37
|
+
}
|
|
38
|
+
// Accept aliases used in host docs.
|
|
39
|
+
if (token === "file" || token === "filesystem" || token === "fs-first")
|
|
40
|
+
return "file-first";
|
|
41
|
+
if (token === "repl" || token === "kernel-first")
|
|
42
|
+
return "repl-first";
|
|
43
|
+
if (token === "self-edit" || token === "self-editing" || token === "continual-harness") {
|
|
44
|
+
return "self-mutating";
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Classify host content surface from explicit env / capability signals (#1461/#1357 pointer).
|
|
50
|
+
* Defaults to file-first (historical Directive assumption) when no signal is set.
|
|
51
|
+
*/
|
|
52
|
+
export function classifyHostContentSurface(environ = process.env) {
|
|
53
|
+
const signals = [];
|
|
54
|
+
const explicit = (environ[ENV_HOST_CONTENT_SURFACE] ?? "").trim();
|
|
55
|
+
if (explicit) {
|
|
56
|
+
signals.push(ENV_HOST_CONTENT_SURFACE);
|
|
57
|
+
const parsed = normalizeClassToken(explicit);
|
|
58
|
+
if (parsed !== null) {
|
|
59
|
+
return {
|
|
60
|
+
contentClass: parsed,
|
|
61
|
+
source: `env:${ENV_HOST_CONTENT_SURFACE}`,
|
|
62
|
+
signals,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
contentClass: "unknown",
|
|
67
|
+
source: `env:${ENV_HOST_CONTENT_SURFACE}:unrecognized`,
|
|
68
|
+
signals,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const selfMutate = envTruthy(environ[ENV_HOST_SELF_MUTATE]);
|
|
72
|
+
const replFirst = envTruthy(environ[ENV_HOST_REPL_FIRST]);
|
|
73
|
+
if (selfMutate)
|
|
74
|
+
signals.push(ENV_HOST_SELF_MUTATE);
|
|
75
|
+
if (replFirst)
|
|
76
|
+
signals.push(ENV_HOST_REPL_FIRST);
|
|
77
|
+
// Self-mutating supersedes REPL-first when both are set (broader honesty surface).
|
|
78
|
+
if (selfMutate) {
|
|
79
|
+
return {
|
|
80
|
+
contentClass: "self-mutating",
|
|
81
|
+
source: `env:${ENV_HOST_SELF_MUTATE}`,
|
|
82
|
+
signals,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (replFirst) {
|
|
86
|
+
return {
|
|
87
|
+
contentClass: "repl-first",
|
|
88
|
+
source: `env:${ENV_HOST_REPL_FIRST}`,
|
|
89
|
+
signals,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
contentClass: "file-first",
|
|
94
|
+
source: "assumed",
|
|
95
|
+
signals,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function hashBody(text) {
|
|
99
|
+
return createHash("sha256").update(text, "utf8").digest("hex").slice(0, 12);
|
|
100
|
+
}
|
|
101
|
+
function mapPlanState(raw) {
|
|
102
|
+
if (typeof raw !== "string")
|
|
103
|
+
return "unknown";
|
|
104
|
+
switch (raw) {
|
|
105
|
+
case "current":
|
|
106
|
+
case "stale":
|
|
107
|
+
case "missing":
|
|
108
|
+
case "absent":
|
|
109
|
+
case "unreadable":
|
|
110
|
+
case "template-missing":
|
|
111
|
+
case "template-malformed":
|
|
112
|
+
return raw;
|
|
113
|
+
default:
|
|
114
|
+
return "unknown";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Probe managed AGENTS.md section drift (tamper-evident boundary, not full #830 pins).
|
|
119
|
+
* Uses agents:refresh plan state + embedded marker sha + body hash.
|
|
120
|
+
*/
|
|
121
|
+
export function probeManagedSectionDrift(projectRoot, seams = {}) {
|
|
122
|
+
const agentsPath = join(projectRoot, "AGENTS.md");
|
|
123
|
+
const plan = agentsRefreshPlan(projectRoot, seams.agentsMdSeams ?? {});
|
|
124
|
+
const state = mapPlanState(plan.state);
|
|
125
|
+
let embeddedSha = null;
|
|
126
|
+
let bodyHash = null;
|
|
127
|
+
const readAgents = seams.agentsMdSeams?.readAgents ??
|
|
128
|
+
((path) => {
|
|
129
|
+
try {
|
|
130
|
+
if (!existsSync(path))
|
|
131
|
+
return null;
|
|
132
|
+
return readFileSync(path, "utf8");
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
const text = readAgents(agentsPath);
|
|
140
|
+
if (text !== null) {
|
|
141
|
+
const extracted = extractManagedSection(text);
|
|
142
|
+
if (extracted !== null) {
|
|
143
|
+
const attrs = parseManagedSectionAttrs(extracted);
|
|
144
|
+
embeddedSha = attrs?.sha ?? null;
|
|
145
|
+
bodyHash = hashBody(stripManagedSectionAttrs(extracted));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// body hash is best-effort
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
state,
|
|
154
|
+
embeddedSha,
|
|
155
|
+
bodyHash,
|
|
156
|
+
path: typeof plan.path === "string" ? plan.path : agentsPath,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Full host-surface report for session-start payload + lines. */
|
|
160
|
+
export function probeHostContentSurface(projectRoot, seams = {}) {
|
|
161
|
+
const environ = seams.environ ?? process.env;
|
|
162
|
+
const classified = classifyHostContentSurface(environ);
|
|
163
|
+
const managedSection = probeManagedSectionDrift(projectRoot, seams);
|
|
164
|
+
return {
|
|
165
|
+
contentClass: classified.contentClass,
|
|
166
|
+
classSource: classified.source,
|
|
167
|
+
signals: classified.signals,
|
|
168
|
+
managedSection,
|
|
169
|
+
runtimeMode: seams.runtimeMode ?? null,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function hostContentSurfaceToDict(report) {
|
|
173
|
+
return {
|
|
174
|
+
content_class: report.contentClass,
|
|
175
|
+
class_source: report.classSource,
|
|
176
|
+
signals: [...report.signals],
|
|
177
|
+
runtime_mode: report.runtimeMode,
|
|
178
|
+
managed_section: {
|
|
179
|
+
state: report.managedSection.state,
|
|
180
|
+
embedded_sha: report.managedSection.embeddedSha,
|
|
181
|
+
body_hash: report.managedSection.bodyHash,
|
|
182
|
+
path: report.managedSection.path,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Format operator-facing host-surface lines (#3162).
|
|
188
|
+
* Always emits one summary line; adds honesty note for non-file-first or drift.
|
|
189
|
+
*/
|
|
190
|
+
export function formatHostContentSurfaceLines(report) {
|
|
191
|
+
const managed = report.managedSection.state;
|
|
192
|
+
const summary = `[deft host-surface] class=${report.contentClass} source=${report.classSource} ` +
|
|
193
|
+
`managed=${managed}` +
|
|
194
|
+
(report.managedSection.embeddedSha ? ` sha=${report.managedSection.embeddedSha}` : "") +
|
|
195
|
+
(report.managedSection.bodyHash ? ` body=${report.managedSection.bodyHash}` : "");
|
|
196
|
+
const lines = [summary];
|
|
197
|
+
if (report.contentClass === "repl-first" || report.contentClass === "self-mutating") {
|
|
198
|
+
lines.push("[deft host-surface] honesty: file gates and agent-only pins do not see host-kernel " +
|
|
199
|
+
"or mid-run host refine work product — see content/docs/host-surface-assumptions.md (#3162)");
|
|
200
|
+
}
|
|
201
|
+
if (managed === "stale" || managed === "missing" || managed === "absent") {
|
|
202
|
+
lines.push("[deft host-surface] managed AGENTS section drift — run `deft agents:refresh` " +
|
|
203
|
+
"(or `task agents:refresh`) so pins/managed constitution match the deposit");
|
|
204
|
+
}
|
|
205
|
+
return lines;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Probe + format; fail-open for session-start (never throws to callers that wrap).
|
|
209
|
+
*/
|
|
210
|
+
export function maybeFormatHostContentSurfaceLines(projectRoot, seams = {}) {
|
|
211
|
+
const report = probeHostContentSurface(projectRoot, seams);
|
|
212
|
+
return { report, lines: formatHostContentSurfaceLines(report) };
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=host-content-surface.js.map
|
package/dist/platform/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./agents-consumer-header.js";
|
|
2
2
|
export * from "./agents-md.js";
|
|
3
3
|
export * from "./constants.js";
|
|
4
|
+
export * from "./host-content-surface.js";
|
|
4
5
|
export * from "./ip-risk.js";
|
|
5
6
|
export * from "./linear-scan.js";
|
|
6
7
|
export * from "./platform-capabilities.js";
|
package/dist/platform/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./agents-consumer-header.js";
|
|
2
2
|
export * from "./agents-md.js";
|
|
3
3
|
export * from "./constants.js";
|
|
4
|
+
export * from "./host-content-surface.js";
|
|
4
5
|
export * from "./ip-risk.js";
|
|
5
6
|
export * from "./linear-scan.js";
|
|
6
7
|
export * from "./platform-capabilities.js";
|