wicked-crew 0.7.47 → 0.7.48
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/chat-citations.d.ts +10 -1
- package/dist/api/chat-citations.d.ts.map +1 -1
- package/dist/api/chat-citations.js +28 -0
- package/dist/api/chat-citations.js.map +1 -1
- package/dist/api/chat-scope.d.ts +4 -2
- package/dist/api/chat-scope.d.ts.map +1 -1
- package/dist/api/chat-scope.js +35 -3
- package/dist/api/chat-scope.js.map +1 -1
- package/dist/api/chat-transcripts.d.ts +23 -1
- package/dist/api/chat-transcripts.d.ts.map +1 -1
- package/dist/api/chat-transcripts.js +61 -6
- package/dist/api/chat-transcripts.js.map +1 -1
- package/dist/api/eval-sample.d.ts +6 -6
- package/dist/api/routes.d.ts +11 -0
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +99 -4
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.d.ts +15 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +94 -3
- package/dist/api/server.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +16 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/adapter.d.ts +16 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +16 -0
- package/dist/core/adapter.js.map +1 -1
- package/dist/decisions/chat-recorder.d.ts +129 -0
- package/dist/decisions/chat-recorder.d.ts.map +1 -0
- package/dist/decisions/chat-recorder.js +367 -0
- package/dist/decisions/chat-recorder.js.map +1 -0
- package/dist/decisions/consider.d.ts +102 -0
- package/dist/decisions/consider.d.ts.map +1 -0
- package/dist/decisions/consider.js +207 -0
- package/dist/decisions/consider.js.map +1 -0
- package/dist/decisions/events.d.ts +2 -0
- package/dist/decisions/events.d.ts.map +1 -1
- package/dist/decisions/events.js +2 -0
- package/dist/decisions/events.js.map +1 -1
- package/dist/decisions/ingest.d.ts +28 -5
- package/dist/decisions/ingest.d.ts.map +1 -1
- package/dist/decisions/ingest.js +14 -6
- package/dist/decisions/ingest.js.map +1 -1
- package/dist/decisions/rules-text.d.ts +21 -0
- package/dist/decisions/rules-text.d.ts.map +1 -0
- package/dist/decisions/rules-text.js +50 -0
- package/dist/decisions/rules-text.js.map +1 -0
- package/dist/editors/csp.d.ts +31 -0
- package/dist/editors/csp.d.ts.map +1 -0
- package/dist/editors/csp.js +46 -0
- package/dist/editors/csp.js.map +1 -0
- package/dist/editors/pack-install.d.ts +28 -0
- package/dist/editors/pack-install.d.ts.map +1 -0
- package/dist/editors/pack-install.js +153 -0
- package/dist/editors/pack-install.js.map +1 -0
- package/dist/editors/registry.d.ts +142 -0
- package/dist/editors/registry.d.ts.map +1 -0
- package/dist/editors/registry.js +255 -0
- package/dist/editors/registry.js.map +1 -0
- package/dist/editors/routes.d.ts +46 -0
- package/dist/editors/routes.d.ts.map +1 -0
- package/dist/editors/routes.js +166 -0
- package/dist/editors/routes.js.map +1 -0
- package/dist/interactive/bridge-pool.d.ts +7 -2
- package/dist/interactive/bridge-pool.d.ts.map +1 -1
- package/dist/interactive/bridge-pool.js +6 -1
- package/dist/interactive/bridge-pool.js.map +1 -1
- package/dist/interactive/theme-events.d.ts +119 -0
- package/dist/interactive/theme-events.d.ts.map +1 -0
- package/dist/interactive/theme-events.js +606 -0
- package/dist/interactive/theme-events.js.map +1 -0
- package/dist/projects/routes.d.ts +2 -2
- package/dist/standing-orders/routes.d.ts +21 -21
- package/dist/standing-orders/store.d.ts +9 -9
- package/dist/watch/checks/claim-vs-evidence.d.ts +30 -0
- package/dist/watch/checks/claim-vs-evidence.d.ts.map +1 -0
- package/dist/watch/checks/claim-vs-evidence.js +152 -0
- package/dist/watch/checks/claim-vs-evidence.js.map +1 -0
- package/dist/watch/checks/index.d.ts.map +1 -1
- package/dist/watch/checks/index.js +5 -0
- package/dist/watch/checks/index.js.map +1 -1
- package/dist/watch/checks/warned-rule.d.ts +26 -0
- package/dist/watch/checks/warned-rule.d.ts.map +1 -0
- package/dist/watch/checks/warned-rule.js +126 -0
- package/dist/watch/checks/warned-rule.js.map +1 -0
- package/dist/watch/registry.d.ts +29 -0
- package/dist/watch/registry.d.ts.map +1 -1
- package/dist/watch/registry.js +60 -1
- package/dist/watch/registry.js.map +1 -1
- package/dist/watch/types.d.ts +19 -0
- package/dist/watch/types.d.ts.map +1 -1
- package/endpoint-manifest.json +95 -1
- package/package.json +1 -1
- package/watch/entries/claim-vs-evidence.json +13 -0
- package/watch/entries/risky-call.json +12 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The studio chat recorder (DES-decision-capture §4.3.3, DC-S4b; DOC-4: the pair framing).
|
|
3
|
+
*
|
|
4
|
+
* A chat seat is asked (the scope statement's `## Decisions` section, `chat-scope.ts`) to end every
|
|
5
|
+
* reply with a fenced `wicked-decisions` JSON block labelling the OPERATOR'S words of the turn it is
|
|
6
|
+
* answering. This module is the whole host:
|
|
7
|
+
*
|
|
8
|
+
* - {@link splitDecisionsBlock} / {@link DecisionFenceFilter}: the block never reaches a reader. The
|
|
9
|
+
* `chatReply` text is cut before persist and broadcast (`ChatTranscriptStore.rewriteEvent`), and the
|
|
10
|
+
* streamed `chatDelta` frames hold back a tail that could still become the fence, then drop
|
|
11
|
+
* everything from the fence on until the turn's reply (review S4 / N8).
|
|
12
|
+
* - {@link ChatDecisionRecorder}: the recorder is chosen deterministically from the turn's audience
|
|
13
|
+
* (the first `claude` seat, else the first seat in roster order — review N3); its items become
|
|
14
|
+
* records when their quote is found, whitespace-normalised, in the SAME turn's operator message
|
|
15
|
+
* (the model may label words, never invent them); every other seat's items are `labels.votes`.
|
|
16
|
+
* - It fails OPEN with no regex fallback: a missing or malformed block leaves the turn
|
|
17
|
+
* "unclassified" (a counter on `/diagnostics`) and the operator's words are STILL recorded, so the
|
|
18
|
+
* deterministic derivation (`derive.ts`) runs with no model at all. The model only adds offers.
|
|
19
|
+
* - DOC-4: an item that `approves_proposal` carries `decision_text` written from the seat's approved
|
|
20
|
+
* proposal; that text becomes the record's statement and the record names the proposal (the
|
|
21
|
+
* previous turn, the recorder seat). A bare "lets do it" therefore yields a decision whose text is
|
|
22
|
+
* the proposal's, never auto (`derive.ts` refuses auto for an approval).
|
|
23
|
+
*
|
|
24
|
+
* Words enter the ledger only through `ingestDecision` (§4.3.1): the actor is the one who SENT the
|
|
25
|
+
* message (`noteSend`, from the route), so an agent token's chat message records nothing.
|
|
26
|
+
*/
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
import { chatTurnStaleAfterMs } from '../api/chat-turns.js';
|
|
29
|
+
import { ingestDecision } from './ingest.js';
|
|
30
|
+
/** The opening fence, exactly as the codebook (`skills/mem/refs/decision-report.md`) spells it. */
|
|
31
|
+
export const DECISIONS_FENCE = '```wicked-decisions';
|
|
32
|
+
const STEERING_TYPES = [
|
|
33
|
+
'architecture',
|
|
34
|
+
'development',
|
|
35
|
+
'security',
|
|
36
|
+
'testing',
|
|
37
|
+
'operations',
|
|
38
|
+
'compliance',
|
|
39
|
+
'design-ux',
|
|
40
|
+
];
|
|
41
|
+
const ItemSchema = z.object({
|
|
42
|
+
quote: z.string().min(1).max(4000),
|
|
43
|
+
decision_text: z.string().max(600).nullish(),
|
|
44
|
+
type: z.enum(['rule', 'correction', 'scope', 'exception', 'choice', 'confirmation']),
|
|
45
|
+
codify: z.boolean().default(false),
|
|
46
|
+
ambiguous: z.boolean().default(false),
|
|
47
|
+
steering_type: z.enum(STEERING_TYPES).default('development'),
|
|
48
|
+
approves_proposal: z.boolean().default(false),
|
|
49
|
+
same_as: z.string().nullable().default(null),
|
|
50
|
+
});
|
|
51
|
+
const BlockSchema = z.object({ items: z.array(z.unknown()) });
|
|
52
|
+
/**
|
|
53
|
+
* Parse a block body. `null` when the JSON is malformed or the body is not `{items: [...]}`; an item
|
|
54
|
+
* that fails its own shape is dropped and counted, the rest survive.
|
|
55
|
+
*/
|
|
56
|
+
export function parseDecisionsBlock(body) {
|
|
57
|
+
let raw;
|
|
58
|
+
try {
|
|
59
|
+
raw = JSON.parse(body);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const block = BlockSchema.safeParse(raw);
|
|
65
|
+
if (!block.success)
|
|
66
|
+
return null;
|
|
67
|
+
const items = [];
|
|
68
|
+
let dropped = 0;
|
|
69
|
+
for (const candidate of block.data.items) {
|
|
70
|
+
const item = ItemSchema.safeParse(candidate);
|
|
71
|
+
if (!item.success) {
|
|
72
|
+
dropped += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const d = item.data;
|
|
76
|
+
items.push({
|
|
77
|
+
quote: d.quote,
|
|
78
|
+
...(typeof d.decision_text === 'string' && d.decision_text.trim() !== '' ? { decision_text: d.decision_text.trim() } : {}),
|
|
79
|
+
type: d.type,
|
|
80
|
+
codify: d.codify,
|
|
81
|
+
ambiguous: d.ambiguous,
|
|
82
|
+
steering_type: d.steering_type,
|
|
83
|
+
approves_proposal: d.approves_proposal,
|
|
84
|
+
same_as: d.same_as,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return { items, dropped };
|
|
88
|
+
}
|
|
89
|
+
function trimEnd(text) {
|
|
90
|
+
return text.replace(/\s+$/u, '');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Cut the LAST fenced `wicked-decisions` block out of a reply. The opening fence must be a line of
|
|
94
|
+
* its own (leading indentation allowed — a list item or a quoted block is still a block; a mention of
|
|
95
|
+
* the fence inside a sentence is prose, codex r1). An opening fence with no closing fence — a reply
|
|
96
|
+
* cut off mid-block — is removed from its line on and reported `truncated`; nothing of a block ever
|
|
97
|
+
* reaches a reader.
|
|
98
|
+
*/
|
|
99
|
+
export function splitDecisionsBlock(text) {
|
|
100
|
+
const openings = [...text.matchAll(/(^|\n)[ \t]*```wicked-decisions[ \t]*(?=\r?\n|$)/gu)];
|
|
101
|
+
const opening = openings[openings.length - 1];
|
|
102
|
+
if (opening === undefined || opening.index === undefined)
|
|
103
|
+
return { text, block: null, truncated: false };
|
|
104
|
+
const lineStart = opening.index + opening[1].length;
|
|
105
|
+
const afterOpen = text.indexOf('\n', lineStart);
|
|
106
|
+
if (afterOpen === -1)
|
|
107
|
+
return { text: trimEnd(text.slice(0, lineStart)), block: null, truncated: true };
|
|
108
|
+
const close = /\n[ \t]*```/u.exec(text.slice(afterOpen));
|
|
109
|
+
if (close === null)
|
|
110
|
+
return { text: trimEnd(text.slice(0, lineStart)), block: null, truncated: true };
|
|
111
|
+
const closeAt = afterOpen + close.index;
|
|
112
|
+
const body = text.slice(afterOpen + 1, closeAt).trim();
|
|
113
|
+
const rest = text.slice(closeAt + close[0].length).replace(/^[ \t]*\r?\n?/u, '');
|
|
114
|
+
return { text: trimEnd(text.slice(0, lineStart) + rest), block: body, truncated: false };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The `chatDelta` filter: per (chat, seat), hold back the longest tail that is still a prefix of the
|
|
118
|
+
* fence (so a fence split across frames is caught), and from the fence on drop every character
|
|
119
|
+
* until {@link reset} (the turn's `chatReply`, which carries the clean full text anyway).
|
|
120
|
+
*/
|
|
121
|
+
export class DecisionFenceFilter {
|
|
122
|
+
state = new Map();
|
|
123
|
+
static key(chat, cliKey) {
|
|
124
|
+
return `${chat}\u0000${cliKey}`;
|
|
125
|
+
}
|
|
126
|
+
delta(chat, cliKey, text) {
|
|
127
|
+
const key = DecisionFenceFilter.key(chat, cliKey);
|
|
128
|
+
const s = this.state.get(key) ?? { held: '', dropping: false };
|
|
129
|
+
if (s.dropping) {
|
|
130
|
+
this.state.set(key, s);
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
const buf = s.held + text;
|
|
134
|
+
const at = buf.indexOf(DECISIONS_FENCE);
|
|
135
|
+
if (at !== -1) {
|
|
136
|
+
this.state.set(key, { held: '', dropping: true });
|
|
137
|
+
return buf.slice(0, at);
|
|
138
|
+
}
|
|
139
|
+
let hold = 0;
|
|
140
|
+
for (let n = Math.min(buf.length, DECISIONS_FENCE.length - 1); n > 0; n -= 1) {
|
|
141
|
+
if (DECISIONS_FENCE.startsWith(buf.slice(buf.length - n))) {
|
|
142
|
+
hold = n;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (hold === 0) {
|
|
147
|
+
this.state.delete(key);
|
|
148
|
+
return buf;
|
|
149
|
+
}
|
|
150
|
+
this.state.set(key, { held: buf.slice(buf.length - hold), dropping: false });
|
|
151
|
+
return buf.slice(0, buf.length - hold);
|
|
152
|
+
}
|
|
153
|
+
reset(chat, cliKey) {
|
|
154
|
+
this.state.delete(DecisionFenceFilter.key(chat, cliKey));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Whitespace-normalised: the only normalisation quote matching applies. */
|
|
158
|
+
function norm(text) {
|
|
159
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
160
|
+
}
|
|
161
|
+
/** The recorder of a turn: the first `claude` seat of its audience, else the first seat (review N3). */
|
|
162
|
+
export function pickRecorder(seats) {
|
|
163
|
+
return seats.find((s) => s === 'claude' || s.startsWith('claude')) ?? seats[0];
|
|
164
|
+
}
|
|
165
|
+
export class ChatDecisionRecorder {
|
|
166
|
+
deps;
|
|
167
|
+
turns = new Map();
|
|
168
|
+
lastTurn = new Map();
|
|
169
|
+
counters = new Map();
|
|
170
|
+
unclassified = 0;
|
|
171
|
+
recorded = 0;
|
|
172
|
+
now;
|
|
173
|
+
staleAfterMs;
|
|
174
|
+
inFlight = new Set();
|
|
175
|
+
constructor(deps) {
|
|
176
|
+
this.deps = deps;
|
|
177
|
+
this.now = deps.now ?? Date.now;
|
|
178
|
+
this.staleAfterMs = deps.staleAfterMs ?? chatTurnStaleAfterMs();
|
|
179
|
+
}
|
|
180
|
+
static key(chat, turnId) {
|
|
181
|
+
return `${chat}\u0000${turnId}`;
|
|
182
|
+
}
|
|
183
|
+
log(msg) {
|
|
184
|
+
this.deps.log?.(msg);
|
|
185
|
+
}
|
|
186
|
+
counter(cliKey) {
|
|
187
|
+
let c = this.counters.get(cliKey);
|
|
188
|
+
if (c === undefined) {
|
|
189
|
+
c = { replies: 0, blocks: 0, malformed: 0 };
|
|
190
|
+
this.counters.set(cliKey, c);
|
|
191
|
+
}
|
|
192
|
+
return c;
|
|
193
|
+
}
|
|
194
|
+
/** The route recorded a send: who said it (the actor), to whom (the audience the engine reached). */
|
|
195
|
+
noteSend(chat, turnId, actor, text, seats) {
|
|
196
|
+
// codex r1: a send sweeps too, so a chat whose seat went quiet does not keep its stale turns
|
|
197
|
+
// until some reply happens to arrive. Tracked, never awaited on the route.
|
|
198
|
+
this.track(this.sweep());
|
|
199
|
+
const prev = this.lastTurn.get(chat);
|
|
200
|
+
this.turns.set(ChatDecisionRecorder.key(chat, turnId), {
|
|
201
|
+
chat,
|
|
202
|
+
turnId,
|
|
203
|
+
at: this.now(),
|
|
204
|
+
actor,
|
|
205
|
+
text,
|
|
206
|
+
seats: [...new Set(seats)],
|
|
207
|
+
replies: new Map(),
|
|
208
|
+
...(prev !== undefined ? { prevTurnId: prev } : {}),
|
|
209
|
+
});
|
|
210
|
+
this.lastTurn.set(chat, turnId);
|
|
211
|
+
}
|
|
212
|
+
/** A seat ended its part of a turn. Resolves once any recording it triggered has settled. */
|
|
213
|
+
async onReply(input) {
|
|
214
|
+
let parsed = null;
|
|
215
|
+
if (input.kind !== 'failed') {
|
|
216
|
+
const c = this.counter(input.cliKey);
|
|
217
|
+
c.replies += 1;
|
|
218
|
+
if (input.block !== null) {
|
|
219
|
+
parsed = parseDecisionsBlock(input.block);
|
|
220
|
+
if (parsed === null)
|
|
221
|
+
c.malformed += 1;
|
|
222
|
+
else
|
|
223
|
+
c.blocks += 1;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
await this.sweep();
|
|
227
|
+
if (input.turnId === undefined)
|
|
228
|
+
return;
|
|
229
|
+
const turn = this.turns.get(ChatDecisionRecorder.key(input.chat, input.turnId));
|
|
230
|
+
if (turn === undefined)
|
|
231
|
+
return; // a turn this daemon never saw sent (a restart): nothing to record
|
|
232
|
+
turn.replies.set(input.cliKey, parsed);
|
|
233
|
+
if (turn.seats.every((s) => turn.replies.has(s)))
|
|
234
|
+
await this.finalize(turn);
|
|
235
|
+
}
|
|
236
|
+
/** The chat is gone: whatever its open turns gathered is recorded now. */
|
|
237
|
+
async closed(chat) {
|
|
238
|
+
for (const turn of [...this.turns.values()]) {
|
|
239
|
+
if (turn.chat === chat)
|
|
240
|
+
await this.finalize(turn);
|
|
241
|
+
}
|
|
242
|
+
this.lastTurn.delete(chat);
|
|
243
|
+
}
|
|
244
|
+
track(work) {
|
|
245
|
+
const tracked = work
|
|
246
|
+
.catch((err) => this.log(`[decisions] chat recorder: ${err instanceof Error ? err.message : String(err)}`))
|
|
247
|
+
.finally(() => this.inFlight.delete(tracked));
|
|
248
|
+
this.inFlight.add(tracked);
|
|
249
|
+
}
|
|
250
|
+
/** Resolves once every recording in flight has settled (tests, shutdown). */
|
|
251
|
+
async idle() {
|
|
252
|
+
while (this.inFlight.size > 0)
|
|
253
|
+
await Promise.all([...this.inFlight]);
|
|
254
|
+
}
|
|
255
|
+
diagnostics() {
|
|
256
|
+
const recorder = {};
|
|
257
|
+
for (const [k, v] of this.counters)
|
|
258
|
+
recorder[k] = { ...v };
|
|
259
|
+
return { mode: this.deps.service.mode, recorder, unclassified_turns: this.unclassified, recorded_turns: this.recorded };
|
|
260
|
+
}
|
|
261
|
+
async sweep() {
|
|
262
|
+
const now = this.now();
|
|
263
|
+
for (const turn of [...this.turns.values()]) {
|
|
264
|
+
if (now - turn.at >= this.staleAfterMs)
|
|
265
|
+
await this.finalize(turn);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async finalize(turn) {
|
|
269
|
+
this.turns.delete(ChatDecisionRecorder.key(turn.chat, turn.turnId));
|
|
270
|
+
const work = this.record(turn).catch((err) => this.log(`[decisions] chat ${turn.chat} turn ${turn.turnId}: ${err instanceof Error ? err.message : String(err)}`));
|
|
271
|
+
this.track(work);
|
|
272
|
+
await work;
|
|
273
|
+
}
|
|
274
|
+
async record(turn) {
|
|
275
|
+
// DC-S7: what the seats considered and cited this turn — whoever sent the message.
|
|
276
|
+
if (this.deps.onTurnRecorded !== undefined) {
|
|
277
|
+
await this.deps.onTurnRecorded(turn.chat, turn.turnId).catch((err) => this.log(`[decisions] considered for chat ${turn.chat} turn ${turn.turnId}: ${err instanceof Error ? err.message : String(err)}`));
|
|
278
|
+
}
|
|
279
|
+
// §4.3.1: an agent's words are not a decision source. The block was still stripped and counted.
|
|
280
|
+
if (turn.actor.kind !== 'human')
|
|
281
|
+
return;
|
|
282
|
+
const recorder = pickRecorder(turn.seats);
|
|
283
|
+
const own = recorder !== undefined ? (turn.replies.get(recorder) ?? null) : null;
|
|
284
|
+
if (own === null)
|
|
285
|
+
this.unclassified += 1;
|
|
286
|
+
const projectId = this.deps.projectOf(turn.chat) ?? null;
|
|
287
|
+
const haystack = norm(turn.text);
|
|
288
|
+
const records = [];
|
|
289
|
+
if (own !== null && recorder !== undefined) {
|
|
290
|
+
for (const item of own.items) {
|
|
291
|
+
const quote = norm(item.quote);
|
|
292
|
+
if (quote === '' || !haystack.includes(quote)) {
|
|
293
|
+
this.log(`[decisions] chat ${turn.chat} turn ${turn.turnId}: ${recorder} quote not found in the operator's message; dropped`);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const labels = {
|
|
297
|
+
is_decision: true,
|
|
298
|
+
type: item.type,
|
|
299
|
+
codify: item.codify,
|
|
300
|
+
ambiguous: item.ambiguous,
|
|
301
|
+
steering_type: item.steering_type,
|
|
302
|
+
same_as: item.same_as,
|
|
303
|
+
approves_proposal: item.approves_proposal,
|
|
304
|
+
recorder,
|
|
305
|
+
};
|
|
306
|
+
const votes = [];
|
|
307
|
+
for (const [cliKey, other] of turn.replies) {
|
|
308
|
+
if (cliKey === recorder || other === null)
|
|
309
|
+
continue;
|
|
310
|
+
const vote = other.items.find((o) => norm(o.quote) === quote);
|
|
311
|
+
if (vote !== undefined)
|
|
312
|
+
votes.push({ cli_key: cliKey, type: vote.type, codify: vote.codify });
|
|
313
|
+
}
|
|
314
|
+
const approved = item.approves_proposal && item.decision_text !== undefined;
|
|
315
|
+
const record = await ingestDecision(this.deps.service, {
|
|
316
|
+
host: 'studio-chat',
|
|
317
|
+
actor: turn.actor,
|
|
318
|
+
words: quote,
|
|
319
|
+
projectId,
|
|
320
|
+
chatId: turn.chat,
|
|
321
|
+
turnId: turn.turnId,
|
|
322
|
+
labels,
|
|
323
|
+
votes,
|
|
324
|
+
proposal: approved && turn.prevTurnId !== undefined
|
|
325
|
+
? { turn_id: turn.prevTurnId, cli_key: recorder, excerpt: item.decision_text.slice(0, 300) }
|
|
326
|
+
: null,
|
|
327
|
+
...(approved ? { statement: item.decision_text } : {}),
|
|
328
|
+
});
|
|
329
|
+
if (record !== null)
|
|
330
|
+
records.push(record);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (records.length === 0) {
|
|
334
|
+
// No usable block, or no quote that matched: the words are recorded as typed, and the
|
|
335
|
+
// deterministic derivation alone decides the route (§4.3.3 fails open, no regex fallback).
|
|
336
|
+
const record = await ingestDecision(this.deps.service, {
|
|
337
|
+
host: 'studio-chat',
|
|
338
|
+
actor: turn.actor,
|
|
339
|
+
words: turn.text,
|
|
340
|
+
projectId,
|
|
341
|
+
chatId: turn.chat,
|
|
342
|
+
turnId: turn.turnId,
|
|
343
|
+
});
|
|
344
|
+
if (record !== null)
|
|
345
|
+
records.push(record);
|
|
346
|
+
}
|
|
347
|
+
if (records.length === 0)
|
|
348
|
+
return; // WICKED_DECISIONS=off, or nothing to record
|
|
349
|
+
this.recorded += 1;
|
|
350
|
+
const items = [];
|
|
351
|
+
for (const r of records) {
|
|
352
|
+
const view = this.deps.service.deps.ledger.view(r.id);
|
|
353
|
+
if (view !== null)
|
|
354
|
+
items.push(view);
|
|
355
|
+
}
|
|
356
|
+
const frame = {
|
|
357
|
+
type: 'chatDecisions',
|
|
358
|
+
chat: turn.chat,
|
|
359
|
+
turn_id: turn.turnId,
|
|
360
|
+
items,
|
|
361
|
+
...(projectId !== null ? { project_id: projectId } : {}),
|
|
362
|
+
};
|
|
363
|
+
this.deps.broadcast(frame);
|
|
364
|
+
this.deps.transcripts.recordDecisions({ chat: turn.chat, turnId: turn.turnId, items });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=chat-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-recorder.js","sourceRoot":"","sources":["../../src/decisions/chat-recorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Considered · set aside · cited (unchecked)" (DES-decision-capture §4.7, DC-S7).
|
|
3
|
+
*
|
|
4
|
+
* - **Considered** = the engine's `considerRules({projects:[project]}).in_force` for the turn's or
|
|
5
|
+
* unit's project (project rules + global ones). On an engine without the binding, the global
|
|
6
|
+
* rules alone, and the Consideration says so (`source: 'global-only'`).
|
|
7
|
+
* - **Set aside** = the engine's `set_aside` (out of scope / replaced / retired) plus crew's own
|
|
8
|
+
* pending candidates for that project — decisions `offered` but not yet confirmed.
|
|
9
|
+
* - **Cited (unchecked)** = every `[rule:<id>]` the persisted reply or step wrote. An id in the
|
|
10
|
+
* in-force set is "cited by the step — unchecked"; any other id is unverified. It is NEVER
|
|
11
|
+
* summed into a "followed" count (B4): advisory rules never decide, so nothing here can know
|
|
12
|
+
* whether a rule was followed — DES-rule-check supplies that verdict.
|
|
13
|
+
*
|
|
14
|
+
* The same object answers `GET /chats/:id/turns/:turnId/considered` and
|
|
15
|
+
* `GET /runs/:id/units/:unitKey/considered?attempt=`, and one `wicked.crew.rule.considered` fact
|
|
16
|
+
* (counts and ids, never words) is emitted when a turn or a unit attempt lands — keyed per turn
|
|
17
|
+
* and per (run, ord, attempt), so a re-dispatch is its own record (review N5).
|
|
18
|
+
*/
|
|
19
|
+
import { RULE_CITED_LABEL, extractRuleCitations } from '../api/chat-citations.js';
|
|
20
|
+
import type { ChatTranscriptStore } from '../api/chat-transcripts.js';
|
|
21
|
+
import type { ConformanceRule, Consideration, SessionView } from '../core/types.js';
|
|
22
|
+
import { type DecisionBusEmit } from './events.js';
|
|
23
|
+
import type { DecisionLedger } from './ledger.js';
|
|
24
|
+
import { rulesPreface } from './rules-text.js';
|
|
25
|
+
export { RULE_CITED_LABEL, extractRuleCitations, rulesPreface };
|
|
26
|
+
/** What the service needs from the engine; every method optional so a partial stub never throws. */
|
|
27
|
+
export interface ConsiderAdapter {
|
|
28
|
+
projectRulesSupported?(): boolean;
|
|
29
|
+
listConformanceRules?(): Promise<ConformanceRule[]>;
|
|
30
|
+
considerRules?(query: {
|
|
31
|
+
projects?: string[];
|
|
32
|
+
steering_type?: string;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
in_force: ConformanceRule[];
|
|
35
|
+
set_aside: Array<{
|
|
36
|
+
id: string;
|
|
37
|
+
statement: string;
|
|
38
|
+
reason: 'out_of_scope' | 'replaced' | 'retired';
|
|
39
|
+
}>;
|
|
40
|
+
}>;
|
|
41
|
+
sessionsDetail?(): Promise<SessionView[]>;
|
|
42
|
+
workOutput?(unitId: string): Promise<string | null>;
|
|
43
|
+
}
|
|
44
|
+
export interface InForceRead {
|
|
45
|
+
rules: ConformanceRule[];
|
|
46
|
+
setAside: Array<{
|
|
47
|
+
id: string;
|
|
48
|
+
statement: string;
|
|
49
|
+
reason: 'out_of_scope' | 'replaced' | 'retired';
|
|
50
|
+
}>;
|
|
51
|
+
source: Consideration['source'];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The in-force rules for a project. `considerRules` when the engine has it; else the global,
|
|
55
|
+
* non-retired rules; else (no engine read at all) nothing, said as `unavailable`. Never throws.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readInForce(adapter: ConsiderAdapter, projectId: string | null, log?: (m: string) => void): Promise<InForceRead>;
|
|
58
|
+
export interface ConsiderationDeps {
|
|
59
|
+
adapter: ConsiderAdapter;
|
|
60
|
+
/** Crew's own pending candidates (`offered`) become `not_confirmed` set-asides. Absent = none. */
|
|
61
|
+
ledger?: DecisionLedger;
|
|
62
|
+
/** The chat turn's persisted replies. Absent = no chat considerations. */
|
|
63
|
+
transcripts?: ChatTranscriptStore;
|
|
64
|
+
projectOf: (id: string) => string | undefined;
|
|
65
|
+
emit?: DecisionBusEmit | null;
|
|
66
|
+
log?: (msg: string) => void;
|
|
67
|
+
}
|
|
68
|
+
export declare class ConsiderationService {
|
|
69
|
+
private readonly deps;
|
|
70
|
+
/** chat → the rule ids its seats have been told about (the statement at open, then every preface). */
|
|
71
|
+
private readonly seen;
|
|
72
|
+
constructor(deps: ConsiderationDeps);
|
|
73
|
+
private log;
|
|
74
|
+
inForceFor(projectId: string | null): Promise<InForceRead>;
|
|
75
|
+
/** The in-force ids, or `null` when no engine read was possible (a citation then stays unchecked). */
|
|
76
|
+
inForceIds(projectId: string | null): Promise<ReadonlySet<string> | null>;
|
|
77
|
+
/** The chat opened with these rules in its statement. */
|
|
78
|
+
noteChatOpen(chat: string, rules: ReadonlyArray<ConformanceRule>): void;
|
|
79
|
+
/**
|
|
80
|
+
* The preface the next send carries, or `null`: the rules in force now that the seats have not
|
|
81
|
+
* been told about. A chat this daemon did not open (a restart) is SEEDED on its first send —
|
|
82
|
+
* nothing is known about what its seats were told, so the current set stands in for the open
|
|
83
|
+
* statement and that send carries no preface; a rule that lands after the seed is new to the
|
|
84
|
+
* seats and IS prefaced on the send after (codex r1, wording fixed: "seeded, then prefaced").
|
|
85
|
+
*/
|
|
86
|
+
prefaceForSend(chat: string, opts?: {
|
|
87
|
+
inForce?: ReadonlyArray<ConformanceRule>;
|
|
88
|
+
}): Promise<string | null>;
|
|
89
|
+
chatClosed(chat: string): void;
|
|
90
|
+
private notConfirmed;
|
|
91
|
+
private build;
|
|
92
|
+
/** The Consideration of one chat turn, or `null` when the transcript holds no such turn. */
|
|
93
|
+
forChatTurn(chat: string, turnId: string): Promise<Consideration | null>;
|
|
94
|
+
/** The Consideration of one unit attempt (`unitKey` as `resolveUnit` accepts it), or `null`. */
|
|
95
|
+
forUnit(runId: string, unitKey: string, attempt?: number): Promise<Consideration | null>;
|
|
96
|
+
private emit;
|
|
97
|
+
/** A chat turn's replies all landed: compute and emit. Never throws. */
|
|
98
|
+
onChatTurn(chat: string, turnId: string): Promise<void>;
|
|
99
|
+
/** A unit attempt's output was captured: compute and emit. Never throws. */
|
|
100
|
+
onUnitCaptured(runId: string, ord: number, attempt: number): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=consider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consider.d.ts","sourceRoot":"","sources":["../../src/decisions/consider.ts"],"names":[],"mappings":"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"}
|