wicked-crew 0.7.1 → 0.7.3
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/api/delivery-index.d.ts +61 -0
- package/dist/api/delivery-index.d.ts.map +1 -0
- package/dist/api/delivery-index.js +87 -0
- package/dist/api/delivery-index.js.map +1 -0
- package/dist/api/governance-steering.d.ts +139 -0
- package/dist/api/governance-steering.d.ts.map +1 -0
- package/dist/api/governance-steering.js +302 -0
- package/dist/api/governance-steering.js.map +1 -0
- package/dist/api/governance-wiki.d.ts +28 -0
- package/dist/api/governance-wiki.d.ts.map +1 -0
- package/dist/api/governance-wiki.js +97 -0
- package/dist/api/governance-wiki.js.map +1 -0
- package/dist/api/routes.d.ts +21 -16
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +196 -10
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +56 -1
- package/dist/api/server.js.map +1 -1
- package/dist/api/testing.d.ts +117 -0
- package/dist/api/testing.d.ts.map +1 -0
- package/dist/api/testing.js +152 -0
- package/dist/api/testing.js.map +1 -0
- package/dist/campaign/supervision.d.ts +296 -0
- package/dist/campaign/supervision.d.ts.map +1 -0
- package/dist/campaign/supervision.js +515 -0
- package/dist/campaign/supervision.js.map +1 -0
- package/dist/campaigns/plan.d.ts +64 -0
- package/dist/campaigns/plan.d.ts.map +1 -0
- package/dist/campaigns/plan.js +201 -0
- package/dist/campaigns/plan.js.map +1 -0
- package/dist/campaigns/routes.d.ts +28 -0
- package/dist/campaigns/routes.d.ts.map +1 -0
- package/dist/campaigns/routes.js +177 -0
- package/dist/campaigns/routes.js.map +1 -0
- package/dist/core/adapter.d.ts +160 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +300 -1
- package/dist/core/adapter.js.map +1 -1
- package/dist/projects/routes.d.ts +4 -4
- package/dist/qe/acceptance.d.ts +20 -1
- package/dist/qe/acceptance.d.ts.map +1 -1
- package/dist/qe/acceptance.js +32 -0
- package/dist/qe/acceptance.js.map +1 -1
- package/dist/qe/conformance.d.ts +158 -0
- package/dist/qe/conformance.d.ts.map +1 -0
- package/dist/qe/conformance.js +244 -0
- package/dist/qe/conformance.js.map +1 -0
- package/dist/studio/assets/index-BAW9rqNT.css +32 -0
- package/dist/studio/assets/index-UahdRwF7.js +532 -0
- package/dist/studio/index.html +2 -2
- package/dist/studio/testid-inventory.json +4387 -0
- package/endpoint-manifest.json +154 -9
- package/package.json +3 -3
- package/dist/studio/assets/index-D-BFUYnY.js +0 -530
- package/dist/studio/assets/index-D6S9zUtO.css +0 -32
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run→delivered-PR index (CREW-UX-8, crew#321).
|
|
3
|
+
*
|
|
4
|
+
* `LaunchRunBody.deliver?: 'pr'` is input-only; the deliver phase computes the PR URL
|
|
5
|
+
* (`core/deliver.ts` — re-derived from the remote, refused without one) and prints it as the
|
|
6
|
+
* unit's last output line, which is console text, not a wire. This index is the read-side
|
|
7
|
+
* latency layer that lets the run DTOs echo `session.delivery` on BOTH `GET /runs` and
|
|
8
|
+
* `GET /runs/:id` without a `workOutput` read per request — the persisted field that makes a
|
|
9
|
+
* list surface's "4 of 5 siblings delivered" rollup affordable without an N-fetch fan-out
|
|
10
|
+
* (wicked-studio#27).
|
|
11
|
+
*
|
|
12
|
+
* Mirrors `RetryIndex`/`GuidanceIndex`'s posture exactly: the DURABLE record is the audit
|
|
13
|
+
* trail (the engine run record has no such field) — action `run.delivered`, `detail.url` —
|
|
14
|
+
* hydrated once at server start (best-effort: a missing or unreadable trail leaves deliveries
|
|
15
|
+
* blank, the pre-#321 behavior, not an error) and updated at the same post-terminal point
|
|
16
|
+
* that writes the audit entry.
|
|
17
|
+
*
|
|
18
|
+
* KNOWN LIMIT (stated in the contract too): runs that delivered before this landed have no
|
|
19
|
+
* `run.delivered` entry and carry no field. Do NOT backfill by scanning `work_output` at
|
|
20
|
+
* hydrate — that is an unbounded boot cost. Historical runs still resolve their URL through
|
|
21
|
+
* the per-run output endpoint.
|
|
22
|
+
*/
|
|
23
|
+
import type { AuditLog } from './audit.js';
|
|
24
|
+
import type { SessionView, WorkUnit } from '../core/types.js';
|
|
25
|
+
/** The wire shape `AgentSession.delivery` carries (api-types 0.11.0). */
|
|
26
|
+
export interface SessionDelivery {
|
|
27
|
+
kind: 'pull_request';
|
|
28
|
+
url: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The PR URL in a deliver transcript — crew's own extraction, mirrored
|
|
32
|
+
* (`core/deliver.ts`: `grep -Eo 'https://[^[:space:]]+/pull/[0-9]+' | tail -1`). Requiring
|
|
33
|
+
* the digits keeps `…/pull/new/<branch>` — the create-PR form git prints on every push —
|
|
34
|
+
* from ever matching; the LAST match wins, same as `tail -1`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function prUrlFrom(text: string): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* This run's deliver unit, or `null`. The composed id suffix (`<base>:deliver`) is the
|
|
39
|
+
* primary key; the `tool_cmd` probe is the fallback for an operator OVERLAY that carried the
|
|
40
|
+
* deliver phase under its own name — do NOT key on `workflow_id`, which is plain for
|
|
41
|
+
* overlay-carried deliver phases (crew#321).
|
|
42
|
+
*/
|
|
43
|
+
export declare function deliverUnitOf(view: SessionView): WorkUnit | null;
|
|
44
|
+
export declare class DeliveryIndex {
|
|
45
|
+
private readonly runToUrl;
|
|
46
|
+
/**
|
|
47
|
+
* Load deliveries from EVERY `run.delivered` entry in the trail — exhaustively, not capped
|
|
48
|
+
* (BRIEF-UX-002 C5, the same defect class as `RetryIndex.hydrate`: a durable record must
|
|
49
|
+
* not vanish because 1000+ newer writes landed on top of it). The trail answers newest
|
|
50
|
+
* first, so the FIRST entry seen per run wins; older entries for the same run are
|
|
51
|
+
* superseded and skipped. Still best-effort, like its siblings; cost is one full-file scan
|
|
52
|
+
* at boot. (Third exhaustive trail scan at boot, after RetryIndex and GuidanceIndex — a
|
|
53
|
+
* FOURTH should trigger consolidating them into one pass, per crew#321.)
|
|
54
|
+
*/
|
|
55
|
+
hydrate(audit: AuditLog, log?: (msg: string) => void): Promise<void>;
|
|
56
|
+
/** Record the run's delivered PR URL (idempotent — the newest write wins). */
|
|
57
|
+
set(runId: string, url: string): void;
|
|
58
|
+
/** The delivery for this run, or `undefined` (the DTO spells that as an ABSENT field). */
|
|
59
|
+
deliveryFor(runId: string): SessionDelivery | undefined;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=delivery-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-index.d.ts","sourceRoot":"","sources":["../../src/api/delivery-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGrD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CAIhE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD;;;;;;;;OAQG;IACG,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB1E,8EAA8E;IAC9E,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAIrC,0FAA0F;IAC1F,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;CAIxD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run→delivered-PR index (CREW-UX-8, crew#321).
|
|
3
|
+
*
|
|
4
|
+
* `LaunchRunBody.deliver?: 'pr'` is input-only; the deliver phase computes the PR URL
|
|
5
|
+
* (`core/deliver.ts` — re-derived from the remote, refused without one) and prints it as the
|
|
6
|
+
* unit's last output line, which is console text, not a wire. This index is the read-side
|
|
7
|
+
* latency layer that lets the run DTOs echo `session.delivery` on BOTH `GET /runs` and
|
|
8
|
+
* `GET /runs/:id` without a `workOutput` read per request — the persisted field that makes a
|
|
9
|
+
* list surface's "4 of 5 siblings delivered" rollup affordable without an N-fetch fan-out
|
|
10
|
+
* (wicked-studio#27).
|
|
11
|
+
*
|
|
12
|
+
* Mirrors `RetryIndex`/`GuidanceIndex`'s posture exactly: the DURABLE record is the audit
|
|
13
|
+
* trail (the engine run record has no such field) — action `run.delivered`, `detail.url` —
|
|
14
|
+
* hydrated once at server start (best-effort: a missing or unreadable trail leaves deliveries
|
|
15
|
+
* blank, the pre-#321 behavior, not an error) and updated at the same post-terminal point
|
|
16
|
+
* that writes the audit entry.
|
|
17
|
+
*
|
|
18
|
+
* KNOWN LIMIT (stated in the contract too): runs that delivered before this landed have no
|
|
19
|
+
* `run.delivered` entry and carry no field. Do NOT backfill by scanning `work_output` at
|
|
20
|
+
* hydrate — that is an unbounded boot cost. Historical runs still resolve their URL through
|
|
21
|
+
* the per-run output endpoint.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The PR URL in a deliver transcript — crew's own extraction, mirrored
|
|
25
|
+
* (`core/deliver.ts`: `grep -Eo 'https://[^[:space:]]+/pull/[0-9]+' | tail -1`). Requiring
|
|
26
|
+
* the digits keeps `…/pull/new/<branch>` — the create-PR form git prints on every push —
|
|
27
|
+
* from ever matching; the LAST match wins, same as `tail -1`.
|
|
28
|
+
*/
|
|
29
|
+
export function prUrlFrom(text) {
|
|
30
|
+
const matches = text.match(/https:\/\/\S+\/pull\/\d+/g);
|
|
31
|
+
return matches === null ? null : (matches[matches.length - 1] ?? null);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* This run's deliver unit, or `null`. The composed id suffix (`<base>:deliver`) is the
|
|
35
|
+
* primary key; the `tool_cmd` probe is the fallback for an operator OVERLAY that carried the
|
|
36
|
+
* deliver phase under its own name — do NOT key on `workflow_id`, which is plain for
|
|
37
|
+
* overlay-carried deliver phases (crew#321).
|
|
38
|
+
*/
|
|
39
|
+
export function deliverUnitOf(view) {
|
|
40
|
+
const byId = view.units.find((u) => u.id.endsWith(':deliver'));
|
|
41
|
+
if (byId !== undefined)
|
|
42
|
+
return byId;
|
|
43
|
+
return view.units.find((u) => (u.tool_cmd ?? []).join(' ').includes('gh pr create')) ?? null;
|
|
44
|
+
}
|
|
45
|
+
export class DeliveryIndex {
|
|
46
|
+
runToUrl = new Map();
|
|
47
|
+
/**
|
|
48
|
+
* Load deliveries from EVERY `run.delivered` entry in the trail — exhaustively, not capped
|
|
49
|
+
* (BRIEF-UX-002 C5, the same defect class as `RetryIndex.hydrate`: a durable record must
|
|
50
|
+
* not vanish because 1000+ newer writes landed on top of it). The trail answers newest
|
|
51
|
+
* first, so the FIRST entry seen per run wins; older entries for the same run are
|
|
52
|
+
* superseded and skipped. Still best-effort, like its siblings; cost is one full-file scan
|
|
53
|
+
* at boot. (Third exhaustive trail scan at boot, after RetryIndex and GuidanceIndex — a
|
|
54
|
+
* FOURTH should trigger consolidating them into one pass, per crew#321.)
|
|
55
|
+
*/
|
|
56
|
+
async hydrate(audit, log) {
|
|
57
|
+
try {
|
|
58
|
+
const seen = new Set();
|
|
59
|
+
for (const entry of await audit.readAll({ action: 'run.delivered' })) {
|
|
60
|
+
if (typeof entry.runId !== 'string')
|
|
61
|
+
continue;
|
|
62
|
+
if (seen.has(entry.runId))
|
|
63
|
+
continue;
|
|
64
|
+
// Newest entry decides, even when malformed — marking the run seen BEFORE the url
|
|
65
|
+
// check keeps a corrupt newest write from resurrecting an older one (the #312 rule).
|
|
66
|
+
seen.add(entry.runId);
|
|
67
|
+
const url = entry.detail?.['url'];
|
|
68
|
+
if (typeof url !== 'string' || url === '')
|
|
69
|
+
continue;
|
|
70
|
+
this.runToUrl.set(entry.runId, url);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
log?.(`[runs] delivery-index hydrate failed (prior runs read as undelivered until restart): ${err instanceof Error ? err.message : String(err)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Record the run's delivered PR URL (idempotent — the newest write wins). */
|
|
78
|
+
set(runId, url) {
|
|
79
|
+
this.runToUrl.set(runId, url);
|
|
80
|
+
}
|
|
81
|
+
/** The delivery for this run, or `undefined` (the DTO spells that as an ABSENT field). */
|
|
82
|
+
deliveryFor(runId) {
|
|
83
|
+
const url = this.runToUrl.get(runId);
|
|
84
|
+
return url === undefined ? undefined : { kind: 'pull_request', url };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=delivery-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-index.js","sourceRoot":"","sources":["../../src/api/delivery-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACxD,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,IAAI,IAAI,CAAC;AAC/F,CAAC;AAED,MAAM,OAAO,aAAa;IACP,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtD;;;;;;;;OAQG;IACH,KAAK,CAAC,OAAO,CAAC,KAAe,EAAE,GAA2B;QACxD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,KAAK,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;gBACrE,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;oBAAE,SAAS;gBAC9C,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;oBAAE,SAAS;gBACpC,kFAAkF;gBAClF,qFAAqF;gBACrF,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACtB,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;gBAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE;oBAAE,SAAS;gBACpD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,EAAE,CACH,wFACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,GAAG,CAAC,KAAa,EAAE,GAAW;QAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,0FAA0F;IAC1F,WAAW,CAAC,KAAa;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC;IACvE,CAAC;CACF"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/api/v1/governance/steering/*` surface (STEERING program) — the management wire behind
|
|
3
|
+
* the studio's top-level Steering nav: batch IMPORT of steering docs/rules, and "add with chat"
|
|
4
|
+
* AUTHORING as a governed run. Per-rule CRUD deliberately lives elsewhere — this module adds no
|
|
5
|
+
* second door: `GET/POST /governance/rules` (now steering-aware: `?type=` facet,
|
|
6
|
+
* `?include_retired=`, steering-field writes) and `DELETE /governance/rules/:id` in
|
|
7
|
+
* `api/routes.ts` ARE the steering-rule CRUD, because a steering rule IS a conformance rule
|
|
8
|
+
* (the wiki/rules model and the old policy model merged into one).
|
|
9
|
+
*
|
|
10
|
+
* Thin by design, like `campaigns/routes.ts`: validation is zod (strict, unknown keys named —
|
|
11
|
+
* the FINDING-031 doctrine); import normalization/validation is the ENGINE's ingest path on the
|
|
12
|
+
* single-writer actor (fail-closed per entry — the daemon never opens the store for writes);
|
|
13
|
+
* authoring is the EXISTING run machinery (`launchRun` + the `steering-author` drop-in workflow
|
|
14
|
+
* + the standard `POST /runs/:id/gate` HITL flow) — no new chat stack. Error posture mirrors the
|
|
15
|
+
* campaign surface: a build whose engine addon predates the steering model answers 501
|
|
16
|
+
* (`SteeringUnsupportedError` — "upgrade the engine"), never 400 ("fix your request").
|
|
17
|
+
*
|
|
18
|
+
* estate's MCP surface stays READ-ONLY on rules; every steering WRITE flows through here (the
|
|
19
|
+
* governed operator path) — the AW-11 "no rules.write on estate" invariant is a design input of
|
|
20
|
+
* this module, not a coincidence.
|
|
21
|
+
*/
|
|
22
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
import { type CoreAdapter } from '../core/adapter.js';
|
|
25
|
+
import type { Actor } from '../core/types.js';
|
|
26
|
+
import type { AuditLog } from './audit.js';
|
|
27
|
+
/**
|
|
28
|
+
* The steering-type vocabulary (`wicked-crew-api-types::SteeringType`) — enum-as-string, the
|
|
29
|
+
* engine's serde default is `architecture`. ONE spelling, exported: `api/routes.ts` reads it for
|
|
30
|
+
* the `?type=` browse facet so the two surfaces can never disagree about what a type is.
|
|
31
|
+
*/
|
|
32
|
+
export declare const STEERING_TYPE_VALUES: readonly ["architecture", "development", "security", "testing", "operations", "compliance", "design-ux"];
|
|
33
|
+
export declare const STEERING_TYPES: ReadonlySet<string>;
|
|
34
|
+
/** The engine's serde default for a rule row that predates (or omits) `steering_type`. */
|
|
35
|
+
export declare const DEFAULT_STEERING_TYPE = "architecture";
|
|
36
|
+
/** Per-entry ceiling on an import doc / rule payload (UTF-8 bytes) — mirrors the run-files cap. */
|
|
37
|
+
export declare const STEERING_ENTRY_MAX_BYTES: number;
|
|
38
|
+
/** Batch ceiling — an import is a page action, not a bulk-migration channel. */
|
|
39
|
+
export declare const STEERING_IMPORT_MAX_ENTRIES = 200;
|
|
40
|
+
/** Authoring intent is operator prose, not a document store (the guidance-cap doctrine). */
|
|
41
|
+
export declare const STEERING_INSTRUCTIONS_MAX_BYTES: number;
|
|
42
|
+
export declare const SteeringImportSchema: z.ZodObject<{
|
|
43
|
+
type: z.ZodOptional<z.ZodEnum<["architecture", "development", "security", "testing", "operations", "compliance", "design-ux"]>>;
|
|
44
|
+
entries: z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
|
|
45
|
+
kind: z.ZodLiteral<"doc">;
|
|
46
|
+
name: z.ZodOptional<z.ZodString>;
|
|
47
|
+
content: z.ZodString;
|
|
48
|
+
}, "strict", z.ZodTypeAny, {
|
|
49
|
+
kind: "doc";
|
|
50
|
+
content: string;
|
|
51
|
+
name?: string | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
kind: "doc";
|
|
54
|
+
content: string;
|
|
55
|
+
name?: string | undefined;
|
|
56
|
+
}>, z.ZodObject<{
|
|
57
|
+
kind: z.ZodLiteral<"rule">;
|
|
58
|
+
rule: z.ZodType<import("wicked-crew-api-types").ConformanceRule, z.ZodTypeDef, import("wicked-crew-api-types").ConformanceRule>;
|
|
59
|
+
}, "strict", z.ZodTypeAny, {
|
|
60
|
+
kind: "rule";
|
|
61
|
+
rule: import("wicked-crew-api-types").ConformanceRule;
|
|
62
|
+
}, {
|
|
63
|
+
kind: "rule";
|
|
64
|
+
rule: import("wicked-crew-api-types").ConformanceRule;
|
|
65
|
+
}>]>, "many">;
|
|
66
|
+
}, "strict", z.ZodTypeAny, {
|
|
67
|
+
entries: ({
|
|
68
|
+
kind: "doc";
|
|
69
|
+
content: string;
|
|
70
|
+
name?: string | undefined;
|
|
71
|
+
} | {
|
|
72
|
+
kind: "rule";
|
|
73
|
+
rule: import("wicked-crew-api-types").ConformanceRule;
|
|
74
|
+
})[];
|
|
75
|
+
type?: "architecture" | "development" | "security" | "testing" | "operations" | "compliance" | "design-ux" | undefined;
|
|
76
|
+
}, {
|
|
77
|
+
entries: ({
|
|
78
|
+
kind: "doc";
|
|
79
|
+
content: string;
|
|
80
|
+
name?: string | undefined;
|
|
81
|
+
} | {
|
|
82
|
+
kind: "rule";
|
|
83
|
+
rule: import("wicked-crew-api-types").ConformanceRule;
|
|
84
|
+
})[];
|
|
85
|
+
type?: "architecture" | "development" | "security" | "testing" | "operations" | "compliance" | "design-ux" | undefined;
|
|
86
|
+
}>;
|
|
87
|
+
export declare const SteeringAuthorSchema: z.ZodObject<{
|
|
88
|
+
instructions: z.ZodString;
|
|
89
|
+
type: z.ZodOptional<z.ZodEnum<["architecture", "development", "security", "testing", "operations", "compliance", "design-ux"]>>;
|
|
90
|
+
paths: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
91
|
+
documents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
92
|
+
name: z.ZodString;
|
|
93
|
+
content: z.ZodString;
|
|
94
|
+
}, "strict", z.ZodTypeAny, {
|
|
95
|
+
content: string;
|
|
96
|
+
name: string;
|
|
97
|
+
}, {
|
|
98
|
+
content: string;
|
|
99
|
+
name: string;
|
|
100
|
+
}>, "many">>;
|
|
101
|
+
repoRef: z.ZodOptional<z.ZodString>;
|
|
102
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, "strict", z.ZodTypeAny, {
|
|
104
|
+
instructions: string;
|
|
105
|
+
type?: "architecture" | "development" | "security" | "testing" | "operations" | "compliance" | "design-ux" | undefined;
|
|
106
|
+
repoRef?: string | undefined;
|
|
107
|
+
paths?: string[] | undefined;
|
|
108
|
+
documents?: {
|
|
109
|
+
content: string;
|
|
110
|
+
name: string;
|
|
111
|
+
}[] | undefined;
|
|
112
|
+
sessionId?: string | undefined;
|
|
113
|
+
}, {
|
|
114
|
+
instructions: string;
|
|
115
|
+
type?: "architecture" | "development" | "security" | "testing" | "operations" | "compliance" | "design-ux" | undefined;
|
|
116
|
+
repoRef?: string | undefined;
|
|
117
|
+
paths?: string[] | undefined;
|
|
118
|
+
documents?: {
|
|
119
|
+
content: string;
|
|
120
|
+
name: string;
|
|
121
|
+
}[] | undefined;
|
|
122
|
+
sessionId?: string | undefined;
|
|
123
|
+
}>;
|
|
124
|
+
/**
|
|
125
|
+
* Where the author route lands inline documents so the run can read them: a per-run inbox on the
|
|
126
|
+
* daemon host — the existing run file mechanism (runs read the daemon's filesystem; the paths
|
|
127
|
+
* ride the problem statement, never the prompt-fattening content itself).
|
|
128
|
+
*/
|
|
129
|
+
export declare function steeringInboxDir(runId: string): string;
|
|
130
|
+
export interface SteeringRoutesDeps {
|
|
131
|
+
audit: AuditLog;
|
|
132
|
+
actorOf: (req: FastifyRequest & {
|
|
133
|
+
actor?: Actor;
|
|
134
|
+
}) => Actor;
|
|
135
|
+
/** The default council roster for the authoring run (already parsed). */
|
|
136
|
+
roster: () => unknown[];
|
|
137
|
+
}
|
|
138
|
+
export declare function registerGovernanceSteeringRoutes(app: FastifyInstance, adapter: CoreAdapter, deps: SteeringRoutesDeps): void;
|
|
139
|
+
//# sourceMappingURL=governance-steering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governance-steering.d.ts","sourceRoot":"","sources":["../../src/api/governance-steering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA4B,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAA6C,MAAM,kBAAkB,CAAC;AACzF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,0GAQvB,CAAC;AAEX,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAAiC,CAAC;AAEjF,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AAEpD,mGAAmG;AACnG,eAAO,MAAM,wBAAwB,QAAa,CAAC;AACnD,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,4FAA4F;AAC5F,eAAO,MAAM,+BAA+B,QAAY,CAAC;AA0CzD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQtB,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYtB,CAAC;AAEZ;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,KAAK,KAAK,CAAC;IAC5D,yEAAyE;IACzE,MAAM,EAAE,MAAM,OAAO,EAAE,CAAC;CACzB;AAED,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,kBAAkB,GACvB,IAAI,CAkMN"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/api/v1/governance/steering/*` surface (STEERING program) — the management wire behind
|
|
3
|
+
* the studio's top-level Steering nav: batch IMPORT of steering docs/rules, and "add with chat"
|
|
4
|
+
* AUTHORING as a governed run. Per-rule CRUD deliberately lives elsewhere — this module adds no
|
|
5
|
+
* second door: `GET/POST /governance/rules` (now steering-aware: `?type=` facet,
|
|
6
|
+
* `?include_retired=`, steering-field writes) and `DELETE /governance/rules/:id` in
|
|
7
|
+
* `api/routes.ts` ARE the steering-rule CRUD, because a steering rule IS a conformance rule
|
|
8
|
+
* (the wiki/rules model and the old policy model merged into one).
|
|
9
|
+
*
|
|
10
|
+
* Thin by design, like `campaigns/routes.ts`: validation is zod (strict, unknown keys named —
|
|
11
|
+
* the FINDING-031 doctrine); import normalization/validation is the ENGINE's ingest path on the
|
|
12
|
+
* single-writer actor (fail-closed per entry — the daemon never opens the store for writes);
|
|
13
|
+
* authoring is the EXISTING run machinery (`launchRun` + the `steering-author` drop-in workflow
|
|
14
|
+
* + the standard `POST /runs/:id/gate` HITL flow) — no new chat stack. Error posture mirrors the
|
|
15
|
+
* campaign surface: a build whose engine addon predates the steering model answers 501
|
|
16
|
+
* (`SteeringUnsupportedError` — "upgrade the engine"), never 400 ("fix your request").
|
|
17
|
+
*
|
|
18
|
+
* estate's MCP surface stays READ-ONLY on rules; every steering WRITE flows through here (the
|
|
19
|
+
* governed operator path) — the AW-11 "no rules.write on estate" invariant is a design input of
|
|
20
|
+
* this module, not a coincidence.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID } from 'node:crypto';
|
|
23
|
+
import { existsSync, promises as fsp } from 'node:fs';
|
|
24
|
+
import { basename, isAbsolute, join } from 'node:path';
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
import { SteeringUnsupportedError } from '../core/adapter.js';
|
|
27
|
+
import { API_PREFIX } from './api-prefix.js';
|
|
28
|
+
const V = API_PREFIX;
|
|
29
|
+
/**
|
|
30
|
+
* The steering-type vocabulary (`wicked-crew-api-types::SteeringType`) — enum-as-string, the
|
|
31
|
+
* engine's serde default is `architecture`. ONE spelling, exported: `api/routes.ts` reads it for
|
|
32
|
+
* the `?type=` browse facet so the two surfaces can never disagree about what a type is.
|
|
33
|
+
*/
|
|
34
|
+
export const STEERING_TYPE_VALUES = [
|
|
35
|
+
'architecture',
|
|
36
|
+
'development',
|
|
37
|
+
'security',
|
|
38
|
+
'testing',
|
|
39
|
+
'operations',
|
|
40
|
+
'compliance',
|
|
41
|
+
'design-ux',
|
|
42
|
+
];
|
|
43
|
+
export const STEERING_TYPES = new Set(STEERING_TYPE_VALUES);
|
|
44
|
+
/** The engine's serde default for a rule row that predates (or omits) `steering_type`. */
|
|
45
|
+
export const DEFAULT_STEERING_TYPE = 'architecture';
|
|
46
|
+
/** Per-entry ceiling on an import doc / rule payload (UTF-8 bytes) — mirrors the run-files cap. */
|
|
47
|
+
export const STEERING_ENTRY_MAX_BYTES = 512 * 1024;
|
|
48
|
+
/** Batch ceiling — an import is a page action, not a bulk-migration channel. */
|
|
49
|
+
export const STEERING_IMPORT_MAX_ENTRIES = 200;
|
|
50
|
+
/** Authoring intent is operator prose, not a document store (the guidance-cap doctrine). */
|
|
51
|
+
export const STEERING_INSTRUCTIONS_MAX_BYTES = 16 * 1024;
|
|
52
|
+
function message(err) {
|
|
53
|
+
return err instanceof Error ? err.message : String(err);
|
|
54
|
+
}
|
|
55
|
+
/** Same unknown-key-naming 400 body builder as api/routes.ts (restated because that module does
|
|
56
|
+
* not export it and importing routes.ts here would be a cycle through registerRoutes). */
|
|
57
|
+
function invalidBody(err, what) {
|
|
58
|
+
const unknown = err.issues.flatMap((i) => (i.code === 'unrecognized_keys' ? i.keys : []));
|
|
59
|
+
const error = unknown.length > 0
|
|
60
|
+
? `${what}: unknown field${unknown.length > 1 ? 's' : ''} ${unknown
|
|
61
|
+
.map((k) => `\`${k}\``)
|
|
62
|
+
.join(', ')} — this endpoint does not accept ${unknown.length > 1 ? 'them' : 'it'}, and ignoring ${unknown.length > 1 ? 'them' : 'it'} would run a different request than you sent`
|
|
63
|
+
: what;
|
|
64
|
+
return { error, details: err.issues };
|
|
65
|
+
}
|
|
66
|
+
// A doc entry: frontmattered markdown, the SAME format `rules ingest --dir` consumes. The rule
|
|
67
|
+
// entry's payload is validated shape-wise by the ENGINE's normalize/validate path (fail-closed
|
|
68
|
+
// per entry) — the route checks only "is an object", so engine-side validation stays the ONE
|
|
69
|
+
// spelling of what a rule is.
|
|
70
|
+
const ImportDocSchema = z
|
|
71
|
+
.object({
|
|
72
|
+
kind: z.literal('doc'),
|
|
73
|
+
name: z.string().min(1).max(256).optional(),
|
|
74
|
+
content: z.string().min(1),
|
|
75
|
+
})
|
|
76
|
+
.strict();
|
|
77
|
+
const ImportRuleSchema = z
|
|
78
|
+
.object({
|
|
79
|
+
kind: z.literal('rule'),
|
|
80
|
+
rule: z.custom((v) => typeof v === 'object' && v !== null && !Array.isArray(v), { message: '`rule` must be a rule object' }),
|
|
81
|
+
})
|
|
82
|
+
.strict();
|
|
83
|
+
export const SteeringImportSchema = z
|
|
84
|
+
.object({
|
|
85
|
+
type: z.enum(STEERING_TYPE_VALUES).optional(),
|
|
86
|
+
entries: z
|
|
87
|
+
.array(z.discriminatedUnion('kind', [ImportDocSchema, ImportRuleSchema]))
|
|
88
|
+
.min(1)
|
|
89
|
+
.max(STEERING_IMPORT_MAX_ENTRIES),
|
|
90
|
+
})
|
|
91
|
+
.strict();
|
|
92
|
+
export const SteeringAuthorSchema = z
|
|
93
|
+
.object({
|
|
94
|
+
instructions: z.string().min(1),
|
|
95
|
+
type: z.enum(STEERING_TYPE_VALUES).optional(),
|
|
96
|
+
paths: z.array(z.string().min(1)).max(32).optional(),
|
|
97
|
+
documents: z
|
|
98
|
+
.array(z.object({ name: z.string().min(1).max(128), content: z.string().min(1) }).strict())
|
|
99
|
+
.max(32)
|
|
100
|
+
.optional(),
|
|
101
|
+
repoRef: z.string().min(1).optional(),
|
|
102
|
+
sessionId: z.string().min(1).optional(),
|
|
103
|
+
})
|
|
104
|
+
.strict();
|
|
105
|
+
/**
|
|
106
|
+
* Where the author route lands inline documents so the run can read them: a per-run inbox on the
|
|
107
|
+
* daemon host — the existing run file mechanism (runs read the daemon's filesystem; the paths
|
|
108
|
+
* ride the problem statement, never the prompt-fattening content itself).
|
|
109
|
+
*/
|
|
110
|
+
export function steeringInboxDir(runId) {
|
|
111
|
+
// Env override first (the workflowOverlayDir pattern) — what keeps tests out of the
|
|
112
|
+
// operator's real ~/.wicked.
|
|
113
|
+
if (process.env.WICKED_STEERING_INBOX_DIR) {
|
|
114
|
+
return join(process.env.WICKED_STEERING_INBOX_DIR, runId);
|
|
115
|
+
}
|
|
116
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
|
|
117
|
+
return join(home, '.wicked', 'steering-inbox', runId);
|
|
118
|
+
}
|
|
119
|
+
export function registerGovernanceSteeringRoutes(app, adapter, deps) {
|
|
120
|
+
const { audit, actorOf } = deps;
|
|
121
|
+
// ── Batch import (the Steering page's Import action) ─────────────────────────
|
|
122
|
+
// JSON body only, deliberately: this daemon speaks no multipart (no parser registered, and the
|
|
123
|
+
// studio client speaks JSON everywhere else), so doc bytes ride `content` inline. `type` is the
|
|
124
|
+
// page's inferred steering type, applied engine-side as the default for entries that omit one.
|
|
125
|
+
// 200 even when entries rejected — fail-closed PER ENTRY, and the per-entry results ARE the
|
|
126
|
+
// answer; only a malformed batch (400), a pre-steering engine (501), or an engine failure (500)
|
|
127
|
+
// refuse the request as a whole.
|
|
128
|
+
app.post(`${V}/governance/steering/import`, {
|
|
129
|
+
config: {
|
|
130
|
+
manifest: {
|
|
131
|
+
requestType: 'SteeringImportBody',
|
|
132
|
+
responseType: 'SteeringImportResponse',
|
|
133
|
+
// 501: the installed engine addon predates the steering model (wicked-core-ts < 0.7.5)
|
|
134
|
+
// — upgrade the engine, the request was already correct.
|
|
135
|
+
statusCodes: [200, 400, 500, 501],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
}, async (req, reply) => {
|
|
139
|
+
const parsed = SteeringImportSchema.safeParse(req.body);
|
|
140
|
+
if (!parsed.success) {
|
|
141
|
+
return reply.code(400).send(invalidBody(parsed.error, 'Invalid steering import body'));
|
|
142
|
+
}
|
|
143
|
+
// Byte caps checked in the route, not zod's char-counting `max`, so the 400 names the
|
|
144
|
+
// actual limit (the guidance-cap doctrine).
|
|
145
|
+
for (const [i, entry] of parsed.data.entries.entries()) {
|
|
146
|
+
const payload = entry.kind === 'doc' ? entry.content : JSON.stringify(entry.rule);
|
|
147
|
+
const bytes = Buffer.byteLength(payload, 'utf8');
|
|
148
|
+
if (bytes > STEERING_ENTRY_MAX_BYTES) {
|
|
149
|
+
return reply.code(400).send({
|
|
150
|
+
error: `entries[${i}] is ${bytes} bytes, over the ${STEERING_ENTRY_MAX_BYTES}-byte per-entry cap`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const results = await adapter.importSteeringRules(parsed.data.entries, parsed.data.type);
|
|
156
|
+
const imported = results.filter((r) => r.status === 'imported').length;
|
|
157
|
+
const rejected = results.length - imported;
|
|
158
|
+
audit.record('governance.steering.imported', actorOf(req), {
|
|
159
|
+
detail: {
|
|
160
|
+
entries: results.length,
|
|
161
|
+
imported,
|
|
162
|
+
rejected,
|
|
163
|
+
...(parsed.data.type !== undefined ? { type: parsed.data.type } : {}),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
return { results, imported, rejected };
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
if (err instanceof SteeringUnsupportedError) {
|
|
170
|
+
return reply.code(501).send({ error: message(err) });
|
|
171
|
+
}
|
|
172
|
+
// An engine/store failure is OURS, not the caller's — the batch already parsed, so
|
|
173
|
+
// nothing here maps to 400 (per-ENTRY validation failures come back in `results`).
|
|
174
|
+
return reply.code(500).send({ error: message(err) });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// ── "Add with chat" (the Steering page's conversational authoring action) ────
|
|
178
|
+
// REUSES crew's run machinery end to end: launches the `steering-author` drop-in workflow
|
|
179
|
+
// (BUILTIN_WORKFLOWS — analyze → propose, with an unconditional human gate on the terminal
|
|
180
|
+
// propose phase: TH-12 propose-as-gate), inline documents land in the per-run steering inbox
|
|
181
|
+
// (the run reads the daemon host's filesystem — the existing run file mechanism), and the
|
|
182
|
+
// operator answers the proposal through the standard POST /runs/:id/gate. Approved rules land
|
|
183
|
+
// via POST /governance/rules with `provenance.source: "chat"` — the run itself writes nothing.
|
|
184
|
+
//
|
|
185
|
+
// Gated on the steering engine too, on purpose: the run only PROPOSES, but proposing rules
|
|
186
|
+
// whose steering fields the CRUD would then refuse (501) is a trap, not a feature.
|
|
187
|
+
app.post(`${V}/governance/steering/author`, {
|
|
188
|
+
config: {
|
|
189
|
+
manifest: {
|
|
190
|
+
requestType: 'SteeringAuthorBody',
|
|
191
|
+
responseType: '{ runId: string }',
|
|
192
|
+
// 409: engine busy / sessionId already exists; 501: pre-steering engine.
|
|
193
|
+
statusCodes: [201, 400, 409, 500, 501],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
}, async (req, reply) => {
|
|
197
|
+
const parsed = SteeringAuthorSchema.safeParse(req.body);
|
|
198
|
+
if (!parsed.success) {
|
|
199
|
+
return reply.code(400).send(invalidBody(parsed.error, 'Invalid steering author body'));
|
|
200
|
+
}
|
|
201
|
+
const b = parsed.data;
|
|
202
|
+
const intentBytes = Buffer.byteLength(b.instructions, 'utf8');
|
|
203
|
+
if (intentBytes > STEERING_INSTRUCTIONS_MAX_BYTES) {
|
|
204
|
+
return reply.code(400).send({
|
|
205
|
+
error: `instructions is ${intentBytes} bytes, over the ${STEERING_INSTRUCTIONS_MAX_BYTES}-byte cap — put long material in \`documents\` or \`paths\`, not the intent`,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
for (const [i, doc] of (b.documents ?? []).entries()) {
|
|
209
|
+
const bytes = Buffer.byteLength(doc.content, 'utf8');
|
|
210
|
+
if (bytes > STEERING_ENTRY_MAX_BYTES) {
|
|
211
|
+
return reply.code(400).send({
|
|
212
|
+
error: `documents[${i}] is ${bytes} bytes, over the ${STEERING_ENTRY_MAX_BYTES}-byte per-document cap`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// The run id NAMES the per-run inbox directory below, and `join` honors `../` — a
|
|
217
|
+
// free-form id could land inline documents outside the inbox (or alias another run's).
|
|
218
|
+
// Same doctrine as the document `name` scrub: caller text is never a path — but the id
|
|
219
|
+
// must round-trip exactly (it is the run's identity), so refuse loudly instead of
|
|
220
|
+
// scrubbing silently.
|
|
221
|
+
if (b.sessionId !== undefined && !/^(?!\.+$)[A-Za-z0-9._-]{1,128}$/.test(b.sessionId)) {
|
|
222
|
+
return reply.code(400).send({
|
|
223
|
+
error: 'sessionId must be 1-128 characters of [A-Za-z0-9._-] (and not dots only) — it names ' +
|
|
224
|
+
"the run's steering-inbox directory on the daemon host",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (!adapter.steeringSupported()) {
|
|
228
|
+
return reply
|
|
229
|
+
.code(501)
|
|
230
|
+
.send({ error: new SteeringUnsupportedError('Authoring steering rules').message });
|
|
231
|
+
}
|
|
232
|
+
// Paths are read on the DAEMON host — validate loudly here rather than letting the run
|
|
233
|
+
// burn a phase discovering a browser-local path. Absolute only (the extraWriteRoots
|
|
234
|
+
// doctrine), and existing (a named 400 beats a worker's "no such file" mid-run).
|
|
235
|
+
for (const p of b.paths ?? []) {
|
|
236
|
+
if (!isAbsolute(p)) {
|
|
237
|
+
return reply
|
|
238
|
+
.code(400)
|
|
239
|
+
.send({ error: `paths: \`${p}\` must be absolute on the daemon host` });
|
|
240
|
+
}
|
|
241
|
+
if (!existsSync(p)) {
|
|
242
|
+
return reply.code(400).send({
|
|
243
|
+
error: `paths: \`${p}\` does not exist on the daemon host — the authoring run reads the daemon's filesystem, not the browser's`,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const runId = b.sessionId ?? randomUUID();
|
|
248
|
+
// Inline documents → per-run inbox files; only their PATHS ride the problem statement
|
|
249
|
+
// (content in the prompt would fatten it past what prompt transports tolerate).
|
|
250
|
+
const docPaths = [];
|
|
251
|
+
if (b.documents !== undefined && b.documents.length > 0) {
|
|
252
|
+
const dir = steeringInboxDir(runId);
|
|
253
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
254
|
+
for (const [i, doc] of b.documents.entries()) {
|
|
255
|
+
// basename + charset scrub: the caller's `name` is display text, never a path. The
|
|
256
|
+
// index prefix keeps scrubbed names collision-free and the filename non-empty.
|
|
257
|
+
const safe = `${i}-${basename(doc.name).replace(/[^a-zA-Z0-9._-]/g, '_')}`;
|
|
258
|
+
const target = join(dir, safe);
|
|
259
|
+
await fsp.writeFile(target, doc.content, 'utf8');
|
|
260
|
+
docPaths.push(target);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const type = b.type ?? DEFAULT_STEERING_TYPE;
|
|
264
|
+
const sources = [...(b.paths ?? []), ...docPaths];
|
|
265
|
+
const problem = [
|
|
266
|
+
`Author steering rules for the '${type}' steering type (use it as the default steering_type for every proposed rule without a better fit).`,
|
|
267
|
+
'',
|
|
268
|
+
'Operator intent:',
|
|
269
|
+
b.instructions,
|
|
270
|
+
...(sources.length > 0
|
|
271
|
+
? ['', 'Analyze these files/directories on this machine:', ...sources.map((s) => `- ${s}`)]
|
|
272
|
+
: []),
|
|
273
|
+
].join('\n');
|
|
274
|
+
try {
|
|
275
|
+
await adapter.launchRun({
|
|
276
|
+
problem,
|
|
277
|
+
sessionId: runId,
|
|
278
|
+
clisJson: JSON.stringify(deps.roster()),
|
|
279
|
+
workflow: 'steering-author',
|
|
280
|
+
...(b.repoRef !== undefined ? { repoRef: b.repoRef } : {}),
|
|
281
|
+
});
|
|
282
|
+
// The same trail entry POST /runs writes — this IS a run launch, findable by the same
|
|
283
|
+
// `?action=run.launched` query — plus the steering detail the Steering header renders.
|
|
284
|
+
audit.record('run.launched', actorOf(req), {
|
|
285
|
+
runId,
|
|
286
|
+
detail: {
|
|
287
|
+
workflow: 'steering-author',
|
|
288
|
+
steeringType: type,
|
|
289
|
+
sources: sources.length,
|
|
290
|
+
...(b.repoRef !== undefined ? { repoRef: b.repoRef } : {}),
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
return reply.code(201).send({ runId });
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
const msg = message(err);
|
|
297
|
+
const busy = /busy|in flight|already/i.test(msg);
|
|
298
|
+
return reply.code(busy ? 409 : 400).send({ error: msg });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=governance-steering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governance-steering.js","sourceRoot":"","sources":["../../src/api/governance-steering.ts"],"names":[],"mappings":"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"}
|