wicked-crew 0.7.22 → 0.7.24
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/audit.d.ts +4 -1
- package/dist/api/audit.d.ts.map +1 -1
- package/dist/api/audit.js +5 -1
- package/dist/api/audit.js.map +1 -1
- package/dist/api/eval-store.d.ts +85 -0
- package/dist/api/eval-store.d.ts.map +1 -0
- package/dist/api/eval-store.js +176 -0
- package/dist/api/eval-store.js.map +1 -0
- package/dist/api/governance-steering.d.ts +5 -0
- package/dist/api/governance-steering.d.ts.map +1 -1
- package/dist/api/governance-steering.js +8 -9
- package/dist/api/governance-steering.js.map +1 -1
- package/dist/api/routes.d.ts +11 -0
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +116 -19
- package/dist/api/routes.js.map +1 -1
- package/dist/api/run-timing-index.d.ts +54 -0
- package/dist/api/run-timing-index.d.ts.map +1 -0
- package/dist/api/run-timing-index.js +82 -0
- package/dist/api/run-timing-index.js.map +1 -0
- package/dist/api/server.d.ts +4 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +15 -1
- package/dist/api/server.js.map +1 -1
- package/dist/api/testing.d.ts +17 -1
- package/dist/api/testing.d.ts.map +1 -1
- package/dist/api/testing.js +93 -22
- package/dist/api/testing.js.map +1 -1
- package/dist/studio/assets/index-BmXt63Ij.js +537 -0
- package/dist/studio/assets/index-eso7X_3n.css +32 -0
- package/dist/studio/index.html +2 -2
- package/dist/studio/testid-inventory.json +411 -150
- package/endpoint-manifest.json +20 -1
- package/package.json +2 -2
- package/dist/studio/assets/index-CDWyxcSj.js +0 -537
- package/dist/studio/assets/index-Dz6mXrD5.css +0 -32
package/dist/api/routes.js
CHANGED
|
@@ -36,6 +36,7 @@ import { AuditLog } from './audit.js';
|
|
|
36
36
|
import { AcpFoldCache, EngineVersionCache, eventsDirOf, installedPackageVersion, listStoreFiles, readStudioBundleVersion, } from './diagnostics.js';
|
|
37
37
|
import { RetryIndex } from './retry-index.js';
|
|
38
38
|
import { GroupIndex } from './group-index.js';
|
|
39
|
+
import { RunTimingIndex, recordRunLaunched } from './run-timing-index.js';
|
|
39
40
|
import { GuidanceIndex } from './guidance-index.js';
|
|
40
41
|
import { DeliveryIndex, gitRunBranchIsEmpty, gitWorktreeIsClean, isDeliverConflictStranded, prUrlFrom, } from './delivery-index.js';
|
|
41
42
|
import { DeliveryDerivationCache } from './delivery-cache.js';
|
|
@@ -93,6 +94,39 @@ function invalidBody(err, what) {
|
|
|
93
94
|
function firstQueryValue(v) {
|
|
94
95
|
return Array.isArray(v) ? v[0] : v;
|
|
95
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Parse an optional `since`/`until` date-bound query param (unix SECONDS). Returns the parsed
|
|
99
|
+
* non-negative integer, `undefined` when the param is absent or blank, or the sentinel `'invalid'`
|
|
100
|
+
* when it is present but not a non-negative integer — the caller turns `'invalid'` into a 400 that
|
|
101
|
+
* names the offending param (a silently-dropped bad bound would answer a different date question
|
|
102
|
+
* than the one asked).
|
|
103
|
+
*/
|
|
104
|
+
function parseUnixSecondsParam(raw) {
|
|
105
|
+
if (raw === undefined || raw.trim() === '')
|
|
106
|
+
return undefined;
|
|
107
|
+
const n = Number(raw);
|
|
108
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0)
|
|
109
|
+
return 'invalid';
|
|
110
|
+
return n;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Inclusive `[since, until]` date-range predicate (unix seconds) shared by the memory + rules
|
|
114
|
+
* browse filters. An item with NO `created_at` is EXCLUDED whenever either bound is set — an
|
|
115
|
+
* unknown creation time is never asserted in range, and never fabricated (crew never invents a
|
|
116
|
+
* timestamp for a row the store did not stamp). With NEITHER bound set every item passes, so the
|
|
117
|
+
* un-filtered browse is unchanged.
|
|
118
|
+
*/
|
|
119
|
+
function withinDateRange(createdAt, since, until) {
|
|
120
|
+
if (since === undefined && until === undefined)
|
|
121
|
+
return true;
|
|
122
|
+
if (typeof createdAt !== 'number')
|
|
123
|
+
return false;
|
|
124
|
+
if (since !== undefined && createdAt < since)
|
|
125
|
+
return false;
|
|
126
|
+
if (until !== undefined && createdAt > until)
|
|
127
|
+
return false;
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
96
130
|
/** A JSON object whose every value is a string (a facet/intent tuple, axis→value). */
|
|
97
131
|
function isStringRecord(v) {
|
|
98
132
|
return (typeof v === 'object' &&
|
|
@@ -201,6 +235,10 @@ function shapeMemoryItem(raw) {
|
|
|
201
235
|
};
|
|
202
236
|
if (typeof r['score'] === 'number')
|
|
203
237
|
item.score = r['score'];
|
|
238
|
+
// `created_at` (unix seconds) rides through from estate `memory.list` verbatim — the "added" time
|
|
239
|
+
// the surface date-filters on. Absent on an item the store did not stamp; never fabricated.
|
|
240
|
+
if (typeof r['created_at'] === 'number')
|
|
241
|
+
item.created_at = r['created_at'];
|
|
204
242
|
return item;
|
|
205
243
|
}
|
|
206
244
|
/** Shape the estate `memory.coverage` result (`{ total, by_tier, by_kind }`) into the wire
|
|
@@ -392,6 +430,7 @@ runtime = {}) {
|
|
|
392
430
|
const signedIn = runtime.signedIn ?? signedInHeuristic;
|
|
393
431
|
const retryIndex = runtime.retryIndex ?? new RetryIndex();
|
|
394
432
|
const groupIndex = runtime.groupIndex ?? new GroupIndex();
|
|
433
|
+
const runTimingIndex = runtime.runTimingIndex ?? new RunTimingIndex();
|
|
395
434
|
const guidanceIndex = runtime.guidanceIndex ?? new GuidanceIndex();
|
|
396
435
|
const deliveryIndex = runtime.deliveryIndex ?? new DeliveryIndex();
|
|
397
436
|
const worktreeExists = runtime.worktreeExists ?? ((p) => existsSync(p));
|
|
@@ -491,6 +530,13 @@ runtime = {}) {
|
|
|
491
530
|
const guidance = guidanceIndex.guidanceFor(view.session.id);
|
|
492
531
|
if (guidance !== undefined)
|
|
493
532
|
view.session.guidance = guidance;
|
|
533
|
+
// Run launch time (home command-center run metrics): unix SECONDS from the `run.launched`
|
|
534
|
+
// audit entry, ABSENT when the daemon has no launch record for this run (onboarding/campaign
|
|
535
|
+
// runs launched off POST /runs, pre-field runs) — never fabricated, so a bucketed KPI can
|
|
536
|
+
// exclude an undated run rather than dating it with a false now.
|
|
537
|
+
const createdAt = runTimingIndex.createdAtFor(view.session.id);
|
|
538
|
+
if (createdAt !== undefined)
|
|
539
|
+
view.session.created_at = createdAt;
|
|
494
540
|
const state = resolveDelivery(view, conflictStrand);
|
|
495
541
|
view.session.delivery = state.delivery;
|
|
496
542
|
if (state.deliverUrl !== undefined)
|
|
@@ -966,24 +1012,29 @@ runtime = {}) {
|
|
|
966
1012
|
// Who launched it — the engine's LaunchOptions carries no actor field
|
|
967
1013
|
// (checked, wicked-core-ts 0.6.0), so the crew-side trail is the system
|
|
968
1014
|
// of record for run provenance (task #88).
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1015
|
+
// `run.launched` is the system of record for run provenance (the engine's LaunchOptions
|
|
1016
|
+
// carries no actor field — task #88). `recordRunLaunched` writes the trail entry AND stamps
|
|
1017
|
+
// the run-timing index with the SAME durable `ts` (home command-center run metrics): the live
|
|
1018
|
+
// `created_at` equals what a restart rehydrates from the trail — no 1s drift, and no fabricated
|
|
1019
|
+
// value (audit disabled → nothing stamped → `created_at` ABSENT, the honest answer for a run
|
|
1020
|
+
// with no durable launch record). `createServer` always builds a REAL trail, so this fires on
|
|
1021
|
+
// every production launch. The SHARED helper (Copilot #466) is why the recon/steering-author
|
|
1022
|
+
// launchers stamp identically instead of leaving their runs undated until a restart.
|
|
1023
|
+
recordRunLaunched(audit, runTimingIndex, actorOf(req), runId, {
|
|
1024
|
+
...(b.workflow !== undefined ? { workflow: b.workflow } : {}),
|
|
1025
|
+
...(b.repoRef !== undefined ? { repoRef: b.repoRef } : {}),
|
|
1026
|
+
...(b.projectId !== undefined ? { projectId: b.projectId } : {}),
|
|
1027
|
+
// crew#393: the RESOLVED delivery decision, not just the caller's field — so the
|
|
1028
|
+
// trail says what the run will actually do, and whether the daemon decided it.
|
|
1029
|
+
deliver,
|
|
1030
|
+
...(deliverDefaulted ? { deliverDefaulted: true } : {}),
|
|
1031
|
+
// CREW-UX-3: the trail is the durable record of lineage — the retry index (and a
|
|
1032
|
+
// restarted daemon's hydrate) reads it back from exactly this entry.
|
|
1033
|
+
...(b.retryOf !== undefined ? { retryOf: b.retryOf } : {}),
|
|
1034
|
+
// wicked-studio#27: the trail is likewise the durable record of the group attach —
|
|
1035
|
+
// the group index (and a restarted daemon's hydrate) reads it back from here.
|
|
1036
|
+
...(b.campaignId !== undefined ? { campaignId: b.campaignId } : {}),
|
|
1037
|
+
...(b.groupLabel !== undefined ? { groupLabel: b.groupLabel } : {}),
|
|
987
1038
|
});
|
|
988
1039
|
if (b.retryOf !== undefined)
|
|
989
1040
|
retryIndex.set(runId, b.retryOf);
|
|
@@ -2063,6 +2114,27 @@ runtime = {}) {
|
|
|
2063
2114
|
return invalid('status', status, 'active|retired|all');
|
|
2064
2115
|
}
|
|
2065
2116
|
const layer = facet('layer');
|
|
2117
|
+
// `since`/`until` (unix SECONDS, inclusive) date-bound the listed rows by each rule's
|
|
2118
|
+
// `created_at`. A present-but-non-integer bound is a 400. NOTE: `created_at` is wired-but-
|
|
2119
|
+
// pending a core-ts bump — it landed on the engine's `ConformanceRule` after the published
|
|
2120
|
+
// core-ts this daemon builds against, so on the current binding NO rule carries it and this
|
|
2121
|
+
// filter narrows to empty. The pass-through is verbatim (`listConformanceRules` returns the
|
|
2122
|
+
// parsed rows unchanged), so the bound starts working the moment crew re-pins to a core-ts
|
|
2123
|
+
// that surfaces it — a rule with no `created_at` is excluded, never dated with a fabricated now.
|
|
2124
|
+
const since = facet('since');
|
|
2125
|
+
const sinceSecs = parseUnixSecondsParam(since);
|
|
2126
|
+
if (sinceSecs === 'invalid') {
|
|
2127
|
+
return reply
|
|
2128
|
+
.code(400)
|
|
2129
|
+
.send({ error: `since must be a non-negative integer in unix seconds (got \`${since ?? ''}\`)` });
|
|
2130
|
+
}
|
|
2131
|
+
const until = facet('until');
|
|
2132
|
+
const untilSecs = parseUnixSecondsParam(until);
|
|
2133
|
+
if (untilSecs === 'invalid') {
|
|
2134
|
+
return reply
|
|
2135
|
+
.code(400)
|
|
2136
|
+
.send({ error: `until must be a non-negative integer in unix seconds (got \`${until ?? ''}\`)` });
|
|
2137
|
+
}
|
|
2066
2138
|
const rules = (await adapter.listConformanceRules()).filter((r) => (severity === undefined || r.severity === severity) &&
|
|
2067
2139
|
(ruleType === undefined || r.rule_type === ruleType) &&
|
|
2068
2140
|
(layer === undefined || r.targets.layer === layer) &&
|
|
@@ -2070,7 +2142,9 @@ runtime = {}) {
|
|
|
2070
2142
|
// steering engine always stamps it, but filter defensively over mixed-era rows.
|
|
2071
2143
|
(steeringType === undefined || (r.steering_type ?? DEFAULT_STEERING_TYPE) === steeringType) &&
|
|
2072
2144
|
// `retired` is absent on rows written before the field existed, which read as active.
|
|
2073
|
-
(status === 'all' || (status === 'retired') === (r.retired === true))
|
|
2145
|
+
(status === 'all' || (status === 'retired') === (r.retired === true)) &&
|
|
2146
|
+
// Undated rules (every rule on a pre-bump core-ts) are excluded once a bound is set.
|
|
2147
|
+
withinDateRange(r.created_at, sinceSecs, untilSecs));
|
|
2074
2148
|
return { rules };
|
|
2075
2149
|
});
|
|
2076
2150
|
app.get(`${V}/governance/claims`, async () => {
|
|
@@ -2991,6 +3065,18 @@ runtime = {}) {
|
|
|
2991
3065
|
}
|
|
2992
3066
|
limit = n;
|
|
2993
3067
|
}
|
|
3068
|
+
// `since`/`until` (unix SECONDS, inclusive) date-bound the complete set crew-side by each
|
|
3069
|
+
// memory's `created_at`. A present-but-non-integer bound is a 400 (fail-loud, never a silent
|
|
3070
|
+
// no-filter); a memory with no `created_at` is excluded once either bound is set (see
|
|
3071
|
+
// withinDateRange). Applied BEFORE the row cap, so `limit` cuts the dated set.
|
|
3072
|
+
const since = parseUnixSecondsParam(firstQueryValue(q.since));
|
|
3073
|
+
if (since === 'invalid') {
|
|
3074
|
+
return reply.code(400).send({ error: '`since` must be a non-negative integer (unix seconds)' });
|
|
3075
|
+
}
|
|
3076
|
+
const until = parseUnixSecondsParam(firstQueryValue(q.until));
|
|
3077
|
+
if (until === 'invalid') {
|
|
3078
|
+
return reply.code(400).send({ error: '`until` must be a non-negative integer (unix seconds)' });
|
|
3079
|
+
}
|
|
2994
3080
|
// `query`, when present, is a case-insensitive CONTENT substring filter over the complete set
|
|
2995
3081
|
// — a management search that finds faceted memories too (unlike relevance recall).
|
|
2996
3082
|
const query = (firstQueryValue(q.query) ?? '').trim().toLowerCase();
|
|
@@ -3007,6 +3093,9 @@ runtime = {}) {
|
|
|
3007
3093
|
const pairs = Object.entries(facetFilter);
|
|
3008
3094
|
memories = memories.filter((m) => pairs.every(([k, v]) => m.facets[k] === v));
|
|
3009
3095
|
}
|
|
3096
|
+
if (since !== undefined || until !== undefined) {
|
|
3097
|
+
memories = memories.filter((m) => withinDateRange(m.created_at, since, until));
|
|
3098
|
+
}
|
|
3010
3099
|
if (limit !== undefined)
|
|
3011
3100
|
memories = memories.slice(0, limit);
|
|
3012
3101
|
const body = { memories };
|
|
@@ -3094,6 +3183,8 @@ runtime = {}) {
|
|
|
3094
3183
|
audit,
|
|
3095
3184
|
actorOf,
|
|
3096
3185
|
roster: () => CoreAdapter.roster(),
|
|
3186
|
+
// So the steering-author run's `run.launched` entry stamps `created_at` live too (Copilot #466).
|
|
3187
|
+
runTimingIndex,
|
|
3097
3188
|
});
|
|
3098
3189
|
// ── Testing (crew-testing) — governance evals + eval corpora + the recon trigger ────────────
|
|
3099
3190
|
// Run the evals (does the steering corpus catch what it claims to?) and import a named eval
|
|
@@ -3105,6 +3196,12 @@ runtime = {}) {
|
|
|
3105
3196
|
actorOf,
|
|
3106
3197
|
roster: () => CoreAdapter.roster(),
|
|
3107
3198
|
projects: { bus: projects.bus, index: projects.index },
|
|
3199
|
+
// So the recon fan's `run.launched` entries stamp `created_at` live too (Copilot #466), not
|
|
3200
|
+
// just after a restart re-hydrates the trail.
|
|
3201
|
+
runTimingIndex,
|
|
3202
|
+
// The eval history store: `createServer` supplies a real one (state-home-rooted); a
|
|
3203
|
+
// directly-driven route set records nothing unless it injects one (no `~/.wicked-crew` writes).
|
|
3204
|
+
...(runtime.evalStore !== undefined ? { evalStore: runtime.evalStore } : {}),
|
|
3108
3205
|
});
|
|
3109
3206
|
// ── The wicked-interactive bridge, reverse-proxied (DES-MERGE-001 §5.3/§7.2) ──
|
|
3110
3207
|
// Mounted BESIDE the routes above and under the same `${V}` prefix, so it inherits one
|