crewhaus 0.2.0 → 0.2.1
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/dataset-mine.d.ts +3 -2
- package/dist/dataset-mine.js +3 -2
- package/dist/index.js +72 -8
- package/dist/route.d.ts +19 -0
- package/dist/route.js +90 -0
- package/dist/security-digest.d.ts +2 -1
- package/dist/security-digest.js +18 -12
- package/dist/tools-cli.js +2 -0
- package/package.json +89 -88
package/dist/dataset-mine.d.ts
CHANGED
|
@@ -102,8 +102,9 @@ export type EgressBlock = {
|
|
|
102
102
|
};
|
|
103
103
|
/**
|
|
104
104
|
* Extract egress-block candidates from parsed audit records. The audit log MAY
|
|
105
|
-
* carry no `egress_decision` records at all (
|
|
106
|
-
* in that case. A block is any record whose verdict
|
|
105
|
+
* carry no `egress_decision` records at all (they are written only on warn/block
|
|
106
|
+
* verdicts) — this returns [] in that case. A block is any record whose verdict
|
|
107
|
+
* is not an allow. Because
|
|
107
108
|
* the audit payload does not reliably carry a turn input, these become
|
|
108
109
|
* standalone candidates keyed by a best-effort session id + the lineage
|
|
109
110
|
* summary; the CLI attaches them to the matching session's LAST turn input
|
package/dist/dataset-mine.js
CHANGED
|
@@ -173,8 +173,9 @@ export function mineSession(sessionId, events) {
|
|
|
173
173
|
}
|
|
174
174
|
/**
|
|
175
175
|
* Extract egress-block candidates from parsed audit records. The audit log MAY
|
|
176
|
-
* carry no `egress_decision` records at all (
|
|
177
|
-
* in that case. A block is any record whose verdict
|
|
176
|
+
* carry no `egress_decision` records at all (they are written only on warn/block
|
|
177
|
+
* verdicts) — this returns [] in that case. A block is any record whose verdict
|
|
178
|
+
* is not an allow. Because
|
|
178
179
|
* the audit payload does not reliably carry a turn input, these become
|
|
179
180
|
* standalone candidates keyed by a best-effort session id + the lineage
|
|
180
181
|
* summary; the CLI attaches them to the matching session's LAST turn input
|
package/dist/index.js
CHANGED
|
@@ -260,6 +260,9 @@ import { RetireError, buildRetirementPlan, formatPlan, formatRetirementResult, r
|
|
|
260
260
|
// Item 25 — model right-sizing downshift search core (pure enumeration + cost
|
|
261
261
|
// projection + $/score ranking); side-effect-free so it is unit-testable.
|
|
262
262
|
import { buildRightSizeReport, enumerateSlotCandidates, } from "./right-size";
|
|
263
|
+
// Adaptive model routing — `crewhaus route status|reset` inspects/clears the
|
|
264
|
+
// per-(routeKey, model) reward scoreboard behind `agent.model_pool`.
|
|
265
|
+
import { runRoute } from "./route";
|
|
263
266
|
// Item 13 — `crewhaus scaffold-evals` + `init --with-evals`: day-one eval
|
|
264
267
|
// assets generated from the spec (deterministic template / one-shot model
|
|
265
268
|
// sample stubs + a single spec-goal llm_judge or floor grader), in a
|
|
@@ -453,6 +456,12 @@ const INIT_SCHEMA = {
|
|
|
453
456
|
// Overwrite an existing scaffolded workflow or eval assets (never the
|
|
454
457
|
// spec).
|
|
455
458
|
{ name: "force", takesValue: false },
|
|
459
|
+
// Item 39 — interactive spec authoring: interview the user (via the
|
|
460
|
+
// resolved model, or a scripted stdin questionnaire when no credentials)
|
|
461
|
+
// and emit a parseSpec-validated crewhaus.yaml. Composes with --detect to
|
|
462
|
+
// prefill the model from what's reachable.
|
|
463
|
+
{ name: "interactive", short: "i", takesValue: false },
|
|
464
|
+
{ name: "detect", takesValue: false },
|
|
456
465
|
{ name: "help", short: "h" },
|
|
457
466
|
],
|
|
458
467
|
};
|
|
@@ -491,12 +500,6 @@ const GRADERS_SUGGEST_SCHEMA = {
|
|
|
491
500
|
{ name: "min-score", takesValue: true },
|
|
492
501
|
// Overwrite an existing review file (default: refuse).
|
|
493
502
|
{ name: "force", takesValue: false },
|
|
494
|
-
// Item 39 — interactive spec authoring: interview the user (via the
|
|
495
|
-
// resolved model, or a scripted stdin questionnaire when no credentials)
|
|
496
|
-
// and emit a parseSpec-validated crewhaus.yaml. Composes with --detect to
|
|
497
|
-
// prefill the model from what's reachable.
|
|
498
|
-
{ name: "interactive", short: "i", takesValue: false },
|
|
499
|
-
{ name: "detect", takesValue: false },
|
|
500
503
|
{ name: "help", short: "h" },
|
|
501
504
|
],
|
|
502
505
|
};
|
|
@@ -529,6 +532,17 @@ const DOCTOR_SCHEMA = {
|
|
|
529
532
|
// Container-HEALTHCHECK exit semantics (0 within/no-data, 1 breach).
|
|
530
533
|
{ name: "slo", takesValue: false },
|
|
531
534
|
{ name: "ttft", takesValue: false },
|
|
535
|
+
// Item 40 — `--detect`: read-only inventory of reachable providers, the
|
|
536
|
+
// local Ollama/vLLM endpoint's models, and MCP servers from
|
|
537
|
+
// .mcp.json / Claude Desktop config. `--no-probe` skips the localhost HTTP
|
|
538
|
+
// probe (offline / CI).
|
|
539
|
+
{ name: "detect", takesValue: false },
|
|
540
|
+
{ name: "no-probe", takesValue: false },
|
|
541
|
+
// Item 40 — `--fix`: apply the mechanical remediations doctor otherwise
|
|
542
|
+
// only prints (scaffold crewhaus.yaml, create .crewhaus/, mark outward
|
|
543
|
+
// tools scope:external, append commented .env stubs). Dry-run is the
|
|
544
|
+
// DEFAULT: without --fix, doctor prints the diff it WOULD apply.
|
|
545
|
+
{ name: "fix", takesValue: false },
|
|
532
546
|
{ name: "help", short: "h" },
|
|
533
547
|
],
|
|
534
548
|
};
|
|
@@ -1343,7 +1357,7 @@ function usageText() {
|
|
|
1343
1357
|
" [--watch] re-validate on change (authoring aid; does not re-launch)",
|
|
1344
1358
|
" [--resume <id>] resume a specific session (cli targets only)",
|
|
1345
1359
|
" [--continue] resume the most-recent session (cli targets only)",
|
|
1346
|
-
" [--prompt <text>]
|
|
1360
|
+
" [--prompt <text>] run one turn and exit, printing the reply (cli: no REPL; browser: the single-turn input, else stdin)",
|
|
1347
1361
|
" [--justification-judge rule-based|claude] Pillar 3 intent-gate judge (FR-004)",
|
|
1348
1362
|
" [--egress-matcher substring|semantic] Pillar 3 sink-side matcher (FR-006)",
|
|
1349
1363
|
" [--egress-embedder <model>] embedder for --egress-matcher semantic",
|
|
@@ -1410,6 +1424,8 @@ function usageText() {
|
|
|
1410
1424
|
" context --bundle [-o <file>] emit a single-markdown orientation manifest",
|
|
1411
1425
|
" [--factory-root <p>] [--docs-root <p>] [--demos-root <p>]",
|
|
1412
1426
|
" cost-summary --session <id> summarize cost_accrual events for a session",
|
|
1427
|
+
" route status [--dir <root>] show the adaptive model_pool reward scoreboard",
|
|
1428
|
+
" route reset [--dir <root>] wipe the scoreboard (default root .crewhaus)",
|
|
1413
1429
|
" advise [--session <id> | --all] mine session logs for spec advice (item 14)",
|
|
1414
1430
|
" [--json] [-o <dir>] writes suggestions.json + report.html (default .crewhaus/advice)",
|
|
1415
1431
|
" tools list list every builtin tool + its metadata (item 18)",
|
|
@@ -2709,6 +2725,8 @@ async function loadToolMap() {
|
|
|
2709
2725
|
glob: fs.glob,
|
|
2710
2726
|
grep: fs.grep,
|
|
2711
2727
|
bash: bash.bash,
|
|
2728
|
+
bashOutput: bash.bashOutput,
|
|
2729
|
+
killShell: bash.killShell,
|
|
2712
2730
|
todoWrite: todo.todoWrite,
|
|
2713
2731
|
webFetch: web.webFetch,
|
|
2714
2732
|
webSearch: web.webSearch,
|
|
@@ -2887,6 +2905,7 @@ async function resolveEgressMatcher(args, irEgressMatcher) {
|
|
|
2887
2905
|
async function runRun(args) {
|
|
2888
2906
|
if (args.flags["help"]) {
|
|
2889
2907
|
process.stdout.write("usage: crewhaus run <spec.yaml> [--model <model>] [--permission-mode <default|plan|auto|bypass>] [--resume <sessionId> | --continue] [--prompt <text>] [--justification-judge rule-based|claude] [--egress-matcher substring|semantic] [--egress-embedder <model>]\n" +
|
|
2908
|
+
" --prompt <text> runs a single turn non-interactively and prints the reply, then exits (no REPL) — for scripting/CI; composes with --resume/--continue\n" +
|
|
2890
2909
|
" --model accepts the full router grammar: claude-* (Anthropic), openai/<m>, gemini/<m>, bedrock/<id> (geo prefixes tolerated), local/<m>@<url>\n" +
|
|
2891
2910
|
" A spec with a feedback: block asks `rate this session? [g]ood / [b]ad / [enter] skip`\n" +
|
|
2892
2911
|
" on clean REPL exit (one keystroke, 10s timeout, TTY only — never when piped). Opt out\n" +
|
|
@@ -2962,6 +2981,19 @@ async function runRunCli(args, ir, specPath) {
|
|
|
2962
2981
|
resumeId = match.id;
|
|
2963
2982
|
process.stdout.write(`[continue] resuming session ${match.id} (last updated ${match.updatedAt})\n`);
|
|
2964
2983
|
}
|
|
2984
|
+
// `--prompt <text>` runs ONE turn non-interactively and exits (no REPL) —
|
|
2985
|
+
// the cli analogue of the browser target's --prompt and `claude -p`, for
|
|
2986
|
+
// scripting/CI/pipelines. The final assistant message is written to stdout.
|
|
2987
|
+
// Composes with --resume/--continue (one more turn on a resumed session).
|
|
2988
|
+
const promptFlag = args.flags["prompt"];
|
|
2989
|
+
let oneShotPrompt;
|
|
2990
|
+
if (typeof promptFlag === "string") {
|
|
2991
|
+
const trimmed = promptFlag.trim();
|
|
2992
|
+
if (trimmed.length === 0) {
|
|
2993
|
+
die("--prompt requires non-empty text");
|
|
2994
|
+
}
|
|
2995
|
+
oneShotPrompt = trimmed;
|
|
2996
|
+
}
|
|
2965
2997
|
let tools = [];
|
|
2966
2998
|
if (ir.tools.length > 0) {
|
|
2967
2999
|
// Section 14 — apply per-tool config (e.g. registerFetchConfig) before
|
|
@@ -3356,8 +3388,9 @@ async function runRunCli(args, ir, specPath) {
|
|
|
3356
3388
|
process.stdout.write(`[sandbox] backend "${process.env["CREWHAUS_SANDBOX"]}" — python/javascript/shell enabled (still require an alwaysAllow rule)\n`);
|
|
3357
3389
|
}
|
|
3358
3390
|
}
|
|
3391
|
+
let oneShotResult;
|
|
3359
3392
|
try {
|
|
3360
|
-
await runChatLoop({
|
|
3393
|
+
oneShotResult = await runChatLoop({
|
|
3361
3394
|
model,
|
|
3362
3395
|
instructions: mcpQuarantineNotice !== undefined
|
|
3363
3396
|
? `${ir.agent.instructions}\n\n${mcpQuarantineNotice}`
|
|
@@ -3370,11 +3403,24 @@ async function runRunCli(args, ir, specPath) {
|
|
|
3370
3403
|
hooks,
|
|
3371
3404
|
skills,
|
|
3372
3405
|
slashCommands,
|
|
3406
|
+
// `--prompt` → run exactly one turn from the seeded prompt and return
|
|
3407
|
+
// its final message (no interactive REPL). runChatLoop's singleTurn
|
|
3408
|
+
// mode also suppresses the `you>` / `agent ready` chrome.
|
|
3409
|
+
...(oneShotPrompt !== undefined
|
|
3410
|
+
? { singleTurn: true, seedMessages: [{ role: "user", content: oneShotPrompt }] }
|
|
3411
|
+
: {}),
|
|
3373
3412
|
...(hasCodeExecTools ? { sandboxAvailable } : {}),
|
|
3374
3413
|
...(subAgents !== undefined ? { subAgents, spawnSubAgent } : {}),
|
|
3375
3414
|
...(ir.target === "cli" && ir.agent.maxTokens !== undefined
|
|
3376
3415
|
? { maxTokens: ir.agent.maxTokens }
|
|
3377
3416
|
: {}),
|
|
3417
|
+
// Thread `compaction.model` so auto-compaction summarizes on the
|
|
3418
|
+
// spec's chosen (typically cheaper) model instead of the primary.
|
|
3419
|
+
// The compiler already resolves the `cheapest` sentinel to a concrete
|
|
3420
|
+
// id, so this is a raw model string. Mirrors the target-cli emitter.
|
|
3421
|
+
...(ir.target === "cli" && ir.compaction.model !== undefined
|
|
3422
|
+
? { compactionModel: ir.compaction.model }
|
|
3423
|
+
: {}),
|
|
3378
3424
|
// Item 22 — provider failover chain: thread `agent.model_fallbacks` +
|
|
3379
3425
|
// `agent.circuit_breaker` through to the runtime, mirroring the
|
|
3380
3426
|
// target-cli codegen path. Skipped when `--model` overrides the
|
|
@@ -3428,6 +3474,12 @@ async function runRunCli(args, ir, specPath) {
|
|
|
3428
3474
|
if (mcpHost)
|
|
3429
3475
|
await mcpHost.disconnectAll();
|
|
3430
3476
|
}
|
|
3477
|
+
// One-shot (`--prompt`): emit the final assistant message to stdout so it
|
|
3478
|
+
// can be piped/captured. The REPL path streams as it goes and returns the
|
|
3479
|
+
// same trailing text, so only print it in one-shot mode.
|
|
3480
|
+
if (oneShotPrompt !== undefined && oneShotResult !== undefined) {
|
|
3481
|
+
process.stdout.write(`${oneShotResult}\n`);
|
|
3482
|
+
}
|
|
3431
3483
|
// Item 1 — post-session feedback teardown: the one-keystroke exit rating
|
|
3432
3484
|
// prompt (TTY only) and the feedback.autoDistill consumer. Runs only on a
|
|
3433
3485
|
// clean REPL exit (runChatLoop returned; a throw above skips it) and is
|
|
@@ -12949,6 +13001,18 @@ switch (subcommand) {
|
|
|
12949
13001
|
}
|
|
12950
13002
|
break;
|
|
12951
13003
|
}
|
|
13004
|
+
case "route":
|
|
13005
|
+
// Adaptive model routing — inspect/reset the reward scoreboard. `runRoute`
|
|
13006
|
+
// throws a plain Error on a bad argument; surface it through die().
|
|
13007
|
+
try {
|
|
13008
|
+
process.stdout.write(`${runRoute(rest)}\n`);
|
|
13009
|
+
}
|
|
13010
|
+
catch (err) {
|
|
13011
|
+
if (err instanceof Error)
|
|
13012
|
+
die(err.message);
|
|
13013
|
+
throw err;
|
|
13014
|
+
}
|
|
13015
|
+
break;
|
|
12952
13016
|
case "context":
|
|
12953
13017
|
runContext(parseFor(rest, CONTEXT_SCHEMA));
|
|
12954
13018
|
break;
|
package/dist/route.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ArmStats } from "@crewhaus/routing-store";
|
|
2
|
+
export type RouteArgs = {
|
|
3
|
+
readonly sub: "status" | "reset";
|
|
4
|
+
readonly dir: string;
|
|
5
|
+
};
|
|
6
|
+
/** Parse `route <sub> [--dir <root>]` argv (everything after `route`). */
|
|
7
|
+
export declare function parseRouteArgs(argv: readonly string[]): RouteArgs;
|
|
8
|
+
/** Snapshot the scoreboard arms (sorted routeKey then model). */
|
|
9
|
+
export declare function loadArms(rootDir: string): ArmStats[];
|
|
10
|
+
/**
|
|
11
|
+
* Render the scoreboard as a human table: grouped by routeKey, best mean-reward
|
|
12
|
+
* arm first within each bucket and starred (the arm a `learned` policy would
|
|
13
|
+
* exploit once every arm clears its sample floor).
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatRouteStatus(arms: readonly ArmStats[]): string;
|
|
16
|
+
/** Wipe the whole scoreboard. Returns the number of arms removed. */
|
|
17
|
+
export declare function resetRouting(rootDir: string): number;
|
|
18
|
+
/** Run `crewhaus route …`, returning the text to print. */
|
|
19
|
+
export declare function runRoute(argv: readonly string[]): string;
|
package/dist/route.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `crewhaus route` — inspect and reset the adaptive-model-routing scoreboard
|
|
3
|
+
* (the durable per-`(routeKey, model)` reward store behind `agent.model_pool`).
|
|
4
|
+
*
|
|
5
|
+
* route status [--dir <root>] show the learned arms, best-per-bucket first
|
|
6
|
+
* route reset [--dir <root>] wipe the scoreboard (kill switch)
|
|
7
|
+
*
|
|
8
|
+
* `--dir` points at the `.crewhaus` root (default `.crewhaus`); the store lives
|
|
9
|
+
* at `<root>/routing/arms.jsonl`. Live per-turn `model_route` decisions are
|
|
10
|
+
* already visible in a run's structured trace; this command surfaces the
|
|
11
|
+
* ACCUMULATED learning that a `learned` policy exploits.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, rmSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { openScoreboard } from "@crewhaus/routing-store";
|
|
16
|
+
const DEFAULT_ROOT = ".crewhaus";
|
|
17
|
+
/** Parse `route <sub> [--dir <root>]` argv (everything after `route`). */
|
|
18
|
+
export function parseRouteArgs(argv) {
|
|
19
|
+
let dir = DEFAULT_ROOT;
|
|
20
|
+
let sub;
|
|
21
|
+
for (let i = 0; i < argv.length; i++) {
|
|
22
|
+
const a = argv[i];
|
|
23
|
+
if (a === "--dir") {
|
|
24
|
+
const v = argv[i + 1];
|
|
25
|
+
if (v === undefined)
|
|
26
|
+
throw new Error("route: --dir requires a path");
|
|
27
|
+
dir = v;
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
else if (a === "status" || a === "reset") {
|
|
31
|
+
sub = a;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new Error(`route: unknown argument "${a}" (expected: status | reset [--dir <root>])`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (sub === undefined)
|
|
38
|
+
throw new Error("route: expected a subcommand (status | reset)");
|
|
39
|
+
return { sub, dir };
|
|
40
|
+
}
|
|
41
|
+
/** Snapshot the scoreboard arms (sorted routeKey then model). */
|
|
42
|
+
export function loadArms(rootDir) {
|
|
43
|
+
return openScoreboard(rootDir).snapshot();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Render the scoreboard as a human table: grouped by routeKey, best mean-reward
|
|
47
|
+
* arm first within each bucket and starred (the arm a `learned` policy would
|
|
48
|
+
* exploit once every arm clears its sample floor).
|
|
49
|
+
*/
|
|
50
|
+
export function formatRouteStatus(arms) {
|
|
51
|
+
if (arms.length === 0) {
|
|
52
|
+
return "No routing data yet. Run a harness whose spec declares `agent.model_pool` to accumulate arms.";
|
|
53
|
+
}
|
|
54
|
+
const byKey = new Map();
|
|
55
|
+
for (const a of arms) {
|
|
56
|
+
const g = byKey.get(a.routeKey) ?? [];
|
|
57
|
+
g.push(a);
|
|
58
|
+
byKey.set(a.routeKey, g);
|
|
59
|
+
}
|
|
60
|
+
const lines = [];
|
|
61
|
+
lines.push(`${"routeKey".padEnd(9)} ${"model".padEnd(28)} ${"n".padStart(5)} ${"reward".padStart(7)} ${"latency".padStart(9)} ${"cost".padStart(10)}`);
|
|
62
|
+
for (const key of [...byKey.keys()].sort()) {
|
|
63
|
+
const group = (byKey.get(key) ?? []).slice().sort((x, y) => y.meanReward - x.meanReward);
|
|
64
|
+
group.forEach((a, i) => {
|
|
65
|
+
const star = i === 0 && a.n > 0 ? " *" : "";
|
|
66
|
+
const latency = `${a.meanLatencyMs.toFixed(0)}ms`;
|
|
67
|
+
const cost = a.meanCostUsd > 0 ? `$${a.meanCostUsd.toFixed(5)}` : "-";
|
|
68
|
+
lines.push(`${key.padEnd(9)} ${a.model.padEnd(28)} ${String(a.n).padStart(5)} ${a.meanReward.toFixed(3).padStart(7)} ${latency.padStart(9)} ${cost.padStart(10)}${star}`);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push("* = current best arm in its bucket (what a `learned` policy exploits once every arm clears its sample floor).");
|
|
73
|
+
return lines.join("\n");
|
|
74
|
+
}
|
|
75
|
+
/** Wipe the whole scoreboard. Returns the number of arms removed. */
|
|
76
|
+
export function resetRouting(rootDir) {
|
|
77
|
+
const removed = loadArms(rootDir).length;
|
|
78
|
+
const path = join(rootDir, "routing", "arms.jsonl");
|
|
79
|
+
if (existsSync(path))
|
|
80
|
+
rmSync(path, { force: true });
|
|
81
|
+
return removed;
|
|
82
|
+
}
|
|
83
|
+
/** Run `crewhaus route …`, returning the text to print. */
|
|
84
|
+
export function runRoute(argv) {
|
|
85
|
+
const args = parseRouteArgs(argv);
|
|
86
|
+
if (args.sub === "status")
|
|
87
|
+
return formatRouteStatus(loadArms(args.dir));
|
|
88
|
+
const removed = resetRouting(args.dir);
|
|
89
|
+
return `Reset routing scoreboard at ${join(args.dir, "routing", "arms.jsonl")} (${removed} arm${removed === 1 ? "" : "s"} removed).`;
|
|
90
|
+
}
|
|
@@ -73,7 +73,8 @@ export type SecurityDigest = {
|
|
|
73
73
|
readonly malformedLines: number;
|
|
74
74
|
readonly countsByKind: Readonly<Record<string, number>>;
|
|
75
75
|
/** Kinds @crewhaus/audit-log declares that produced 0 window records —
|
|
76
|
-
*
|
|
76
|
+
* always includes the still-writerless kinds, plus any writerful kind
|
|
77
|
+
* (e.g. egress_decision) that simply had no records in the window. */
|
|
77
78
|
readonly absentDeclaredKinds: ReadonlyArray<string>;
|
|
78
79
|
};
|
|
79
80
|
/** Pillar 3 intent gate — from `permission_justification_evaluated`. */
|
package/dist/security-digest.js
CHANGED
|
@@ -25,6 +25,12 @@ import { join, resolve } from "node:path";
|
|
|
25
25
|
* - policy_decision policy-engine's auditPolicyDecision (payload
|
|
26
26
|
* { toolName, sideEffect, decision, reason, tenantId, matchedRule }) —
|
|
27
27
|
* deny/audit-and-allow rows only by default.
|
|
28
|
+
* - egress_decision runtime-core's egress classifier (AUTOMATION-
|
|
29
|
+
* OPPORTUNITIES item 20; payload { sinkId, sinkScope, verdict,
|
|
30
|
+
* originsFound, matchCount }) — appended durably on every NON-PASS
|
|
31
|
+
* (warn/block) verdict when the egress audit sink is wired, so the
|
|
32
|
+
* rollup carries warn/block egress history; pass verdicts stay
|
|
33
|
+
* ephemeral on the trace bus and leave no audit record.
|
|
28
34
|
* - model_call, gateway_request, secrets_rotation, secrets_access,
|
|
29
35
|
* deployment_action, retention_enforcement, alert_raised (item-31
|
|
30
36
|
* alert watchdog's `AlertAuditSink`, see alert-sink.ts; payload
|
|
@@ -33,15 +39,14 @@ import { join, resolve } from "node:path";
|
|
|
33
39
|
* but builds no verdict rollup from them.
|
|
34
40
|
*
|
|
35
41
|
* kinds DECLARED in @crewhaus/audit-log but with NO writer anywhere in the
|
|
36
|
-
* tree today: `
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* reads as "no durable writer yet", not "no egress happened".
|
|
42
|
+
* tree today: `tool_classification`, `session_fork`, `tenancy_context`. The
|
|
43
|
+
* digest still aggregates records of these kinds by their documented payload
|
|
44
|
+
* shape so the rollup lights up the day a writer lands, and reports them
|
|
45
|
+
* under `absentDeclaredKinds` so "0 rows" reads as "no durable writer yet",
|
|
46
|
+
* not "nothing happened". (egress_decision graduated to the writer list
|
|
47
|
+
* above once runtime-core began appending it on warn/block verdicts; a
|
|
48
|
+
* window with no warn/block egress still lists it as absent — meaning "no
|
|
49
|
+
* warn/block egress in the window", not "no writer".)
|
|
45
50
|
*
|
|
46
51
|
* Because block-tier verdicts DO leave durable residue in the session event
|
|
47
52
|
* logs (`.crewhaus/sessions/<id>.jsonl` `tool_result` events carry the
|
|
@@ -210,7 +215,8 @@ export function buildSecurityDigest(opts) {
|
|
|
210
215
|
let jDenied = 0;
|
|
211
216
|
const deniedTools = new Map();
|
|
212
217
|
const judges = new Map();
|
|
213
|
-
// Egress rollup accumulators
|
|
218
|
+
// Egress rollup accumulators — fed by egress_decision records (written on
|
|
219
|
+
// warn/block verdicts; see module header).
|
|
214
220
|
let ePassed = 0;
|
|
215
221
|
let eWarned = 0;
|
|
216
222
|
let eBlocked = 0;
|
|
@@ -480,7 +486,7 @@ export function renderSecurityDigestText(d) {
|
|
|
480
486
|
lines.push(` • origin ${o.name}: ${o.count} hit(s)`);
|
|
481
487
|
}
|
|
482
488
|
else {
|
|
483
|
-
lines.push("egress: no
|
|
489
|
+
lines.push("egress: no egress_decision records in window — records are written on warn/block verdicts; live per-run verdicts via CREWHAUS_SECURITY_DIGEST=1");
|
|
484
490
|
}
|
|
485
491
|
lines.push(`policy engine: ${d.policy.decisions} audited decision(s) — ${d.policy.denied} denied, ${d.policy.auditAndAllow} audit-and-allow`);
|
|
486
492
|
for (const t of d.policy.topDeniedTools)
|
|
@@ -576,7 +582,7 @@ ${tableOf(["Tool", "Denials", "Judges", "Mean confidence", "Last reason"], d.jus
|
|
|
576
582
|
${tableOf(["Judge", "Evaluated", "Denied"], d.justification.byJudge.map((j) => [j.judgeModel, String(j.evaluated), String(j.denied)]))}
|
|
577
583
|
<h2>Egress sinks (warn/block)</h2>
|
|
578
584
|
${d.egress.decisions === 0
|
|
579
|
-
? '<p class="note">no
|
|
585
|
+
? '<p class="note">no egress_decision records in window — records are written on warn/block verdicts; live per-run verdicts via CREWHAUS_SECURITY_DIGEST=1</p>'
|
|
580
586
|
: tableOf(["Sink", "Warned", "Blocked", "Origins"], d.egress.topSinks.map((s) => [
|
|
581
587
|
s.sinkId,
|
|
582
588
|
String(s.warned),
|
package/dist/tools-cli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewhaus",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CrewHaus — the meta-harness compiler for AI agents. Compile one crewhaus.yaml spec into a CLI agent, channel bot, RAG pipeline, multi-agent crew, eval harness, voice or browser agent, and more.",
|
|
6
6
|
"keywords": [
|
|
@@ -31,93 +31,94 @@
|
|
|
31
31
|
"test": "bun test src"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@crewhaus/adapter-anthropic": "0.2.
|
|
35
|
-
"@crewhaus/agent-context-isolation": "0.2.
|
|
36
|
-
"@crewhaus/audit-log": "0.2.
|
|
37
|
-
"@crewhaus/eval-optimizer-orchestrator": "0.2.
|
|
38
|
-
"@crewhaus/prompt-optimizer": "0.2.
|
|
39
|
-
"@crewhaus/prompt-optimizer-claude": "0.2.
|
|
40
|
-
"@crewhaus/justification-judge-claude": "0.2.
|
|
41
|
-
"@crewhaus/spec-patch": "0.2.
|
|
42
|
-
"@crewhaus/canary-controller": "0.2.
|
|
43
|
-
"@crewhaus/compiler": "0.2.
|
|
44
|
-
"@crewhaus/computer-use-driver": "0.2.
|
|
45
|
-
"@crewhaus/context-bundle": "0.2.
|
|
46
|
-
"@crewhaus/cost-tracker": "0.2.
|
|
47
|
-
"@crewhaus/data-retention-engine": "0.2.
|
|
48
|
-
"@crewhaus/dataset-registry": "0.2.
|
|
49
|
-
"@crewhaus/deployment-controller": "0.2.
|
|
50
|
-
"@crewhaus/egress-classifier": "0.2.
|
|
51
|
-
"@crewhaus/egress-matcher-semantic": "0.2.
|
|
52
|
-
"@crewhaus/embedder": "0.2.
|
|
53
|
-
"@crewhaus/errors": "0.2.
|
|
54
|
-
"@crewhaus/eval-dataset": "0.2.
|
|
55
|
-
"@crewhaus/eval-grader": "0.2.
|
|
56
|
-
"@crewhaus/eval-judge": "0.2.
|
|
57
|
-
"@crewhaus/eval-report": "0.2.
|
|
58
|
-
"@crewhaus/eval-runner": "0.2.
|
|
59
|
-
"@crewhaus/event-log": "0.2.
|
|
60
|
-
"@crewhaus/gateway-server": "0.2.
|
|
61
|
-
"@crewhaus/grader-safety-classifiers": "0.2.
|
|
62
|
-
"@crewhaus/hooks-engine": "0.2.
|
|
63
|
-
"@crewhaus/infra-utils": "0.2.
|
|
64
|
-
"@crewhaus/ir": "0.2.
|
|
65
|
-
"@crewhaus/ir-passes": "0.2.
|
|
66
|
-
"@crewhaus/logging": "0.2.
|
|
67
|
-
"@crewhaus/mcp-host": "0.2.
|
|
68
|
-
"@crewhaus/memory-store": "0.2.
|
|
69
|
-
"@crewhaus/model-router": "0.2.
|
|
70
|
-
"@crewhaus/module-marketplace-client": "0.2.
|
|
71
|
-
"@crewhaus/migration-engine": "0.2.
|
|
72
|
-
"@crewhaus/migration-runner": "0.2.
|
|
73
|
-
"@crewhaus/plugin-registry": "0.2.
|
|
74
|
-
"@crewhaus/plugin-sdk": "0.2.
|
|
75
|
-
"@crewhaus/permission-engine": "0.2.
|
|
76
|
-
"@crewhaus/pii-redactor": "0.2.
|
|
77
|
-
"@crewhaus/prompt-injection-detector": "0.2.
|
|
78
|
-
"@crewhaus/regression-runner": "0.2.
|
|
79
|
-
"@crewhaus/
|
|
80
|
-
"@crewhaus/
|
|
81
|
-
"@crewhaus/
|
|
82
|
-
"@crewhaus/
|
|
83
|
-
"@crewhaus/
|
|
84
|
-
"@crewhaus/
|
|
85
|
-
"@crewhaus/
|
|
86
|
-
"@crewhaus/
|
|
87
|
-
"@crewhaus/
|
|
88
|
-
"@crewhaus/
|
|
89
|
-
"@crewhaus/
|
|
90
|
-
"@crewhaus/template-
|
|
91
|
-
"@crewhaus/
|
|
92
|
-
"@crewhaus/
|
|
93
|
-
"@crewhaus/
|
|
94
|
-
"@crewhaus/tool-
|
|
95
|
-
"@crewhaus/
|
|
96
|
-
"@crewhaus/target-
|
|
97
|
-
"@crewhaus/
|
|
98
|
-
"@crewhaus/tool-
|
|
99
|
-
"@crewhaus/tool-
|
|
100
|
-
"@crewhaus/tool-
|
|
101
|
-
"@crewhaus/tool-
|
|
102
|
-
"@crewhaus/tool-
|
|
103
|
-
"@crewhaus/tool-image
|
|
104
|
-
"@crewhaus/tool-
|
|
105
|
-
"@crewhaus/tool-
|
|
106
|
-
"@crewhaus/tool-
|
|
107
|
-
"@crewhaus/tool-
|
|
108
|
-
"@crewhaus/tool-
|
|
109
|
-
"@crewhaus/tool-
|
|
110
|
-
"@crewhaus/tool-
|
|
111
|
-
"@crewhaus/tool-
|
|
112
|
-
"@crewhaus/tool-
|
|
113
|
-
"@crewhaus/tool-
|
|
114
|
-
"@crewhaus/tool-
|
|
115
|
-
"@crewhaus/
|
|
116
|
-
"@crewhaus/
|
|
117
|
-
"@crewhaus/
|
|
118
|
-
"@crewhaus/
|
|
119
|
-
"@crewhaus/sandbox
|
|
120
|
-
"@crewhaus/
|
|
34
|
+
"@crewhaus/adapter-anthropic": "0.2.1",
|
|
35
|
+
"@crewhaus/agent-context-isolation": "0.2.1",
|
|
36
|
+
"@crewhaus/audit-log": "0.2.1",
|
|
37
|
+
"@crewhaus/eval-optimizer-orchestrator": "0.2.1",
|
|
38
|
+
"@crewhaus/prompt-optimizer": "0.2.1",
|
|
39
|
+
"@crewhaus/prompt-optimizer-claude": "0.2.1",
|
|
40
|
+
"@crewhaus/justification-judge-claude": "0.2.1",
|
|
41
|
+
"@crewhaus/spec-patch": "0.2.1",
|
|
42
|
+
"@crewhaus/canary-controller": "0.2.1",
|
|
43
|
+
"@crewhaus/compiler": "0.2.1",
|
|
44
|
+
"@crewhaus/computer-use-driver": "0.2.1",
|
|
45
|
+
"@crewhaus/context-bundle": "0.2.1",
|
|
46
|
+
"@crewhaus/cost-tracker": "0.2.1",
|
|
47
|
+
"@crewhaus/data-retention-engine": "0.2.1",
|
|
48
|
+
"@crewhaus/dataset-registry": "0.2.1",
|
|
49
|
+
"@crewhaus/deployment-controller": "0.2.1",
|
|
50
|
+
"@crewhaus/egress-classifier": "0.2.1",
|
|
51
|
+
"@crewhaus/egress-matcher-semantic": "0.2.1",
|
|
52
|
+
"@crewhaus/embedder": "0.2.1",
|
|
53
|
+
"@crewhaus/errors": "0.2.1",
|
|
54
|
+
"@crewhaus/eval-dataset": "0.2.1",
|
|
55
|
+
"@crewhaus/eval-grader": "0.2.1",
|
|
56
|
+
"@crewhaus/eval-judge": "0.2.1",
|
|
57
|
+
"@crewhaus/eval-report": "0.2.1",
|
|
58
|
+
"@crewhaus/eval-runner": "0.2.1",
|
|
59
|
+
"@crewhaus/event-log": "0.2.1",
|
|
60
|
+
"@crewhaus/gateway-server": "0.2.1",
|
|
61
|
+
"@crewhaus/grader-safety-classifiers": "0.2.1",
|
|
62
|
+
"@crewhaus/hooks-engine": "0.2.1",
|
|
63
|
+
"@crewhaus/infra-utils": "0.2.1",
|
|
64
|
+
"@crewhaus/ir": "0.2.1",
|
|
65
|
+
"@crewhaus/ir-passes": "0.2.1",
|
|
66
|
+
"@crewhaus/logging": "0.2.1",
|
|
67
|
+
"@crewhaus/mcp-host": "0.2.1",
|
|
68
|
+
"@crewhaus/memory-store": "0.2.1",
|
|
69
|
+
"@crewhaus/model-router": "0.2.1",
|
|
70
|
+
"@crewhaus/module-marketplace-client": "0.2.1",
|
|
71
|
+
"@crewhaus/migration-engine": "0.2.1",
|
|
72
|
+
"@crewhaus/migration-runner": "0.2.1",
|
|
73
|
+
"@crewhaus/plugin-registry": "0.2.1",
|
|
74
|
+
"@crewhaus/plugin-sdk": "0.2.1",
|
|
75
|
+
"@crewhaus/permission-engine": "0.2.1",
|
|
76
|
+
"@crewhaus/pii-redactor": "0.2.1",
|
|
77
|
+
"@crewhaus/prompt-injection-detector": "0.2.1",
|
|
78
|
+
"@crewhaus/regression-runner": "0.2.1",
|
|
79
|
+
"@crewhaus/routing-store": "0.2.1",
|
|
80
|
+
"@crewhaus/run-context": "0.2.1",
|
|
81
|
+
"@crewhaus/runtime-core": "0.2.1",
|
|
82
|
+
"@crewhaus/secrets-manager": "0.2.1",
|
|
83
|
+
"@crewhaus/session-store": "0.2.1",
|
|
84
|
+
"@crewhaus/spec-registry": "0.2.1",
|
|
85
|
+
"@crewhaus/skills-registry": "0.2.1",
|
|
86
|
+
"@crewhaus/slash-commands": "0.2.1",
|
|
87
|
+
"@crewhaus/smoke-harness": "0.2.1",
|
|
88
|
+
"@crewhaus/spec": "0.2.1",
|
|
89
|
+
"@crewhaus/sub-agent-spawner": "0.2.1",
|
|
90
|
+
"@crewhaus/template-marketplace-client": "0.2.1",
|
|
91
|
+
"@crewhaus/template-registry": "0.2.1",
|
|
92
|
+
"@crewhaus/tenancy": "0.2.1",
|
|
93
|
+
"@crewhaus/trace-event-bus": "0.2.1",
|
|
94
|
+
"@crewhaus/tool-bash": "0.2.1",
|
|
95
|
+
"@crewhaus/tool-builder": "0.2.1",
|
|
96
|
+
"@crewhaus/target-cli": "0.2.1",
|
|
97
|
+
"@crewhaus/target-eval-bundle": "0.2.1",
|
|
98
|
+
"@crewhaus/tool-catalog": "0.2.1",
|
|
99
|
+
"@crewhaus/tool-code-execution": "0.2.1",
|
|
100
|
+
"@crewhaus/tool-codegraph": "0.2.1",
|
|
101
|
+
"@crewhaus/tool-fetch": "0.2.1",
|
|
102
|
+
"@crewhaus/tool-fs": "0.2.1",
|
|
103
|
+
"@crewhaus/tool-image": "0.2.1",
|
|
104
|
+
"@crewhaus/tool-image-generation": "0.2.1",
|
|
105
|
+
"@crewhaus/tool-document-ingest": "0.2.1",
|
|
106
|
+
"@crewhaus/tool-mcp": "0.2.1",
|
|
107
|
+
"@crewhaus/tool-memory": "0.2.1",
|
|
108
|
+
"@crewhaus/tool-mouse-keyboard": "0.2.1",
|
|
109
|
+
"@crewhaus/tool-navigate": "0.2.1",
|
|
110
|
+
"@crewhaus/tool-permission-matcher": "0.2.1",
|
|
111
|
+
"@crewhaus/tool-screen-capture": "0.2.1",
|
|
112
|
+
"@crewhaus/tool-task": "0.2.1",
|
|
113
|
+
"@crewhaus/tool-todo": "0.2.1",
|
|
114
|
+
"@crewhaus/tool-vision-grounding": "0.2.1",
|
|
115
|
+
"@crewhaus/tool-web": "0.2.1",
|
|
116
|
+
"@crewhaus/docker-images": "0.2.1",
|
|
117
|
+
"@crewhaus/crewhaus-cloud": "0.2.1",
|
|
118
|
+
"@crewhaus/federation-discovery": "0.2.1",
|
|
119
|
+
"@crewhaus/sandbox": "0.2.1",
|
|
120
|
+
"@crewhaus/sandbox-image-registry": "0.2.1",
|
|
121
|
+
"@crewhaus/compliance-controls": "0.2.1",
|
|
121
122
|
"yaml": "^2.6.0"
|
|
122
123
|
},
|
|
123
124
|
"devDependencies": {
|